更新要在for循环中迭代的列表/集

时间:2014-02-13 12:31:19

标签: python set sieve-of-eratosthenes

我正在尝试使用Sieve的方法制作一组素数。但似乎prime = prime - set(range(2*n,N,n))未更新prime中的for n in prime:集。

N = int(raw_input()) + 1
prime = set(range(2,N))

for n in prime:
    print n
    prime = prime - set(range(2*n,N,n))

例如,我输入N = 100。输出为1,2,3,...,100,使代码变得非常慢。我该怎么做才能更新一套?

修改 更深入一点:你能解释增强和非增强任务之间的区别吗?只有一套我真的没办法完成吗?因为它看起来很天真,我不能完全相信Python这样的高级语言无法实现这一点,因为在C ++中我只能用一个布尔值数组来实现它

1 个答案:

答案 0 :(得分:4)

for循环仅一次评估可迭代表达式。你没有改变prime,你正在重新绑定一个新对象的名称(prime - set(range(2*n,N,n))的输出,for循环永远不会看到它。

另请注意,for上的set循环将以任意顺序执行此操作;数字排序。你可以很容易地先获得非素数。

但是,如果您使用扩充分配来就地更改prime,则会在迭代时收到有关更改集的错误消息:

>>> N = 50
>>> prime  = set(range(2, N))
>>> for n in prime:
...     print n
...     prime -= set(range(2 * n, N, n))
... 
2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: Set changed size during iteration

您必须直接在range()上循环并跟踪非素数:

non_prime = set()
primes = []

for n in range(2, N):
    if n in non_prime:
        continue
    non_prime |= set(range(2 * n, N, n))
    primes.append(n)

演示:

>>> non_prime = set()
>>> primes = []
>>> for n in range(2, N):
...     if n in non_prime:
...         continue
...     non_prime |= set(range(2 * n, N, n))
...     primes.append(n)
... 
>>> primes
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]

或者您可以使用while循环,并将候选人设置为空:

candidates = set(range(2, N))
primes = []

while candidates:
    n = min(candidates)
    candidates -= set(range(2 * n, N, n))
    candidates.remove(n)
    primes.append(n)

但这需要每次循环遍历所有candidates以找到最小值。

请注意,您可以在此处使用range(n * n, N, n)进一步缩小尺寸范围;过去2 * n之后的所有2 * 2值都已消除,所有3 * n过去3 * 3等等:

>>> primes = []
>>> for n in range(2, N):
...     if n in non_prime:
...         continue
...     non_prime |= set(range(n * n, N, n))
...     primes.append(n)
... 
>>> primes
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]

最后但同样重要的是,实施筛子的常用方法是使用布尔值列表来标记非素数:

sieve = [True] * N
sieve[0] = sieve[1] = False
primes = []

for (n, is_prime) in enumerate(sieve):
    if is_prime:
        primes.append(n)
        for n in range(n * n, N, n):
            sieve[n] = False