繁体   English   中英

是否有一个快速的、功能性的素数生成器?

[英]Is there a fast, functional prime generator?

假设我有一个自然数n并且我想要一个直到n的所有素数的列表(或其他)。

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

有一个涉及优先级队列的功能版本,它非常巧妙——您可以在此处查看 这在大约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)纯算法,我仍然会喜欢。

这是 Melissa O'Neill 算法的 Haskell 实现(来自链接文章)。 与 Gassa 链接的实现不同,我很少使用懒惰,因此性能分析很清楚—— O(n log n log log n) ,即,n log log 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)

在这里,如果(Haskell 的)纯数组算作纯数组(他们应该,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}用于筛选从1024的段; {2,3,5}2648 等等。 另见

此外,基于 Python 生成器的筛选器也可能被认为是功能性的。 根据经验,Python 的dict非常好,尽管我不确定在那里使用的倍数过度生产方案的确切成本,以避免重复组合。


更新:正如预期的那样,测试它确实产生了有利的结果:

{-     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
-}

and O(n log n log log n) as .使用经验增长顺序计算产生n 个素数,其中O(n log log n)通常被视为O(n log n log log n)

in the tested range). "nested-feed"变体实现了延迟技术(也可以在上面链接的 Python 答案中看到),它实现了堆大小的二次减小,这显然对经验复杂性有显着影响,即使没有完全达到更好的结果对于这个答案的基于数组的代码,它能够在 ideone.com 上在不到 10 秒的时间内生成1000 万个素数(在测试范围内,整体增长率仅为 )。

“原始堆”当然是此处其他答案中的代码)。

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

这里是:
在此处输入图片说明

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

不确定它可以计算多快。 它为所有素数返回 0(或者它可能是 1,不记得了)。 Gamma 函数本质上是阶乘的,因此可以在早期很快。 将负 1 提高到分数指数是另一回事,但我相信它可能使用 base_e 中的积分,或者可能使用一些三角函数; 不记得了。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM