有一个快速,功能性的素数发生器吗?

时间:2017-02-08 16:26:54

标签: algorithm functional-programming primes sieve-of-eratosthenes

假设我有一个自然数n,我希望列出(或其他)所有素数的列表(或其他)n

经典的筛选算法在O(n log n)时间和O(n)空间运行 - 对于更多命令式语言而言它很好,但需要对列表和随机访问进行就地修改,基本方式。

这是一个涉及优先级队列的功能版本,非常漂亮 - 您可以查看here。这在大约O(n / log(n))处具有更好的空间复杂度(渐近更好但在实际尺度上存在争议)。不幸的是,时间分析是令人讨厌的,但它几乎O(n^2)(实际上,我认为它与O(n log(n) Li(n))相关,但log(n) Li(n)约为n )。

渐近地说,使用连续的试验除法,在生成它时检查每个数字的素数实际上会更好,因为这只需要O(1)空格和O(n^{3/2})时间。还有更好的方法吗?

编辑:事实证明我的计算完全不正确。文章中的算法是O(n (log n) (log log n)),文章解释并证明(并参见下面的答案),而不是我上面提到的复杂的混乱。如果有一个真正的算法,我仍然很乐意看到真正的O(n log log n)纯算法。

3 个答案:

答案 0 :(得分:3)

这是Melissa O'Neill算法的Haskell实现(来自链接文章)。与Gassa链接的实现不同,我最少使用了懒惰,因此性能分析很明确 - O(n log n log log n),即n log log中的linearithmic n,Eratosthenes命令筛选的写入次数。

堆实现只是一个锦标赛树。平衡逻辑在push;通过每次交换子进程,我们确保对于每个分支,左子树的大小相同或者与右子树相比更大,这确保了深度O(log n)。

module Sieve where

type Nat = Int

data Heap = Leaf !Nat !Nat
          | Branch !Nat !Heap !Heap
          deriving Show

top :: Heap -> Nat
top (Leaf n _) = n
top (Branch n _ _) = n

leaf :: Nat -> Heap
leaf p = Leaf (3 * p) p

branch :: Heap -> Heap -> Heap
branch h1 h2 = Branch (min (top h1) (top h2)) h1 h2

pop :: Heap -> Heap
pop (Leaf n p) = Leaf (n + 2 * p) p
pop (Branch _ h1 h2)
  = case compare (top h1) (top h2) of
        LT -> branch (pop h1) h2
        EQ -> branch (pop h1) (pop h2)
        GT -> branch h1 (pop h2)

push :: Nat -> Heap -> Heap
push p h@(Leaf _ _) = branch (leaf p) h
push p (Branch _ h1 h2) = branch (push p h2) h1

primes :: [Nat]
primes
  = let helper n h
          = case compare n (top h) of
                LT -> n : helper (n + 2) (push n h)
                EQ -> helper (n + 2) (pop h)
                GT -> helper n (pop h)
      in 2 : 3 : helper 5 (leaf 3)

答案 1 :(得分:2)

在这里,如果(Haskell' s)纯数组计为纯(他们应该,IMO)。复杂性显然是 O(n log(log n)),前提是accumArray确实为每个索引提供了 O(1)时间,因为它应该:

import Data.Array.Unboxed 
import Data.List (tails, inits)

ps = 2 : [n | (r:q:_, px) <- (zip . tails . (2:) . map (^2)) ps (inits ps),
              (n,True)    <- assocs (
                               accumArray (\_ _ -> False) True (r+1,q-1)
                                 [(m,()) | p <- px, let s=(r+p)`div`p*p, 
                                           m <- [s,s+p..q-1]] :: UArray Int Bool )]

按素数的连续平方(map (^2)位)之间的段计算素数,通过枚举素数的增长前缀的倍数(inits位)生成复合,就像任何适当的Eratosthenes筛子一样会反复添加。

因此,素数 {2,3} 用于筛选从 10 24 的片段; {2,3,5} 26 48 ;等等。 See also

此外,Python generator-based sieve也可能被视为功能。 Python的dict表现非常出色,empirically,虽然我不确定那里使用的倍数过量生成方案的确切成本,以避免重复复合。< / p>

更新: 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)通常被视为 n 1.05。 ..1.10 O(n log n log log n) as n 1.20 ... 1.25

"nested-feed"变体实现了postponement技术(如上面链接的Python answer中所见),它实现了堆大小的二次减少,这显然对经验有显着影响复杂性,即使没有达到这个答案的基于数组的代码仍然更好的结果,在{ides.com.com上10秒内is able to produce 1000万个素数(总体增长率仅为 n < sup> 1.09 在测试范围内)。

"original heap"当然是the other answer的代码)。

答案 2 :(得分:0)

我推导出一个素数生成函数(按顺序生成所有素数)。为它创建了6页的证明。我认为它实际上是历史上的第一个主要生成函数(至少我找不到任何其他的例子)。

这是:enter image description here

(-1)^((4*gamma(x)+4)/x)-1

不确定计算的速度有多快。它为所有素数返回0(或者它可能是1,不记得了)。伽玛函数基本上是阶乘的,因此可以在早期快速进行。将负1提升为分数指数是一个完整的其他野兽,我相信它在base_e中使用积分,或者可能是一些三角函数;不记得了。

我不知道LaTeX所以,如果有人想编辑我的帖子并包含一个令人惊叹的LaTeX版本!