由于尚未有人展示或解释真正的筛子,我会尝试。
基本方法是从 2 开始计数并消除 2*2 和所有 2 的更高倍数(即 4、6、8...),因为它们都不能是素数。3 在第一轮中幸存下来,所以它是素数,现在我们消除 3*3 和所有 3 的更高倍数(即 9、12、15...)。4 个被消除,5 个幸存,等等。每个素数的平方是一种优化,它利用了每个新素数的所有较小倍数都将在前几轮中被消除的事实。当您使用此过程计数和消除非素数时,只会留下素数。
这是一个非常简单的版本,注意它不使用模除法或根:
def primes(n): # Sieve of Eratosthenes
prime, sieve = [], set()
for q in xrange(2, n+1):
if q not in sieve:
prime.append(q)
sieve.update(range(q*q, n+1, q))
return prime
>>> primes(100)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73
79, 83, 89, 97]
上面的简单方法速度惊人,但没有利用素数只能是奇数的事实。
这是一个基于生成器的版本,它比我发现的任何其他版本都快,但在我的机器上达到了 n = 10**8 的 Python 内存限制。
def pgen(n): # Fastest Eratosthenes generator
yield 2
sieve = set()
for q in xrange(3, n+1, 2):
if q not in sieve:
yield q
sieve.update(range(q*q, n+1, q+q))
>>> timeit('n in pgen(n)', setup="from __main__ import pgen; n=10**6", number=10)
5.987867565927445
这是一个稍慢但内存效率更高的生成器版本:
def pgen(maxnum): # Sieve of Eratosthenes generator
yield 2
np_f = {}
for q in xrange(3, maxnum+1, 2):
f = np_f.pop(q, None)
if f:
while f != np_f.setdefault(q+f, f):
q += f
else:
yield q
np = q*q
if np < maxnum:
np_f[np] = q+q
>>> timeit('n in pgen(n)', setup="from __main__ import pgen; n=10**6", number=10)
7.420101730225724
>>> list(pgen(10))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
要测试一个数字是否为素数,只需执行以下操作:
>>> 539 in pgen(539)
False
>>> 541 in pgen(541)
True
这里有一些关于这个内存效率更高的版本如何工作的提示。它使用 adict
仅存储最少的信息,下一个非质数(作为键)及其因子(作为值)。当在 中找到每个非素数时dict
,将其删除并添加具有相同因子值的下一个非素数键。