Alright, so i've picked up project euler where i left off when using java, and i'm at problem 10. I use Haskell now and i figured it'd be good to learn some haskell since i'm still very much a beginner.
http://projecteuler.net/problem=10
My friend who still codes in java came up with a very straight forward way to implement the sieve of eratosthenes:
I tried implementing a better looking (and what i thought was gonna be a slightly more efficient) Haskell function to find all primes up to 2,000,000. I came to this very elegant, yet apparently enormously inefficient function:
primeSieveV2 :: [Integer] -> [Integer]
primeSieveV2 [] = []
primeSieveV2 (x:xs) = x:primeSieveV2( (filter (\n -> ( mod n x ) /= 0) xs) )
Now i'm not sure why my function is so much slower than his (he claim his works in 5ms), if anything mine should be faster, since i only check composites once (they are removed from the list when they are found) whereas his checks them as many times as they can be formed.
Any help?
The time complexity of your code is n2 (in n primes produced). It is impractical to run for producing more than first 10...20 thousand primes.
The main problem with that code is not that it uses
rem
but that it starts its filters prematurely, so creates too many of them. Here's how you fix it, with a small tweak:This improves the time complexity by about n1/2 (in n primes produced) and gives it a drastic speedup: it gets to
100,000
75x faster. Your 28 seconds should become ~ 0.4 sec. But, you probably tested it in GHCi as interpreted code, not compiled. Marking it1) as:: [Int]
and compiling with-O2
flag gives it another ~ 40x speedup, so it'll be ~ 0.01 sec. To reach2,000,000
with this code takes ~ 90x longer, for a whopping ~ 1 sec of projected run time.1) be sure to use
sum $ map (fromIntegral :: Int -> Integer) $ takeWhile ...
inmain
.see also: http://en.wikipedia.org/wiki/Analysis_of_algorithms#Empirical_orders_of_growth