【发布时间】:2023-03-10 14:19:01
【问题描述】:
考虑到这两种编写函数的方法,该函数可以找到直到特定数字的所有素数:
primes1 = iterate
(\ps -> ps ++ [([x |
x <- [last ps + 1..],
all (\p -> x `mod` p /= 0) ps] !! 0)])
[2]
primesTo1 :: Integer -> [Integer]
primesTo1 n = init $ head $ dropWhile (\p -> last p <= n) primes1
primes2 = iterate
(\ps -> ([x |
x <- [head ps + 1..],
all (\p -> x `mod` p /= 0) ps] !! 0)
: ps)
[2]
primesTo2 :: Integer -> [Integer]
primesTo2 n = tail $ head $ dropWhile (\p -> head p <= n) primes2
为什么primesTo1 比primesTo2 快很多,尽管使用了不同的函数; primesTo1 使用 ++、last 和 init 而不是 :、head 和 tail 用于 primesTo2。
ghci 和 :set +s 的输出:
*Main> primesTo1 10000
...
(0.51 secs, 124779776 bytes)
*Main> primesTo2 10000
...
(3.30 secs, 570648032 bytes)
用ghc -O2编译:
$ time ./primes1
...
./primes1 0.06s user 0.00s system 68% cpu 0.089 total
$ time ./primes2
...
./primes2 0.28s user 0.00s system 98% cpu 0.283 total
注意:我不是在为 Haskell 寻找最佳的素数生成器,我只是对这两个函数的速度差异感到困惑。
【问题讨论】:
-
primes1自下而上扫描素数,primes2自上而下扫描。后者是过滤出合数的一种非常糟糕的慢速方法。 -
@n.m.谢谢,这很有道理。
-
能被 2 或 3 整除的数字远远多于能被 997 整除的数字。
-
@DanielFischer 我认为 n.m.已经说过了。
-
我只是对此进行了扩展,说明了为什么这是一种缓慢的方法。