关于算法:Python中的快速素数筛

A Fast Prime Number Sieve in Python

我一直在使用Eratosthenes的筛子来检查python中的素数生成,人们吹捧为相对较快的选择的解决方案,例如关于优化python中素数生成问题的一些答案中的解决方案,这些解决方案并不简单, 我在这里拥有的简单实现在效率上可以与它们匹敌。 我的实现如下

1
2
3
4
5
6
7
8
9
10
11
12
13
def sieve_for_primes_to(n):
    size = n//2
    sieve = [1]*size
    limit = int(n**0.5)
    for i in range(1,limit):
        if sieve[i]:
            val = 2*i+1
            tmp = ((size-1) - i)//val
            sieve[i+val::val] = [0]*tmp
    return sieve


print [2] + [i*2+1 for i, v in enumerate(sieve_for_primes_to(10000000)) if v and i>0]

定时执行返回

1
2
python -m timeit -n10 -s"import euler""euler.sieve_for_primes_to(1000000)"
10 loops, best of 3: 19.5 msec per loop

虽然上述链接问题的答案中描述的方法是python食谱中最快的方法,但下面给出了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import itertools
def erat2( ):
    D = {  }
    yield 2
    for q in itertools.islice(itertools.count(3), 0, None, 2):
        p = D.pop(q, None)
        if p is None:
            D[q*q] = q
            yield q
        else:
            x = p + q
            while x in D or not (x&1):
                x += p
            D[x] = p

def get_primes_erat(n):
  return list(itertools.takewhile(lambda p: p<n, erat2()))

运行时给出

1
2
python -m timeit -n10 -s"import euler""euler.get_primes_erat(1000000)"
10 loops, best of 3: 697 msec per loop

我的问题是为什么人们会从烹饪书中大肆宣扬以上内容,因为它是理想的主要生成器?


我以最快的方式将您的代码转换为适合@unutbu的素数筛选比较脚本的方式,以列出N以下的所有素数
如下:

1
2
3
4
5
6
7
8
9
10
def sieve_for_primes_to(n):
    size = n//2
    sieve = [1]*size
    limit = int(n**0.5)
    for i in range(1,limit):
        if sieve[i]:
            val = 2*i+1
            tmp = ((size-1) - i)//val
            sieve[i+val::val] = [0]*tmp
    return [2] + [i*2+1 for i, v in enumerate(sieve) if v and i>0]

在我的MBPro i7上,脚本可以快速计算所有小于1000000的素数,但实际上比rwh_primes2,rwh_primes1(1.2),rwh_primes(1.19)和primeSieveSeq(1.12)慢1.5倍(@andreasbriese在页面末尾)。


您只应使用该算法的"推迟"变体。比较您的代码测试运行上限分别为10和20百万

1
2
3
...
print(len( [2] + [i*2+1 for i, v in
  enumerate(sieve_for_primes_to(10000000)) if v and i>0]))

与另一个,分别以664579和1270607的素数运行,以生成

1
2
...
print( list( islice( (p for p in postponed_sieve() ), n-1, n+1)))

显示您的代码"仅"以3.1x ... 3.3x倍的速度运行。 :)速度不快36倍,因为您的计时出于某种原因而显示。

我认为没有人声称它是"理想的"主要生成器,只是它在概念上是干净清晰的。所有这些素数生成函数确实是玩具,无论如何,它们都使用完全不同的算法来处理大量数据。

在较低的范围内,重要的是算法的时间复杂度,应该在~ n^(1+a)a < 0.1...0.2经验增长阶数左右,而这两者似乎都是如此。拥有~ n^1.5甚至~ n^2增长顺序的玩具生成器,玩起来并不有趣。