题
声明:我正在欧拉问题9
我添加了一些非常大的数字,所有的素数为1〜2 000 000
总结那些素数需要永远。我使用的是内置的功能“和” Haskell的。
如:
sum listOfPrimes
是否有任何其他选项快?
- 我的主要发电机是在我的代码慢速链接。
解决方案
这听起来像你的问题不是求和的数字,但它们产生。什么是你listOfPrimes的实现?
此纸可以是感兴趣的: http://lambda-the-ultimate.org/节点/ 3127
其他提示
我希望你使用GHC -02而不是GHCI,对不对?您的问题将在产生,不求和。
一层更快的方法是使用流基于融合的序列,其优化更好。与常规列表:
import Data.List
import qualified Data.Map as M
primes :: [Integer]
primes = mkPrimes 2 M.empty
where
mkPrimes n m = case (M.null m, M.findMin m) of
(False, (n', skips)) | n == n' ->
mkPrimes (succ n) (addSkips n (M.deleteMin m) skips)
_ -> n : mkPrimes (succ n) (addSkip n m n)
addSkip n m s = M.alter (Just . maybe [s] (s:)) (n+s) m
addSkips = foldl' . addSkip
-- fuse:
main = print (sum (takeWhile (<= 2000000) primes))
我们得到的,
$ ghc -O2 --make A.hs
$ time ./A
142913828922
./A 9.99s user 0.17s system 99% cpu 10.166 total
切换到流,所以总和。 takeWhile熔断器:
import qualified Data.List.Stream as S
main = print (S.sum (S.takeWhile (<= 2000000) primes))
保存一些小的时间,
$ time ./A
142913828922
./A 9.60s user 0.13s system 99% cpu 9.795 total
不过,您的问题将是首要的产生,我们可以看到,如果我们完全放弃的总和,与去年更换和:
$ time ./A
1999993
./A 9.65s user 0.12s system 99% cpu 9.768 total
所以,找到一个更好的首要发电机。 : - )
最后,还有快速总理发电机上Hackage库:
HTTP:// hackage.haskell.org/packages/archive/primes/0.1.1/doc/html/Data-Numbers-Primes.html
使用它,我们的时间变为:
$ cabal install primes
$ cabal install stream-fusion
$ cat A.hs
import qualified Data.List.Stream as S
import Data.Numbers.Primes
main = print . S.sum . S.takeWhile (<= 2000000) $ primes
$ ghc -O2 -fvia-C -optc-O3 A.hs --make
$ time ./A
142913828922
./A 0.62s user 0.07s system 99% cpu 0.694 total
我这里写了 “埃拉托塞尼的筛” :
import Data.List
import qualified Data.Map as M
primes :: [Integer]
primes = mkPrimes 2 M.empty
where
mkPrimes n m = case (M.null m, M.findMin m) of
(False, (n', skips)) | n == n' ->
mkPrimes (succ n) (addSkips n (M.deleteMin m) skips)
_ -> n : mkPrimes (succ n) (addSkip n m n)
addSkip n m s = M.alter (Just . maybe [s] (s:)) (n+s) m
addSkips = foldl' . addSkip
利用这一点,它需要大约25秒至print . sum $ takeWhile (<= 20000000)
我的桌面上。有待改善?当然,这需要Ĵ不到1秒钟来运行
+/p:i.p:^:_1]20000000 12272577818052
,但它有一个相当优化的素数生成器。
你的函数的慢部分是肯定的素数的产生,而不是sum
功能。生成素数一个很好的办法是:
isprime :: (Integral i) => i -> Bool
isprime n = isprime_ n primes
where isprime_ n (p:ps)
| p*p > n = True
| n `mod` p == 0 = False
| otherwise = isprime_ n ps
primes :: (Integral i) => [i]
primes = 2 : filter isprime [3,5..]
我认为这是非常具有可读性,虽然也许有点令人惊讶的是它在所有工作,因为它使用的primes
列表的递归和懒惰。这也是相当快的,但人们可以在可读性的费用做了进一步的优化。