I'm getting acquainted with F# by going over Project Euler and solving some of the problems. Many of the early problems consist of prime numbers. After looking around I came up with the following solution:
let primesL =
let rec prim n sofar =
seq { if (sofar |> List.forall (fun i->n%i <>0L)) then
yield n
yield! prim (n+1L) (n::sofar)
else
yield! prim (n+1L) sofar }
prim 2L []
This works well, but then I generate all the prime numbers up to 2000000:
let smallPrimes = primesL |> Seq.takeWhile (fun n->n<=2000000)
This takes ages. It's quite obvious something is done in O(N^2) or worst.
I know I can write an imperative version and implement a sieve of some sort, but I want to stick to functional code. If I wanted imperative, I would have stayed with C#.
What am I missing?
Rather than write a long answer here, I refer you to Melissa O'Neill's great paper on the sieve of Eratosthenes.
You may want to compare your approach with my variant of Problem Euler 10 solution
let rec primes =
Seq.cache <| seq { yield 2; yield! Seq.unfold nextPrime 3 }
and nextPrime n =
if isPrime n then Some(n, n + 2) else nextPrime(n + 2)
and isPrime n =
if n >= 2 then
primes
|> Seq.tryFind (fun x -> n % x = 0 || x * x > n)
|> fun x -> x.Value * x.Value > n
else false
It is purely functional, uses sequence cashing, optimized for primality check; also it yields very useful isPrime n function as a co-result.
And being applied to the original problem
let problem010 () =
primes
|> Seq.takeWhile ((>) 2000000)
|> (Seq.map int64 >> Seq.sum)
it completes in decent 2.5 s. This is not blasting fast, but was good enough to use this primes sequence in handful of my other Project Euler solutions (27, 35, 37, 50, 58, 69, 70, 77 so far).
As to what you're missing in your solution - from your code I believe you're building a brand new sequence on each internal call to prim, i.e. for each natural while my approach uses a single sequence for already found primes and only enumerates its cached instance when producing each next prime.
First, it is O(n^2) - remember that you use List.forall on each iteration.
Second, if you use the generator a lot, you should cache the results (so that each prime number is only calculated once):
let primesL =
let rec prim n sofar =
seq { if (sofar |> List.forall (fun i -> n % i <> 0UL)) then
yield n
yield! prim (n + 1UL) (n::sofar)
else
yield! prim (n + 1UL) sofar }
prim 2UL []
|> Seq.cache
What you really want here is a sieve - I have written a pretty fast F# sieve before here:
F# parallelizing issue when calculating perfect numbers?
Use "Miller–Rabin primality test" for more than some large prime number
Related
I'm reading a book Pearls of Functional Algorithm Design. Tried implementing the divide and conquer solution for smallest free number problem.
minfree xs = minfrom 0 (length xs) xs
minfrom a 0 _ = a
minfrom a n xs = if m == b - a
then minfrom b (n-m) vs
else minfrom a (m) us
where b = a + 1 + (n `div` 2)
(us,vs) = partition (<b) xs
m = length us
But this one works no faster than the solution that one might call "naive" solution. Which is
import Data.List ((\\))
minfree' = head . (\\) [0..]
I don't know why this is like this, what's wrong with the divide and conquer algorithm and how to improve it.
Tried using BangPatterns, implementing the version of partition that also returns first list's length in the tuple, so it eliminates additional traversal for m =length us. None of them made improvement.
First one takes more than 5 seconds, whereas second one does it almost instantly in ghci on input [0..9999999].
You have pathological input on which head . (\\) [0..] performs in O(N) time. \\ is defined as follows:
(\\) = foldl (flip delete)
delete x xs is an O(N) operation that removes the first x from xs. foldl (flip delete) xs ys deletes all elements of ys from xs one by one.
In [0..] \\ [0..9999999], we always find the next element to be deleted at the head of the list, so the result can be evaluated in linear time.
If you instead type minfree' (reverse [0..9999999]) into GHCi, that takes quadratic time and you find that it pretty much never finishes.
The divide-and-conquer algorithm on the other hand would not slow down on the reversed input.
I am attempting to solve Euler problem 3 in Haskell, which involves finding the largest prime factor of a number. My code runs for a long time and seems to hang. What is causing my code to be so grossly inefficient?
primes = sieve (2:[3,5..])
where sieve (x:xs) = x:[y | y <- (sieve xs), mod y x /= 0]
sieve [] = []
primefactors n = filter (\x -> mod n x == 0) (primesUnder n)
where primesUnder z = reverse (takeWhile (< z) primes)
solve3 = head (primefactors 600851475143)
Your main problem is you're checking for enormous primes -- all the way up to 600851475143. You can improve things a lot by observing two things:
Every time you find a prime, you can decrease the maximum prime you look at by dividing away that factor.
You only have to look for primes until you reach the square root of the target. If your primes are bigger than that, and you know there are no smaller factors, you're done.
Using these two improvements together, even without the nicety that you used of only checking primes for divisibility, makes the program run in a snap:
factor = go (2:[3,5..]) where
go (p:ps) n
| p*p > n = [n]
| n `mod` p == 0 = p : go (p:ps) (n `div` p)
| otherwise = go ps n
main = print . last . factor $ 600851475143
In ghci:
*Main> main
6857
(0.00 secs, 0 bytes)
You can see that we only had to inspect numbers up to 6857 -- eight orders of magnitude smaller than what you would have to do with your approach.
Independently, your sieve is dog slow. You could have a look at the wiki for ideas about how to find primes quickly.
I currently have the following function to get the divisors of an integer:
-- All divisors of a number
divisors :: Integer -> [Integer]
divisors 1 = [1]
divisors n = firstHalf ++ secondHalf
where firstHalf = filter (divides n) (candidates n)
secondHalf = filter (\d -> n `div` d /= d) (map (n `div`) (reverse firstHalf))
candidates n = takeWhile (\d -> d * d <= n) [1..n]
I ended up adding the filter to secondHalf because a divisor was repeating when n is a square of a prime number. This seems like a very inefficient way to solve this problem.
So I have two questions: How do I measure if this really is a bottle neck in my algorithm? And if it is, how do I go about finding a better way to avoid repetitions when n is a square of a prime?
To mesure where the bottleneck is, put the three auxiliary definitions (firstHalf, secondHalf, candidates) at the top level, and run your code with the profiler on: ghc -prof --make divisors.hs ./divisors 100 +RTS -p -RTS
Also, you know that the biggest candidate is sqrt n, so instead of doing that many multiplications d*d, just consider [1..floor (sqrt n)]
For better algorithms, you should take a maths book, for it's not a haskell related question… Things you can consider: if "a divides b", then for all divisor d of a, d divides b as well.
You'll want to use memoization or dynamic programming to avoid checking multiple times if a given d divides b (for example, if 15 and 27 divide b, then you need to mathematically check only once that 3 divides b. The other times, you just see if 3 is in your table of divisors of b).
You needn't test all the elements of reversed second half. You know that if the square root is present, it is the head element there:
secondHalf = let (r:ds) = [n `div` d | d <- reverse firstHalf]
in [r | n `div` r /= r] ++ ds
This assumes n is positive.
A simpler way to handle the sqrt of a number differently is to handle it separately:
divs n =
let
r = floor $ sqrt $ fromIntegral n
(a,b) = unzip $ (1,n) : [(d, q) | d<-[2..r-1], let (q,r)=quotRem n d, r==0]
in
if r*r==n
then a ++ r : reverse b
else a ++ reverse b
That way we get the second half for free, as a part of producing the first half.
But this could hardly be a bottleneck in your application because the algorithm itself is inefficient. It is usually much faster to generate the divisors from a number's prime factorization. Prime factorization by trial division can be much quicker because we divide out each divisor as it is found, reducing the number being factorized and thus the amount of divisors that are tried (up to the reduced number's square root). For example, 12348 = 2*2*3*3*7*7*7 and no factor above 7 is tried in the process of factorization, whereas in divs 12348 the number 12348 is divided by all numbers from 2 to 110:
factorize n = go n (2:[3,5..]) -- or: (go n primes) where
where -- primes = 2 :
go n ds#(d:t) -- filter (null.tail.factorize) [3,5..]
| d*d > n = [n]
| r == 0 = d : go q ds
| otherwise = go n t
where (q,r) = quotRem n d
I have the following solution in Haskell to Problem 3:
isPrime :: Integer -> Bool
isPrime p = (divisors p) == [1, p]
divisors :: Integer -> [Integer]
divisors n = [d | d <- [1..n], n `mod` d == 0]
main = print (head (filter isPrime (filter ((==0) . (n `mod`)) [n-1,n-2..])))
where n = 600851475143
However, it takes more than the minute limit given by Project Euler. So how do I analyze the time complexity of my code to determine where I need to make changes?
Note: Please do not post alternative algorithms. I want to figure those out on my own. For now I just want to analyse the code I have and look for ways to improve it. Thanks!
Two things:
Any time you see a list comprehension (as you have in divisors), or equivalently, some series of map and/or filter functions over a list (as you have in main), treat its complexity as Θ(n) just the same as you would treat a for-loop in an imperative language.
This is probably not quite the sort of advice you were expecting, but I hope it will be more helpful: Part of the purpose of Project Euler is to encourage you to think about the definitions of various mathematical concepts, and about the many different algorithms that might correctly satisfy those definitions.
Okay, that second suggestion was a bit too nebulous... What I mean is, for example, the way you've implemented isPrime is really a textbook definition:
isPrime :: Integer -> Bool
isPrime p = (divisors p) == [1, p]
-- p is prime if its only divisors are 1 and p.
Likewise, your implementation of divisors is straightforward:
divisors :: Integer -> [Integer]
divisors n = [d | d <- [1..n], n `mod` d == 0]
-- the divisors of n are the numbers between 1 and n that divide evenly into n.
These definitions both read very nicely! Algorithmically, on the other hand, they are too naïve. Let's take a simple example: what are the divisors of the number 10? [1, 2, 5, 10]. On inspection, you probably notice a couple things:
1 and 10 are pairs, and 2 and 5 are pairs.
Aside from 10 itself, there can't be any divisors of 10 that are greater than 5.
You can probably exploit properties like these to optimize your algorithm, right? So, without looking at your code -- just using pencil and paper -- try sketching out a faster algorithm for divisors. If you've understood my hint, divisors n should run in sqrt n time. You'll find more opportunities along these lines as you continue. You might decide to redefine everything differently, in a way that doesn't use your divisors function at all...
Hope this helps give you the right mindset for tackling these problems!
Let's start from the top.
divisors :: Integer -> [Integer]
divisors n = [d | d <- [1..n], n `mod` d == 0]
For now, let's assume that certain things are cheap: incrementing numbers is O(1), doing mod operations is O(1), and comparisons with 0 are O(1). (These are false assumptions, but what the heck.) The divisors function loops over all numbers from 1 to n, and does an O(1) operation on each number, so computing the complete output is O(n). Notice that here when we say O(n), n is the input number, not the size of the input! Since it takes m=log(n) bits to store n, this function takes O(2^m) time in the size of the input to produce a complete answer. I'll use n and m consistently to mean the input number and input size below.
isPrime :: Integer -> Bool
isPrime p = (divisors p) == [1, p]
In the worst case, p is prime, which forces divisors to produce its whole output. Comparison to a list of statically-known length is O(1), so this is dominated by the call to divisors. O(n), O(2^m)
Your main function does a bunch of things at once, so let's break down subexpressions a bit.
filter ((==0) . (n `mod`))
This loops over a list, and does an O(1) operation on each element. This is O(m), where here m is the length of the input list.
filter isPrime
Loops over a list, doing O(n) work on each element, where here n is the largest number in the list. If the list happens to be n elements long (as it is in your case), this means this is O(n*n) work, or O(2^m*2^m) = O(4^m) work (as above, this analysis is for the case where it produces its entire list).
print . head
Tiny bits of work. Let's call it O(m) for the printing part.
main = print (head (filter isPrime (filter ((==0) . (n `mod`)) [n-1,n-2..])))
Considering all the subexpressions above, the filter isPrime bit is clearly the dominating factor. O(4^m), O(n^2)
Now, there's one final subtlety to consider: throughout the analysis above, I've consistently made the assumption that each function/subexpression was forced to produce its entire output. As we can see in main, this probably isn't true: we call head, which only forces a little bit of the list. However, if the input number itself isn't prime, we know for sure that we must look through at least half the list: there will certainly be no divisors between n/2 and n. So, at best, we cut our work in half -- which has no effect on the asymptotic cost.
Daniel Wagner's answer explains the general strategy of deriving bounds for the runtime complexity rather well. However, as is usually the case for general strategies, it yields too conservative bounds.
So, just for the heck of it, let's investigate this example in some more detail.
main = print (head (filter isPrime (filter ((==0) . (n `mod`)) [n-1,n-2..])))
where n = 600851475143
(Aside: if n were prime, this would cause a runtime error when checking n `mod` 0 == 0, thus I change the list to [n, n-1 .. 2] so that the algorithm works for all n > 1.)
Let's split up the expression into its parts, so we can see and analyse each part more easily
main = print answer
where
n = 600851475143
candidates = [n, n-1 .. 2]
divisorsOfN = filter ((== 0) . (n `mod`)) candidates
primeDivisors = filter isPrime divisorsOfN
answer = head primeDivisors
Like Daniel, I work with the assumption that arithmetic operations, comparisons etc. are O(1) - although not true, that's a good enough approximation for all remotely reasonable inputs.
So, of the list candidates, the elements from n down to answer have to be generated, n - answer + 1 elements, for a total cost of O(n - answer + 1). For composite n, we have answer <= n/2, then that's Θ(n).
Generating the list of divisors as far as needed is then Θ(n - answer + 1) too.
For the number d(n) of divisors of n, we can use the coarse estimate d(n) <= 2√n.
All divisors >= answer of n have to be checked for primality, that's at least half of all divisors.
Since the list of divisors is lazily generated, the complexity of
isPrime :: Integer -> Bool
isPrime p = (divisors p) == [1, p]
is O(smallest prime factor of p), because as soon as the first divisor > 1 is found, the equality test is determined. For composite p, the smallest prime factor is <= √p.
We have < 2√n primality checks of complexity at worst O(√n), and one check of complexity Θ(answer), so the combined work of all prime tests carried out is O(n).
Summing up, the total work needed is O(n), since the cost of each step is O(n) at worst.
In fact, the total work done in this algorithm is Θ(n). If n is prime, generating the list of divisors as far as needed is done in O(1), but the prime test is Θ(n). If n is composite, answer <= n/2, and generating the list of divisors as far as needed is Θ(n).
If we don't consider the arithmetic operations to be O(1), we have to multiply with the complexity of an arithmetic operation on numbers the size of n, that is O(log n) bits, which, depending on the algorithms used, usually gives a factor slightly above log n and below (log n)^2.
So I have devised the following function for seeing if a given number is a prime in Haskell (it assumes the first prime is 2):
isPrime k = length [ x | x <- [2..k], k `mod` x == 0] == 1
it has the obvious pitfall of continuing the evaluation even if it is divisible by several numbers :(. Is there any sane way of "cutting" the evaluation when it finds more than one solution, using list comprehensions?
Also, which other implementations would you you try on? I'm not looking for performance here, I'm just trying to see if there are other more "haskellish" ways of doing the same thing.
A quick change to your code that will 'short circuit' the evaluation, and relies on the laziness of Haskell Lists, is:
isPrime k = if k > 1 then null [ x | x <- [2..k - 1], k `mod` x == 0] else False
The very first divisor of k will cause the list to be non-empty, and the Haskell implementation of null will only look at the first element of the list.
You should only need to check up to sqrt(k) however:
isPrime k = if k > 1 then null [ x | x <- [2..isqrt k], k `mod` x == 0] else False
Of course, if you are looking to do high-performance primality testing, a library is preferred.
Here is the best resource for prime numbers in haskell in haskell.org
and here prime.hs github project
I like this approach:
First make function to get all factors of n:
factors n = [x | x <- [1..n], mod n x == 0]
Then check if factors are only the given number and 1, if so, the number is prime:
prime n = factors n == [1,n]
It's perhaps not directly relevant, but on the topic of finding primes in functional languages I found Melissa E. O'Neill's The Genuine Sieve of Eratosthenes very interesting.
Ignoring the primes issue, and focusing on the narrow point of a more efficient method of length xs == n:
hasLength :: Integral count => [a] -> count -> Bool
_ `hasLength` n | n < 0 = False
[] `hasLength` n = n == 0
(_ : xs) `hasLength` n = xs `hasLength` (pred n)
isPrime k = [ x | x <- [2..k], k `mod` x == 0)] `hasLength` 1
This may be silly and inefficient (I'm a complete Haskell newby), but the function isMyNumberPrime (in ghci) seems to tell you if a number is prime or not.
factors n = [x | x <- [2..(n`div` 2)], mod n x == 0]
factormap n = fmap factors $ factors n
isMyNumberPrime n = case factormap n of [] -> True; _ -> False