Python中的快速素数筛

时间:2013-04-14 21:15:48

标签: python algorithm primes sieve-of-eratosthenes

我使用Eratosthenes的筛子在python中进行素数生成,人们吹嘘作为一个相对快速的选项的解决方案,例如少数the answers to a question on optimising prime number generation in python中的那些并不简单,而且我的简单实现这里有效率的竞争对手。我的实现如下:

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]

执行返回的时间

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

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

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()))

运行时给出

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

我的问题是为什么人们会从相对复杂的烹饪书中吹出上述内容作为理想的素数生成器?

2 个答案:

答案 0 :(得分:8)

我将您的代码转换为符合Fastest way to list all primes below N @unutbu的主要筛选比较脚本 如下:

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上,脚本正在快速计算所有素数&lt; 1000000但实际上比rwh_primes2,rwh_primes1(1.2),rwh_primes(1.19)和primeSieveSeq(1.12)(页面末尾的@andreasbriese)慢1.5倍。

答案 1 :(得分:3)

您应该只使用"postponed" variant of that algorithm。比较您的代码test run最多10到20万的上限,为

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

与另一个,run at生成664579和1270607素数的相应数字,如

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

显示您的代码“仅” 3.1x ... 3.3x 运行得更快。 :) 36x 时间更快,因为您的时间因某些原因而显示。

我认为没有人声称它是一个“理想的”素数发生器,只是它是一个概念上干净清晰的发生器。所有这些主要生成功能都是玩具,真正的东西是使用非常大的数字,无论如何使用完全不同的算法。

在低范围内,重要的是算法的时间复杂度,它应该在~ n^(1+a)a < 0.1...0.2 empirical orders of growth附近,这两者似乎都是真的。拥有~ n^1.5或甚至~ n^2增长订单的玩具生成器玩起来并不好玩。