For project euler i was looking for a way to implement the sieve of Eratosthenes because i expected it to be faster than my original implementation (and needed it to be so). I came up with this function:
sieve :: Int -> [Int] -> [Int]
sieve p (x:xs) | rem x p == 0 = sieve p xs
| otherwise = x : (sieve x $ sieve p xs)
sieve _ [] = []
Which works, but hits the fan very fast, resulting in a stack overflow. I headed here for some advice and immediately struck a spoiler which, to me, looks quite the same, but the difference in performance is outlandish. I still want to continue using my own implementation, and would like to know whether or not my function can be changed easily to use less memory.
Your code has an exponential explosion inside it:
You intended for the inner
sieve
call to just continue filtering byp
, but since you use the samesieve
function, it will also start up new filters for new primes as it encounters them - but that's entirely superfluous since the "upper" level call will also start new filter for the same prime!After 7 passes to the top through the four
sieve
s here, each will split in two creating four newsieve 7
s, so there will be eightsieve
s in the chain!! Even worse, when 9 - the composite! - goes through the sieves, it will split the 2, the 7s and the 5, and will only be rejected by the 3. So it's actually worse than exponential in number of primesn
, and close to being exponential in the value of the last prime produced (which is~ n log(n)
).Change it to
and you get the code equivalent to the one you cite.
If you prefer to hand code everything, you could introduce a new argument, a Boolean flag controlling whether to start up new filters or not: