首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >列出N以下所有素数的最快方法

列出N以下所有素数的最快方法
EN

Stack Overflow用户
提问于 2010-01-15 07:40:27
回答 36查看 244.5K关注 0票数 381

这是我能想到的最好的算法。

代码语言:javascript
复制
def get_primes(n):
    numbers = set(range(n, 1, -1))
    primes = []
    while numbers:
        p = numbers.pop()
        primes.append(p)
        numbers.difference_update(set(range(p*2, n+1, p)))
    return primes

>>> timeit.Timer(stmt='get_primes.get_primes(1000000)', setup='import   get_primes').timeit(1)
1.1499958793645562

它还能做得更快吗?

这段代码有一个缺陷:因为numbers是一个无序集合,所以不能保证numbers.pop()会从集合中删除最小的数字。然而,它对一些输入数字(至少对我来说)是有效的:

代码语言:javascript
复制
>>> sum(get_primes(2000000))
142913828922L
#That's the correct sum of all numbers below 2 million
>>> 529 in get_primes(1000)
False
>>> 529 in get_primes(530)
True
EN

回答 36

Stack Overflow用户

发布于 2010-06-14 13:49:41

更快和更多内存的纯Python代码:

代码语言:javascript
复制
def primes(n):
    """ Returns  a list of primes < n """
    sieve = [True] * n
    for i in range(3,int(n**0.5)+1,2):
        if sieve[i]:
            sieve[i*i::2*i]=[False]*((n-i*i-1)//(2*i)+1)
    return [2] + [i for i in range(3,n,2) if sieve[i]]

或者从半筛开始

代码语言:javascript
复制
def primes1(n):
    """ Returns  a list of primes < n """
    sieve = [True] * (n//2)
    for i in range(3,int(n**0.5)+1,2):
        if sieve[i//2]:
            sieve[i*i//2::i] = [False] * ((n-i*i-1)//(2*i)+1)
    return [2] + [2*i+1 for i in range(1,n//2) if sieve[i]]

速度更快,内存更多的numpy代码:

代码语言:javascript
复制
import numpy
def primesfrom3to(n):
    """ Returns a array of primes, 3 <= p < n """
    sieve = numpy.ones(n//2, dtype=numpy.bool)
    for i in range(3,int(n**0.5)+1,2):
        if sieve[i//2]:
            sieve[i*i//2::i] = False
    return 2*numpy.nonzero(sieve)[0][1::]+1

一个以筛子的三分之一开始的更快的变体:

代码语言:javascript
复制
import numpy
def primesfrom2to(n):
    """ Input n>=6, Returns a array of primes, 2 <= p < n """
    sieve = numpy.ones(n//3 + (n%6==2), dtype=numpy.bool)
    for i in range(1,int(n**0.5)//3+1):
        if sieve[i]:
            k=3*i+1|1
            sieve[       k*k//3     ::2*k] = False
            sieve[k*(k-2*(i&1)+4)//3::2*k] = False
    return numpy.r_[2,3,((3*numpy.nonzero(sieve)[0][1:]+1)|1)]

上面代码的纯python版本(难以编码)如下:

代码语言:javascript
复制
def primes2(n):
    """ Input n>=6, Returns a list of primes, 2 <= p < n """
    n, correction = n-n%6+6, 2-(n%6>1)
    sieve = [True] * (n//3)
    for i in range(1,int(n**0.5)//3+1):
      if sieve[i]:
        k=3*i+1|1
        sieve[      k*k//3      ::2*k] = [False] * ((n//6-k*k//6-1)//k+1)
        sieve[k*(k-2*(i&1)+4)//3::2*k] = [False] * ((n//6-k*(k-2*(i&1)+4)//6-1)//k+1)
    return [2,3] + [3*i+1|1 for i in range(1,n//3-correction) if sieve[i]]

不幸的是,纯python没有采用更简单、更快的numpy方法来进行赋值,而且在循环中调用len()太慢了,就像在[False]*len(sieve[((k*k)//3)::2*k])中一样。所以我不得不即兴纠正输入(避免更多的数学运算),并做一些极端(&痛苦)的数学魔术。

就我个人而言,我认为numpy (被广泛使用)不是Python标准库的一部分是一件遗憾的事情,而且Python开发人员似乎完全忽略了语法和速度方面的改进。

票数 147
EN

Stack Overflow用户

发布于 2010-01-15 07:52:06

Python Cookbook here中有一个非常整洁的示例--该URL上建议的最快版本是:

代码语言:javascript
复制
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

所以这将会给你

代码语言:javascript
复制
def get_primes_erat(n):
  return list(itertools.takewhile(lambda p: p<n, erat2()))

使用pri.py中的以下代码在shell提示符下(我更喜欢这样做)进行测量,我观察到:

代码语言:javascript
复制
$ python2.5 -mtimeit -s'import pri' 'pri.get_primes(1000000)'
10 loops, best of 3: 1.69 sec per loop
$ python2.5 -mtimeit -s'import pri' 'pri.get_primes_erat(1000000)'
10 loops, best of 3: 673 msec per loop

所以看起来Cookbook解决方案的速度要快两倍以上。

票数 42
EN

Stack Overflow用户

发布于 2010-01-16 00:50:02

使用Sundaram's Sieve,我想我打破了pure-Python的记录:

代码语言:javascript
复制
def sundaram3(max_n):
    numbers = range(3, max_n+1, 2)
    half = (max_n)//2
    initial = 4

    for step in xrange(3, max_n+1, 2):
        for i in xrange(initial, half, step):
            numbers[i-1] = 0
        initial += 2*(step+1)

        if initial > half:
            return [2] + filter(None, numbers)

对比:

代码语言:javascript
复制
C:\USERS>python -m timeit -n10 -s "import get_primes" "get_primes.get_primes_erat(1000000)"
10 loops, best of 3: 710 msec per loop

C:\USERS>python -m timeit -n10 -s "import get_primes" "get_primes.daniel_sieve_2(1000000)"
10 loops, best of 3: 435 msec per loop

C:\USERS>python -m timeit -n10 -s "import get_primes" "get_primes.sundaram3(1000000)"
10 loops, best of 3: 327 msec per loop
票数 28
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2068372

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档