7

我一直在使用 Eratosthenes 的筛子在 python 中生成素数,人们吹捧为相对较快的选择的解决方案,例如 关于在 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食谱中最快的方法,但下面给出

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

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

4

2 回答 2

10

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

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

于 2013-09-25T06:18:24.993 回答
3

您应该只使用该算法的“推迟”变体。比较您的代码测试运行上限为 10 和 2000 万,如

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

与另一个,以 664579 和 1270607 素数的相应数字运行以产生,如

...
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至增长顺序的玩具发电机玩起来并不好玩。

于 2013-04-16T00:37:13.073 回答