素数的惰性列表

Posted

技术标签:

【中文标题】素数的惰性列表【英文标题】:Lazy List of Prime Numbers 【发布时间】:2011-04-05 12:29:28 【问题描述】:

如何在 Haskell 中实现一个素数列表,以便可以懒惰地检索它们?

我是 Haskell 的新手,想了解惰性求值功能的实际用途。

【问题讨论】:

类似***.com/questions/1764163/…? 考虑hackage.haskell.org/package/primes 恰恰相反:在 Haskell 中创建非惰性素数列表是一项棘手的任务 by walpen at codegolf: nubBy (((==0).).rem) [2..]。要在 GHCi 中试用它,首先使用 Prelude> :m +Data.List 调出 Data.List 模块。但是惰性在这里没有任何作用,除了允许 unbounded 定义。 [2..10000] 也可以使用并严格评估。 我现在正在使用 GHC-7.10。有关更改的理由,请参阅 ***.com/a/33533257/946226。 【参考方案1】:

这是一个简短的 Haskell 函数,它枚举来自 Literate Programs: 的素数

primes :: [Integer]
primes = sieve [2..]
  where
    sieve (p:xs) = p : sieve [x|x <- xs, x `mod` p > 0]

显然,这不是 Eratosthenes 的筛子(谢谢,Landei)。我认为这仍然是一个有启发性的示例,它表明您可以在 Haskell 中编写非常优雅、简短的代码,并且表明选择错误的数据结构会如何严重影响效率。

【讨论】:

请阅读本文并重新考虑您的答案:cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf “错误的数据结构”(即列表)与该代码的 extreme 低效率无关(O(n^2),in n primes产生 ),而是在每个新发现的素数而不是其正方形上过早启动过滤器的结果。使用过滤器创建correctly postponed,它会以大约 O(n^1.4..1.45) 运行(在产生的n 素数中),就像任何其他正常的试验部门一样。【参考方案2】:

haskell wiki 中有许多延迟生成素数序列的解决方案。第一个也是最简单的是Postponed Turner sieve:(old revision ... NB)

primes :: [Integer]
primes = 2: 3: sieve (tail primes) [5,7..]
 where 
  sieve (p:ps) xs = h ++ sieve ps [x | x <- t, x `rem` p /= 0]  
                                -- or:  filter ((/=0).(`rem`p)) t
                  where (h,~(_:t)) = span (< p*p) xs

【讨论】:

【参考方案3】:

@nikie 接受的答案效率不高,几千后变得相对较慢,但@sleepynate 的答案要好得多。我花了一些时间来理解它,因此这里是相同的代码,只是变量命名更清楚:

lazyPrimes :: [Integer]
lazyPrimes = 2: 3: calcNextPrimes (tail lazyPrimes) [5, 7 .. ]
  where
    calcNextPrimes (p:ps) candidates =
      let (smallerSquareP, (_:biggerSquareP)) = span (< p * p) candidates in
      smallerSquareP ++ calcNextPrimes ps [c | c <- biggerSquareP, rem c p /= 0]

主要思想是下一个素数的候选已经不包含可以被任何小于给函数的第一个素数的素数整除的数字。所以如果你打电话

calcNextPrimes (5:ps) [11,13,17..]

候选列表不包含能被23整除的数字,这意味着第一个非主候选将是5 * 5,因为5* 25 * 35 * 4是已经淘汰了。这使您可以选择所有小于 5 的平方的候选者,然后将它们直接添加到素数中,然后筛选其余的以消除所有可被 5 整除的数字。

【讨论】:

【参考方案4】:
primes = 2 : [x | x <- [3..], all (\y -> x `mod` y /= 0) 
                   (takeWhile (<= (floor . sqrt $ fromIntegral x)) primes)]

列表中有2,对于每个大于2的整数x,检查是否对所有@987654323 primes 中的@ 使得 y &lt;= sqrt(x), x mod y != 0 成立,这意味着 x 除了 1 和它自己之外没有其他因素。

【讨论】:

您可能想要使用[3,5..] 而不是[3..]

以上是关于素数的惰性列表的主要内容,如果未能解决你的问题,请参考以下文章

通过惰性列表加载图像?

有哪些 OCaml 库用于惰性列表处理?

惰性列表布局

Python 嵌套惰性列表

GTK:无限的惰性小部件列表

当参数是列表时,惰性求值如何工作?