在python中实现“ Eratosthenes筛”时出现麻烦的过滤器行为

时间:2019-05-03 09:01:16

标签: python python-3.x algorithm primes

我想出了"Sieve of Eratosthenes" trial division 变体的Python代码:

import itertools

def sieve():
    # begin with all natural numbers above 1
    picker = itertools.count(2)
    while True:
        # take the next available number
        v = next(picker)
        yield v
        # filter from the generator its multiples
        picker = filter(lambda x: x % v != 0, picker)

它不符合我的预期。

在调试时,我得到一些行为,我不知道何时调用filter:lambda的x参数得到一个具体的参数,该参数是picker中的下一个元素发电机。即使看了filter的文档,我也不了解这种行为。

运行

s = sieve()
for i in range(5):
    print(next(s))

我得到:

2
3
4
5
6

代替

2
3
5
7
11

更新:

我的错误来自对lambda如何在Python中工作的误解,更多关于here

向lambda添加一个额外的参数可解决此问题:

picker = filter(lambda x, prime = v: x % prime != 0, picker)

1 个答案:

答案 0 :(得分:3)

我认为问题是因为您依赖局部变量,并且您创建的生成器(使用filter())引用了局部变量,当生成器继续进行迭代时,这些局部变量将被覆盖。

如果您改用局部函数,则可以正常工作:

def sieve():
    def selector(iterator, d):
        for x in iterator:
            if x % d != 0:
                yield x
    picker = itertools.count(2)
    while True:
        # take the next available number
        prime = next(picker)
        yield prime
        # filter from the generator its multiples
        picker = selector(picker, prime)

尝试list(itertools.islice(sieve(), 10))显示:

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

但是我确实需要指出,这是一个纯粹的教育性解决方案,用于说明事物的工作方式。我不会建议将此解决方案用于任何有效的代码。它在内部建立了大量的生成器,只有当您释放父生成器的句柄时才释放它们。这可能会浪费资源,并且您可以创建无限数量的质数而无需创建无限数量的生成器。