优化Haskell代码,计算低于200万的所有素数之和

时间:2010-12-07 15:13:44

标签: haskell optimization primes

Project Euler中的问题10。我在那里看到了一些讨论,但仅限于C。

我使用以下代码计算:

print . sum . sieve $ [2..2000000] where
    sieve [] = []
    sieve (x:xs) = x : sieve (filter ((/= 0) . (`mod` x)) xs)

计算需要很长时间。我想知道是否有更有效的方法来计算它?

3 个答案:

答案 0 :(得分:7)

haskellwiki page for Prime numbers中描述了许多计算haskell素数的快速方法。具体来说,第二个似乎已经足够好了,所以你可以这样写:

main = print . sum . takeWhile (< 2000000) $ primes 

primes = 2: 3: sieve (tail primes) [5,7..] 

sieve (p:ps) xs = h ++ sieve ps [x | x <- t, rem x p /= 0]  
  where (h,~(_:t)) = span (< p*p) xs 

运行它我们得到:

ghc --make -O2 Euler10.hs
time ./SOAns
142913828922

real    0m1.598s
user    0m1.577s
sys 0m0.017s

wiki描述了为什么你的解决方案如此缓慢,主要原因是为每个数字设置了一个筛子,最高可达200​​0000,而每个数量的筛子就足够了。

答案 1 :(得分:6)

您可能会发现this paper以及随后的讨论很有趣。最重要的是,你的sieve实施效率不如Eratosthenes的“真正”筛选效率高。

答案 2 :(得分:0)

我在Haskell中亲眼看到的最简洁的优化的主要筛选代码是NumberSieves包,其中包括基于可变载体的传统筛子和O&O的形式#39;尼尔的筛子。不要在arithmoi包中使用可怕的复杂代码 - 至少其中一些代码目前已被破坏并随机产生分段错误。