【问题标题】:Lazy Sieve of Eratosthenes in PythonPython 中 Eratosthenes 的惰性筛选器
【发布时间】:2011-07-16 03:54:45
【问题描述】:

我正在尝试在 Python 3.2 中编写一个惰性版本的 Sieve of Eratosthenes。代码如下:

import itertools
def primes():
    candidates = itertools.count(2)
    while True:
        prime = next(candidates)
        candidates = (i for i in candidates if i % prime)
        yield prime

但是,当我遍历 primes() 时,我只能得到连续的数字。例如,

print(list(itertools.islice(primes(),0,10)))

打印列表

[2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

令我惊讶的是,以下对 primes() 的微小修改使其工作:

def primes():
    candidates = itertools.count(2)
    while True:
        prime = next(candidates)
        candidates = (i for i in candidates if i % prime)
        next(itertools.tee(candidates)[1]) ########### NEW LINE
        yield prime

我猜我遗漏了有关生成器参数范围的一些信息

candidates = (i for i in candidates if i % prime)

但是如果不添加这个看起来随机的新行,我看不到如何修复代码。有人知道我做错了什么吗?谢谢。

【问题讨论】:

  • 请注意next(itertools.tee(candidates)[1]) 可以重写为next(candidates) 或在python 2 中candidates.next()
  • prime 是否只绑定一次而不是每次循环迭代,这意味着它不会像预期的那样在每个生成器中保持不变?

标签: python python-3.x generator


【解决方案1】:

真正的解决方法是替换:

candidates = (i for i in candidates if i % prime)

与:

candidates = (lambda prime: (i for i in candidates if i % prime))(prime)

【讨论】:

  • 我们通常不会在 Python 中看到像这样的自评估函数 :)
  • 关键问题在(i for i in x if i % p)x在开始时被绑定一次,但是p是为每个项目查找的。​​span>
  • -1 伙计,这样构建 lambda 函数是完全错误的。
  • @Franklin 我不同意。在高度数学化的程序中,简洁性和效率很重要,我相信大量甚至复杂地使用高阶函数是合理的。 +1。
  • 好的...这对我有用...如果我只需要计算高达 ~7900 的素数。之后,筛子因“RuntimeError:超出最大递归深度”而爆炸。由于我不打算重新编译 Python 来更改递归深度设置,我不认为这对我来说是计算素数的好方法......
【解决方案2】:

如果您担心变量的范围,请创建对象/函数来为您保留这些变量:

def filter_multiples(n, xs):
    for i in xs:
        if i % n
            yield i

def primes():
    candidates = itertools.count(2)
    while True:
        prime = next(candidates)
        candidates = filter_multiples(prime, candidates)
        yield prime

(我现在无法使用 Pytho 解释器,所以我不知道这最终是否真的有效......)


顺便说一句,您使用的算法并不是 Erastothenes 的筛子。如果你有时间,可以看看这篇很酷的论文:http://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf

【讨论】:

【解决方案3】:

这是基于论文中的haskell代码的真正素筛的Python实现:The Genuine Sieve of Eratosthenes by Melissa E. O'Neill

它不使用递归或试除法,但相当消耗内存。

from heapq import heappush, heappop, heapreplace
def sieve():
    w = [2,4,2,4,6,2,6,4,2,4,6,6,2,6,4,2,6,4,6,8,4,2,4,2,4,8,6,4,6,2,4,6,2,6,6,4,2,4,6,2,6,4,2,4,2,10,2,10]
    for p in [2,3,5,7]: yield p
    n,o = 11,0
    t = []
    l = len(w)
    p = n
    heappush(t, (p*p,n,o,p))
    yield p
    while True:
        n,o = n+w[o],(o+1)%l
        p = n
        if not t[0][0] <= p:
            heappush(t, (p*p,n,o,p))
            yield p
            continue
        while t[0][0] <= p:
            _,b,c,d = t[0]
            heapreplace(t, (b*d,b+w[c],(c+1)%l,d))

以下内容:

import itertools
print list(itertools.islice(sieve(),0,10))

打印:

[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

【讨论】:

  • 我确定这是正确的(它有效!)但我完全不知道它的含义。请至少使用更具描述性的变量名称!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-19
  • 1970-01-01
  • 1970-01-01
  • 2016-04-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多