就是这样,如果(Haskell 的)纯数组算作纯数组(它们应该,IMO)。复杂性显然是 O(n log (log n)),前提是 accumArray 确实为每个条目花费 O(1) 时间,因为它应该:
import Data.Array.Unboxed
import Data.List (tails, inits)
primes = 2 : [ n |
(r:q:_, px) <- zip (tails (2 : [p^2 | p <- primes]))
(inits primes),
(n,True) <- assocs ( accumArray (\_ _ -> False) True
(r+1,q-1)
[ (m,()) | p <- px
, let s = div (r+p) p * p
, m <- [s,s+p..q-1]]
:: UArray Int Bool ) ]
通过素数的连续平方之间的段计算素数,通过枚举素数列表的相应前缀的倍数生成复合数(使用inits),只需就像任何适当的埃拉托色尼筛子一样,通过重复添加。
所以,素数 {2,3} 用于筛选从 10 到 24 的片段; {2,3,5} 从 26 到 48;等等。 See also.
另外,Python generator-based sieve 也可能被认为是功能性的。 Python 的dicts 的性能非常好,empirically,虽然我不确定那里使用的用于避免重复合成的倍数过度生产方案的确切成本。
更新:testing it 确实产生了有利的结果,正如预期的那样:
{- original heap tweaked nested-feed array-based
(3*p,p) (p*p,2*p) JBwoVL abPSOx
6Uv0cL 2x speed-up another 3x+ speed-up
n^ n^ n^ n^
100K: 0.78s 0.38s 0.13s 0.065s
200K: 2.02s 1.37 0.97s 1.35 0.29s 1.16 0.13s 1.00
400K: 5.05s 1.32 2.40s 1.31 0.70s 1.27 0.29s 1.16
800K: 12.37s 1.29 1M: 2.10s 1.20 0.82s 1.13
2M: 1.71s 1.06
4M: 3.72s 1.12
10M: 9.84s 1.06
overall in the tested range:
1.33 1.21 1.09
-}
empirical orders of growth 计算产生 n 个素数,其中 O(n log log n) 通常被视为 n1.05... 1.10 和 O(n log n log log n) 为 n1.20...1.25。 p>
"nested-feed" 变体实现了postponement 技术(也可以在上面链接的 Python answer 中看到)实现堆大小的二次减小,这显然对经验复杂性有显着影响,即使不是完全在这个答案的基于数组的代码中达到了更好的结果,在 ideone.com 上 is able to produce 10 秒内有 1000 万个素数(总体增长率仅为 n1.09 在测试范围内)。
("original heap"当然是the other answer这里的代码)。