我对Haskell很新,我只想找到前200万个素数的总和。我正试图用筛子生成素数(我想是Eratosthenes的筛子?),但它真的很慢,我不知道为什么。这是我的代码。
sieve (x:xs) = x:(sieve $ filter (\a -> a `mod` x /= 0) xs)
ans = sum $ takeWhile (<2000000) (sieve [2..])
提前致谢。
答案 0 :(得分:5)
P = { 3 , 5 ,...} \ ⋃ {{ p 2 , p 2 + 2p ,...} | P }
中的 p(没有2)。 :)或者在“功能”即基于列表的Haskell中,
primes = 2 : g (fix g) where
g xs = 3 : (gaps 5 $ unionAll [[p*p, p*p+2*p..] | p <- xs])
unionAll ((x:xs):t) = x : union xs (unionAll $ pairs t) where
pairs ((x:xs):ys:t) = (x : union xs ys) : pairs t
fix g = xs where xs = g xs
union (x:xs) (y:ys) = case compare x y of LT -> x : union xs (y:ys)
EQ -> x : union xs ys
GT -> y : union (x:xs) ys
gaps k s@(x:xs) | k<x = k:gaps (k+2) s
| True = gaps (k+2) xs
与answer by augustss中的试验分部代码相比,它在生成200k素数时速度快1.9倍,在400k时速度快2.1倍,empirical time complexity与O(n^1.12..1.15)
相比stream processing paradigm 1}},在上述范围内。它产生1百万个素数的速度快2.6倍。
因为它为每个素数太早打开了多次过滤流,因此最终会出现太多。我们不需要按素数进行过滤,直到在输入中看到它的正方形。
在Fixing it下看到,O(n^1.4)
可以看作是在它自身工作时创建一个流传感器管道:
sieve (x:xs) = x:sieve [y|y<-xs, rem y p/=0]
其中[2..] ==> sieve --> 2
[3..] ==> nomult 2 ==> sieve --> 3
[4..] ==> nomult 2 ==> nomult 3 ==> sieve
[5..] ==> nomult 2 ==> nomult 3 ==> sieve --> 5
[6..] ==> nomult 2 ==> nomult 3 ==> nomult 5 ==> sieve
[7..] ==> nomult 2 ==> nomult 3 ==> nomult 5 ==> sieve --> 7
[8..] ==> nomult 2 ==> nomult 3 ==> nomult 5 ==> nomult 7 ==> sieve
。但是,8不需要检查3的可除性,因为它小于nomult p = filter (\y->rem y p/=0)
,更不用说5或7了。
这是该代码中唯一最严重的问题,尽管在每篇人都提到的那篇文章的开头它被认为是无关紧要的。 {{3}}通过推迟过滤器的创建实现了显着的加速。
答案 1 :(得分:3)
你所做的不是Eratosthenes的筛子;它的试验部门(注意mod运算符)。这是我的版本的Eratosthenes筛选:
import Control.Monad (forM_, when)
import Control.Monad.ST
import Data.Array.ST
import Data.Array.Unboxed
sieve :: Int -> UArray Int Bool
sieve n = runSTUArray $ do
let m = (n-1) `div` 2
r = floor . sqrt $ fromIntegral n
bits <- newArray (0, m-1) True
forM_ [0 .. r `div` 2 - 1] $ \i -> do
isPrime <- readArray bits i
when isPrime $ do
forM_ [2*i*i+6*i+3, 2*i*i+8*i+6 .. (m-1)] $ \j -> do
writeArray bits j False
return bits
primes :: Int -> [Int]
primes n = 2 : [2*i+3 | (i, True) <- assocs $ sieve n]
您可以在http://ideone.com/mu1RN运行它。
答案 2 :(得分:3)
就个人而言,我喜欢这种产生素数的方式
primes :: [Integer]
primes = 2 : filter (isPrime primes) [3,5..]
where isPrime (p:ps) n = p*p > n || n `rem` p /= 0 && isPrime ps n
与此处提出的其他一些方法相比,它也相当快。它仍然是试验部门,但它只用质数进行测试。 (但是,此代码的终止证明有点棘手。)
答案 3 :(得分:1)
你使用的算法根本不是一个筛子,因此就缓慢而言,你应该期待使用试验分割。
Primes大致出现平方根函数的频率......即在1和 n 之间存在ballpark n / log(n)素数。因此,对于前200万个素数,您将需要高达约3200万。但是,您正在构建一个200万元素的数据结构,这些素数将会通过。所以你可以开始明白为什么这么慢。实际上它是O(n ^ 2)。您可以将其减少到O(n *(log n)* log(log n))
这是一个关于各种治疗方法的页面,引导您了解如何减少一点。 http://en.literateprograms.org/Sieve_of_Eratosthenes_(Haskell)