【问题标题】:Fastest way to list all primes below N列出 N 以下所有素数的最快方法
【发布时间】:2011-01-05 07:38:25
【问题描述】:

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

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() 会从集合中删除最小的数字。不过,它适用于某些输入数字(至少对我而言):

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

【问题讨论】:

  • 如果数字声明为 numbers = set(range(n, 2, -2)),则有问题的代码片段要快得多。但无法击败 sundaram3。感谢您的提问。
  • 如果答案中有 Python 3 版本的函数会很好。
  • 肯定有一个库可以做到这一点,所以我们不必自己动手> xkcd 承诺 Python 就像import antigravity 一样简单。没有像require 'prime'; Prime.take(10)(Ruby)这样的东西吗?
  • @ColonelPanic 碰巧我为 Py3 更新了 github.com/jaredks/pyprimesieve 并添加到 PyPi。它肯定比这些更快,但不是数量级 - 比最好的 numpy 版本快约 5 倍。
  • @ColonelPanic:我认为编辑旧答案以注意它们已经老化是合适的,因为这使它成为更有用的资源。如果“已接受”的答案不再是最佳答案,则可以在问题中编辑注释,并使用 2015 年的更新将人们指向当前的最佳方法。

标签: python math optimization primes


【解决方案1】:

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

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

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

$ 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 解决方案的速度是原来的两倍多。

【讨论】:

  • @jbochi,不客气——但请查看该 URL,包括致谢:我们花了 10 个人共同改进代码到这一点,包括Python 性能杰出人物,例如 Tim Peters 和 Raymond Hettinger(我在编辑印刷版食谱后编写了食谱的最终文本,但在编码方面,我的贡献与其他人不相上下)——最后,它是非常微妙和微调的代码,这不足为奇!-)
  • @Alex:知道您的代码“只”是我的两倍,这让我感到非常自豪。 :) URL 读起来也很有趣。再次感谢。
  • 只要稍作改动,它就可以变得更快:见stackoverflow.com/questions/2211990/…
  • ... 它 can be made yet faster 具有额外的 ~1.2x-1.3x 加速,内存占用从 O(n) 到 O(sqrt(n)) 的显着减少以及经验时间复杂度的改进, 通过推迟向字典添加素数,直到在输入中看到它们的 squareTest it here.
【解决方案2】:

算法速度很快,但有一个严重的缺陷:

>>> sorted(get_primes(530))
[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, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163,
167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251,
257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349,
353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443,
449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 527, 529]
>>> 17*31
527
>>> 23*23
529

您假设numbers.pop() 将返回集合中最小的数字,但这根本无法保证。集合是无序的,pop() 删除并返回一个 arbitrary 元素,因此它不能用于从剩余数字中选择下一个素数。

【讨论】:

    【解决方案3】:

    警告: timeit 结果可能因硬件或 Python 版本。

    下面是一个比较多个实现的脚本:

    非常感谢stephan 让我注意到了 sieve_wheel_30。 primesfrom2to、primesfrom3to、rwh_primes、rwh_primes1 和 rwh_primes2 归功于 Robert William Hanks

    使用 psyco 测试的普通 Python 方法中,n=1000000, rwh_primes1 是测试最快的。

    +---------------------+-------+
    | Method              | ms    |
    +---------------------+-------+
    | rwh_primes1         | 43.0  |
    | sieveOfAtkin        | 46.4  |
    | rwh_primes          | 57.4  |
    | sieve_wheel_30      | 63.0  |
    | rwh_primes2         | 67.8  |    
    | sieveOfEratosthenes | 147.0 |
    | ambi_sieve_plain    | 152.0 |
    | sundaram3           | 194.0 |
    +---------------------+-------+
    

    在测试的普通 Python 方法中,没有 psyco,n=1000000, rwh_primes2 是最快的。

    +---------------------+-------+
    | Method              | ms    |
    +---------------------+-------+
    | rwh_primes2         | 68.1  |
    | rwh_primes1         | 93.7  |
    | rwh_primes          | 94.6  |
    | sieve_wheel_30      | 97.4  |
    | sieveOfEratosthenes | 178.0 |
    | ambi_sieve_plain    | 286.0 |
    | sieveOfAtkin        | 314.0 |
    | sundaram3           | 416.0 |
    +---------------------+-------+
    

    在所有测试的方法中,允许 numpy,对于 n=1000000, primesfrom2to 是最快的测试。

    +---------------------+-------+
    | Method              | ms    |
    +---------------------+-------+
    | primesfrom2to       | 15.9  |
    | primesfrom3to       | 18.4  |
    | ambi_sieve          | 29.3  |
    +---------------------+-------+
    

    使用以下命令测量时间:

    python -mtimeit -s"import primes" "primes.{method}(1000000)"
    

    {method} 替换为每个方法名称。

    primes.py:

    #!/usr/bin/env python
    import psyco; psyco.full()
    from math import sqrt, ceil
    import numpy as np
    
    def rwh_primes(n):
        # https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188
        """ Returns  a list of primes < n """
        sieve = [True] * n
        for i in xrange(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 xrange(3,n,2) if sieve[i]]
    
    def rwh_primes1(n):
        # https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188
        """ Returns  a list of primes < n """
        sieve = [True] * (n/2)
        for i in xrange(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 xrange(1,n/2) if sieve[i]]
    
    def rwh_primes2(n):
        # https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188
        """ Input n>=6, Returns a list of primes, 2 <= p < n """
        correction = (n%6>1)
        n = {0:n,1:n-1,2:n+4,3:n+3,4:n+2,5:n+1}[n%6]
        sieve = [True] * (n/3)
        sieve[0] = False
        for i in xrange(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+4*k-2*k*(i&1))/3::2*k]=[False]*((n/6-(k*k+4*k-2*k*(i&1))/6-1)/k+1)
        return [2,3] + [3*i+1|1 for i in xrange(1,n/3-correction) if sieve[i]]
    
    def sieve_wheel_30(N):
        # http://zerovolt.com/?p=88
        ''' Returns a list of primes <= N using wheel criterion 2*3*5 = 30
    
    Copyright 2009 by zerovolt.com
    This code is free for non-commercial purposes, in which case you can just leave this comment as a credit for my work.
    If you need this code for commercial purposes, please contact me by sending an email to: info [at] zerovolt [dot] com.'''
        __smallp = ( 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, 101, 103, 107, 109, 113, 127, 131, 137, 139,
        149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
        229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311,
        313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401,
        409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491,
        499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599,
        601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683,
        691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797,
        809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887,
        907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997)
    
        wheel = (2, 3, 5)
        const = 30
        if N < 2:
            return []
        if N <= const:
            pos = 0
            while __smallp[pos] <= N:
                pos += 1
            return list(__smallp[:pos])
        # make the offsets list
        offsets = (7, 11, 13, 17, 19, 23, 29, 1)
        # prepare the list
        p = [2, 3, 5]
        dim = 2 + N // const
        tk1  = [True] * dim
        tk7  = [True] * dim
        tk11 = [True] * dim
        tk13 = [True] * dim
        tk17 = [True] * dim
        tk19 = [True] * dim
        tk23 = [True] * dim
        tk29 = [True] * dim
        tk1[0] = False
        # help dictionary d
        # d[a , b] = c  ==> if I want to find the smallest useful multiple of (30*pos)+a
        # on tkc, then I need the index given by the product of [(30*pos)+a][(30*pos)+b]
        # in general. If b < a, I need [(30*pos)+a][(30*(pos+1))+b]
        d = {}
        for x in offsets:
            for y in offsets:
                res = (x*y) % const
                if res in offsets:
                    d[(x, res)] = y
        # another help dictionary: gives tkx calling tmptk[x]
        tmptk = {1:tk1, 7:tk7, 11:tk11, 13:tk13, 17:tk17, 19:tk19, 23:tk23, 29:tk29}
        pos, prime, lastadded, stop = 0, 0, 0, int(ceil(sqrt(N)))
        # inner functions definition
        def del_mult(tk, start, step):
            for k in xrange(start, len(tk), step):
                tk[k] = False
        # end of inner functions definition
        cpos = const * pos
        while prime < stop:
            # 30k + 7
            if tk7[pos]:
                prime = cpos + 7
                p.append(prime)
                lastadded = 7
                for off in offsets:
                    tmp = d[(7, off)]
                    start = (pos + prime) if off == 7 else (prime * (const * (pos + 1 if tmp < 7 else 0) + tmp) )//const
                    del_mult(tmptk[off], start, prime)
            # 30k + 11
            if tk11[pos]:
                prime = cpos + 11
                p.append(prime)
                lastadded = 11
                for off in offsets:
                    tmp = d[(11, off)]
                    start = (pos + prime) if off == 11 else (prime * (const * (pos + 1 if tmp < 11 else 0) + tmp) )//const
                    del_mult(tmptk[off], start, prime)
            # 30k + 13
            if tk13[pos]:
                prime = cpos + 13
                p.append(prime)
                lastadded = 13
                for off in offsets:
                    tmp = d[(13, off)]
                    start = (pos + prime) if off == 13 else (prime * (const * (pos + 1 if tmp < 13 else 0) + tmp) )//const
                    del_mult(tmptk[off], start, prime)
            # 30k + 17
            if tk17[pos]:
                prime = cpos + 17
                p.append(prime)
                lastadded = 17
                for off in offsets:
                    tmp = d[(17, off)]
                    start = (pos + prime) if off == 17 else (prime * (const * (pos + 1 if tmp < 17 else 0) + tmp) )//const
                    del_mult(tmptk[off], start, prime)
            # 30k + 19
            if tk19[pos]:
                prime = cpos + 19
                p.append(prime)
                lastadded = 19
                for off in offsets:
                    tmp = d[(19, off)]
                    start = (pos + prime) if off == 19 else (prime * (const * (pos + 1 if tmp < 19 else 0) + tmp) )//const
                    del_mult(tmptk[off], start, prime)
            # 30k + 23
            if tk23[pos]:
                prime = cpos + 23
                p.append(prime)
                lastadded = 23
                for off in offsets:
                    tmp = d[(23, off)]
                    start = (pos + prime) if off == 23 else (prime * (const * (pos + 1 if tmp < 23 else 0) + tmp) )//const
                    del_mult(tmptk[off], start, prime)
            # 30k + 29
            if tk29[pos]:
                prime = cpos + 29
                p.append(prime)
                lastadded = 29
                for off in offsets:
                    tmp = d[(29, off)]
                    start = (pos + prime) if off == 29 else (prime * (const * (pos + 1 if tmp < 29 else 0) + tmp) )//const
                    del_mult(tmptk[off], start, prime)
            # now we go back to top tk1, so we need to increase pos by 1
            pos += 1
            cpos = const * pos
            # 30k + 1
            if tk1[pos]:
                prime = cpos + 1
                p.append(prime)
                lastadded = 1
                for off in offsets:
                    tmp = d[(1, off)]
                    start = (pos + prime) if off == 1 else (prime * (const * pos + tmp) )//const
                    del_mult(tmptk[off], start, prime)
        # time to add remaining primes
        # if lastadded == 1, remove last element and start adding them from tk1
        # this way we don't need an "if" within the last while
        if lastadded == 1:
            p.pop()
        # now complete for every other possible prime
        while pos < len(tk1):
            cpos = const * pos
            if tk1[pos]: p.append(cpos + 1)
            if tk7[pos]: p.append(cpos + 7)
            if tk11[pos]: p.append(cpos + 11)
            if tk13[pos]: p.append(cpos + 13)
            if tk17[pos]: p.append(cpos + 17)
            if tk19[pos]: p.append(cpos + 19)
            if tk23[pos]: p.append(cpos + 23)
            if tk29[pos]: p.append(cpos + 29)
            pos += 1
        # remove exceeding if present
        pos = len(p) - 1
        while p[pos] > N:
            pos -= 1
        if pos < len(p) - 1:
            del p[pos+1:]
        # return p list
        return p
    
    def sieveOfEratosthenes(n):
        """sieveOfEratosthenes(n): return the list of the primes < n."""
        # Code from: <dickinsm@gmail.com>, Nov 30 2006
        # http://groups.google.com/group/comp.lang.python/msg/f1f10ced88c68c2d
        if n <= 2:
            return []
        sieve = range(3, n, 2)
        top = len(sieve)
        for si in sieve:
            if si:
                bottom = (si*si - 3) // 2
                if bottom >= top:
                    break
                sieve[bottom::si] = [0] * -((bottom - top) // si)
        return [2] + [el for el in sieve if el]
    
    def sieveOfAtkin(end):
        """sieveOfAtkin(end): return a list of all the prime numbers <end
        using the Sieve of Atkin."""
        # Code by Steve Krenzel, <Sgk284@gmail.com>, improved
        # Code: https://web.archive.org/web/20080324064651/http://krenzel.info/?p=83
        # Info: http://en.wikipedia.org/wiki/Sieve_of_Atkin
        assert end > 0
        lng = ((end-1) // 2)
        sieve = [False] * (lng + 1)
    
        x_max, x2, xd = int(sqrt((end-1)/4.0)), 0, 4
        for xd in xrange(4, 8*x_max + 2, 8):
            x2 += xd
            y_max = int(sqrt(end-x2))
            n, n_diff = x2 + y_max*y_max, (y_max << 1) - 1
            if not (n & 1):
                n -= n_diff
                n_diff -= 2
            for d in xrange((n_diff - 1) << 1, -1, -8):
                m = n % 12
                if m == 1 or m == 5:
                    m = n >> 1
                    sieve[m] = not sieve[m]
                n -= d
    
        x_max, x2, xd = int(sqrt((end-1) / 3.0)), 0, 3
        for xd in xrange(3, 6 * x_max + 2, 6):
            x2 += xd
            y_max = int(sqrt(end-x2))
            n, n_diff = x2 + y_max*y_max, (y_max << 1) - 1
            if not(n & 1):
                n -= n_diff
                n_diff -= 2
            for d in xrange((n_diff - 1) << 1, -1, -8):
                if n % 12 == 7:
                    m = n >> 1
                    sieve[m] = not sieve[m]
                n -= d
    
        x_max, y_min, x2, xd = int((2 + sqrt(4-8*(1-end)))/4), -1, 0, 3
        for x in xrange(1, x_max + 1):
            x2 += xd
            xd += 6
            if x2 >= end: y_min = (((int(ceil(sqrt(x2 - end))) - 1) << 1) - 2) << 1
            n, n_diff = ((x*x + x) << 1) - 1, (((x-1) << 1) - 2) << 1
            for d in xrange(n_diff, y_min, -8):
                if n % 12 == 11:
                    m = n >> 1
                    sieve[m] = not sieve[m]
                n += d
    
        primes = [2, 3]
        if end <= 3:
            return primes[:max(0,end-2)]
    
        for n in xrange(5 >> 1, (int(sqrt(end))+1) >> 1):
            if sieve[n]:
                primes.append((n << 1) + 1)
                aux = (n << 1) + 1
                aux *= aux
                for k in xrange(aux, end, 2 * aux):
                    sieve[k >> 1] = False
    
        s  = int(sqrt(end)) + 1
        if s  % 2 == 0:
            s += 1
        primes.extend([i for i in xrange(s, end, 2) if sieve[i >> 1]])
    
        return primes
    
    def ambi_sieve_plain(n):
        s = range(3, n, 2)
        for m in xrange(3, int(n**0.5)+1, 2): 
            if s[(m-3)/2]: 
                for t in xrange((m*m-3)/2,(n>>1)-1,m):
                    s[t]=0
        return [2]+[t for t in s if t>0]
    
    def sundaram3(max_n):
        # https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/2073279#2073279
        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)
    
    ################################################################################
    # Using Numpy:
    def ambi_sieve(n):
        # http://tommih.blogspot.com/2009/04/fast-prime-number-generator.html
        s = np.arange(3, n, 2)
        for m in xrange(3, int(n ** 0.5)+1, 2): 
            if s[(m-3)/2]: 
                s[(m*m-3)/2::m]=0
        return np.r_[2, s[s>0]]
    
    def primesfrom3to(n):
        # https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188
        """ Returns a array of primes, p < n """
        assert n>=2
        sieve = np.ones(n/2, dtype=np.bool)
        for i in xrange(3,int(n**0.5)+1,2):
            if sieve[i/2]:
                sieve[i*i/2::i] = False
        return np.r_[2, 2*np.nonzero(sieve)[0][1::]+1]    
    
    def primesfrom2to(n):
        # https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188
        """ Input n>=6, Returns a array of primes, 2 <= p < n """
        sieve = np.ones(n/3 + (n%6==2), dtype=np.bool)
        sieve[0] = False
        for i in xrange(int(n**0.5)/3+1):
            if sieve[i]:
                k=3*i+1|1
                sieve[      ((k*k)/3)      ::2*k] = False
                sieve[(k*k+4*k-2*k*(i&1))/3::2*k] = False
        return np.r_[2,3,((3*np.nonzero(sieve)[0]+1)|1)]
    
    if __name__=='__main__':
        import itertools
        import sys
    
        def test(f1,f2,num):
            print('Testing {f1} and {f2} return same results'.format(
                f1=f1.func_name,
                f2=f2.func_name))
            if not all([a==b for a,b in itertools.izip_longest(f1(num),f2(num))]):
                sys.exit("Error: %s(%s) != %s(%s)"%(f1.func_name,num,f2.func_name,num))
    
        n=1000000
        test(sieveOfAtkin,sieveOfEratosthenes,n)
        test(sieveOfAtkin,ambi_sieve,n)
        test(sieveOfAtkin,ambi_sieve_plain,n) 
        test(sieveOfAtkin,sundaram3,n)
        test(sieveOfAtkin,sieve_wheel_30,n)
        test(sieveOfAtkin,primesfrom3to,n)
        test(sieveOfAtkin,primesfrom2to,n)
        test(sieveOfAtkin,rwh_primes,n)
        test(sieveOfAtkin,rwh_primes1,n)         
        test(sieveOfAtkin,rwh_primes2,n)
    

    运行脚本测试所有实现都给出相同的结果。

    【讨论】:

    • 如果你对非纯 Python 代码感兴趣,那么你应该查看gmpy——它对素数有很好的支持,通过它的next_prime 方法mpz类型。
    • 如果您使用的是 pypy,这些基准(psyco 的)似乎相当不错。令人惊讶的是,我发现 sieveOfEratosthenes 和 ambi_sieve_plain 是 pypy 中最快的。这是我为非 numpy 找到的 gist.github.com/5bf466bb1ee9e5726a52
    • 如果有人想知道这里的函数对于没有 psyco 和 pypy 的纯 python 与 PG7.8 of Wikibooks 相比如何:for n = 1000000: PG7.8: 4.93 s per loop ; rwh_primes1:每个循环 69 毫秒; rwh_primes2:每个循环 57.1 毫秒
    • 你能用 PyPy 更新这个吗,既然 psyco 已经死了,而 PyPy 已经取代了它?
    • 如果这些功能和时间可以为python3更新就太好了。
    【解决方案4】:

    对于最快的代码,numpy 解决方案是最好的。不过,出于纯粹的学术原因,我发布了我的纯 python 版本,它比上面发布的食谱版本快不到 50%。由于我在内存中创建了整个列表,因此您需要足够的空间来容纳所有内容,但它似乎可以很好地扩展。

    def daniel_sieve_2(maxNumber):
        """
        Given a number, returns all numbers less than or equal to
        that number which are prime.
        """
        allNumbers = range(3, maxNumber+1, 2)
        for mIndex, number in enumerate(xrange(3, maxNumber+1, 2)):
            if allNumbers[mIndex] == 0:
                continue
            # now set all multiples to 0
            for index in xrange(mIndex+number, (maxNumber-3)/2+1, number):
                allNumbers[index] = 0
        return [2] + filter(lambda n: n!=0, allNumbers)
    

    结果:

    >>>mine = timeit.Timer("daniel_sieve_2(1000000)",
    ...                    "from sieves import daniel_sieve_2")
    >>>prev = timeit.Timer("get_primes_erat(1000000)",
    ...                    "from sieves import get_primes_erat")
    >>>print "Mine: {0:0.4f} ms".format(min(mine.repeat(3, 1))*1000)
    Mine: 428.9446 ms
    >>>print "Previous Best {0:0.4f} ms".format(min(prev.repeat(3, 1))*1000)
    Previous Best 621.3581 ms
    

    【讨论】:

      【解决方案5】:

      对于真正具有足够大 N 的最快解决方案是下载 pre-calculated list of primes,将其存储为元组并执行以下操作:

      for pos,i in enumerate(primes):
          if i > N:
              print primes[:pos]
      

      如果N &gt; primes[-1]然后计算更多的素数并将新列表保存在你的代码中,所以下次它同样快。

      总是跳出框框思考。

      【讨论】:

      • 公平地说,您必须计算下载、解压缩和格式化素数的时间,并将其与使用算法生成素数的时间进行比较 - 这些算法中的任何一种都可以轻松将结果写入文件以供以后使用。我认为在这种情况下,如果有足够的内存来实际计算所有小于 982,451,653 的素数,numpy 解决方案仍然会更快。
      • @Daniel 正确。但是,存储您拥有的东西并在需要时继续使用仍然存在......
      • @Daniel G 我认为下载时间无关紧要。不是真的关于生成数字,所以您需要考虑用于创建您正在下载的列表的算法。考虑到 O(n),任何时间复杂度都会忽略文件传输的一次。
      • UTM 素数页面的FAQ 建议计算小素数比从磁盘读取它们更快(问题是小素数是什么意思)。
      【解决方案6】:

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

      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)
      

      比较:

      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
      

      【讨论】:

      • 我通过在函数顶部添加“zero = 0”,然后将过滤器中的 lambda 替换为“zero.__sub__”,设法将您的函数加速了大约 20%。不是世界上最漂亮的代码,但速度更快:)
      • @truppo:感谢您的评论!我刚刚意识到传递 None 而不是原来的函数是可行的,它甚至比 zero.__sub__ 更快
      • 你知道如果你通过sundaram3(9)它会返回[2, 3, 5, 7, 9]吗?它似乎用许多——也许是所有——奇数(即使它们不是素数)来做到这一点
      • 它有一个问题:sundaram3(7071) 包含 7071 而它不是素数
      【解决方案7】:

      基于 N

      import sys
      
      def miller_rabin_pass(a, n):
          d = n - 1
          s = 0
          while d % 2 == 0:
              d >>= 1
              s += 1
      
          a_to_power = pow(a, d, n)
          if a_to_power == 1:
              return True
          for i in range(s-1):
              if a_to_power == n - 1:
                  return True
              a_to_power = (a_to_power * a_to_power) % n
          return a_to_power == n - 1
      
      
      def miller_rabin(n):
          if n <= 2:
              return n == 2
      
          if n < 2_047:
              return miller_rabin_pass(2, n)
      
          return all(miller_rabin_pass(a, n) for a in (31, 73))
      
      
      n = int(sys.argv[1])
      primes = [2]
      for p in range(3,n,2):
        if miller_rabin(p):
          primes.append(p)
      print len(primes)
      

      根据 Wikipedia (http://en.wikipedia.org/wiki/Miller–Rabin_primality_test) 上的文章,对于 a = 37 和 73,测试 N

      我改编了原始米勒拉宾测试的概率实现的源代码:https://www.literateprograms.org/miller-rabin_primality_test__python_.html

      【讨论】:

      • 感谢 Miller-Rabin 素性测试,但此代码实际上速度较慢,并且没有提供正确的结果。 37 是素数,没有通过测试。
      • 我猜 37 是一种特殊情况,我的错。不过,我对确定性版本充满希望:)
      • 拉宾米勒没有任何特殊情况。
      • 您误读了这篇文章。它是 31,而不是 37。这就是您的实施失败的原因。
      【解决方案8】:

      如果您可以控制 N,列出所有素数的最快方法是预先计算它们。严重地。预计算是一种被忽视的优化方式。

      【讨论】:

      【解决方案9】:

      这是我通常用来在 Python 中生成素数的代码:

      $ python -mtimeit -s'import sieve' 'sieve.sieve(1000000)' 
      10 loops, best of 3: 445 msec per loop
      $ cat sieve.py
      from math import sqrt
      
      def sieve(size):
       prime=[True]*size
       rng=xrange
       limit=int(sqrt(size))
      
       for i in rng(3,limit+1,+2):
        if prime[i]:
         prime[i*i::+i]=[False]*len(prime[i*i::+i])
      
       return [2]+[i for i in rng(3,size,+2) if prime[i]]
      
      if __name__=='__main__':
       print sieve(100)
      

      它无法与这里发布的更快的解决方案竞争,但至少它是纯 python。

      感谢您发布此问题。我今天真的学到了很多。

      【讨论】:

        【解决方案10】:

        我的猜测是,所有方法中最快的是硬编码代码中的素数。

        那么为什么不写一个慢速脚本来生成另一个源文件,其中包含所有数字硬连线,然后在运行实际程序时导入该源文件。

        当然,这只有在编译时知道 N 的上限时才有效,但(几乎)所有项目欧拉问题都是如此。

         

        PS: 我可能是错的,虽然如果用硬连线素数解析源代码比一开始计算它们要慢,但据我所知 Python 从编译的 @987654321 运行@ 文件,因此在这种情况下读取所有素数最多为 N 的二进制数组应该非常快。

        【讨论】:

          【解决方案11】:

          更快、更节省内存的纯 Python 代码:

          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]]
          

          或从半筛开始

          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 代码:

          import numpy
          def primesfrom3to(n):
              """ Returns a array of primes, 3 <= p < n """
              sieve = numpy.ones(n//2, dtype=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
          

          从三分之一筛子开始的更快变化:

          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=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 版本将是:

          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]]
          

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

          我个人认为 numpy(它被如此广泛地使用)不是 Python 标准库的一部分,而且 Python 开发人员似乎完全忽略了语法和速度的改进,这很遗憾。

          【讨论】:

          • Numpy 现在与 Python 3 兼容。它不在标准库中这一事实很好,这样他们就可以拥有自己的发布周期。
          • 仅将二进制值存储在数组中,我建议bitarray - 在这里使用(用于最简单的素筛;这里不是竞争者!)stackoverflow.com/questions/31120986/…
          • primesfrom2to()方法中进行转换时,除法是否应该在括号内?
          • 如需兼容python 3的纯python版本,请点击此链接:stackoverflow.com/a/33356284/2482582
          • 天哪,这个吸盘很快。
          【解决方案12】:

          很抱歉,erat2() 的算法存在严重缺陷。

          在搜索下一个合数时,我们只需要测试奇数。 q,p 都是奇数;那么 q+p 是偶数,不需要测试,但 q+2*p 总是奇数。这消除了 while 循环条件中的“if even”测试,并节省了大约 30% 的运行时间。

          当我们这样做时:不要使用优雅的 'D.pop(q,None)' 获取和删除方法,而是使用 'if q in D: p=D[q],del D[q]' 这是快两倍!至少在我的机器上(P3-1Ghz)。 所以我建议这个聪明算法的实现:

          def erat3( ):
              from itertools import islice, count
          
              # q is the running integer that's checked for primeness.
              # yield 2 and no other even number thereafter
              yield 2
              D = {}
              # no need to mark D[4] as we will test odd numbers only
              for q in islice(count(3),0,None,2):
                  if q in D:                  #  is composite
                      p = D[q]
                      del D[q]
                      # q is composite. p=D[q] is the first prime that
                      # divides it. Since we've reached q, we no longer
                      # need it in the map, but we'll mark the next
                      # multiple of its witnesses to prepare for larger
                      # numbers.
                      x = q + p+p        # next odd(!) multiple
                      while x in D:      # skip composites
                          x += p+p
                      D[x] = p
                  else:                  # is prime
                      # q is a new prime.
                      # Yield it and mark its first multiple that isn't
                      # already marked in previous iterations.
                      D[q*q] = q
                      yield q
          

          【讨论】:

          【解决方案13】:

          使用 Numpy 实现的半筛略有不同:

          http://rebrained.com/?p=458

          导入数学 导入 numpy def prime6(最多): 素数=numpy.arange(3,upto+1,2) isprime=numpy.ones((upto-1)/2,dtype=bool) 对于素数中的因子[:int(math.sqrt(upto))]: if isprime[(factor-2)/2]: isprime[(factor*3-2)/2:(upto-1)/2:factor]=0 返回 numpy.insert(primes[isprime],0,2)

          有人可以将此与其他时间进行比较吗?在我的机器上,它似乎与其他 Numpy 半筛相当。

          【讨论】:

          • upto=10**6: primesfrom2to() - 7 毫秒; prime6() - 12 毫秒 ideone.com/oDg2Y
          【解决方案14】:

          目前我尝试过的最快的方法是基于Python cookbook erat2 函数:

          import itertools as it
          def erat2a( ):
              D = {  }
              yield 2
              for q in it.islice(it.count(3), 0, None, 2):
                  p = D.pop(q, None)
                  if p is None:
                      D[q*q] = q
                      yield q
                  else:
                      x = q + 2*p
                      while x in D:
                          x += 2*p
                      D[x] = p
          

          有关加速的解释,请参阅this 答案。

          【讨论】:

            【解决方案15】:

            第一次使用python,所以我使用的一些方法可能看起来有点麻烦。我只是直接将我的 c++ 代码转换为 python,这就是我所拥有的(尽管在 python 中有点慢)

            #!/usr/bin/env python
            import time
            
            def GetPrimes(n):
            
                Sieve = [1 for x in xrange(n)]
            
                Done = False
                w = 3
            
                while not Done:
            
                    for q in xrange (3, n, 2):
                        Prod = w*q
                        if Prod < n:
                            Sieve[Prod] = 0
                        else:
                            break
            
                    if w > (n/2):
                        Done = True
                    w += 2
            
                return Sieve
            
            
            
            start = time.clock()
            
            d = 10000000
            Primes = GetPrimes(d)
            
            count = 1 #This is for 2
            
            for x in xrange (3, d, 2):
                if Primes[x]:
                    count+=1
            
            elapsed = (time.clock() - start)
            print "\nFound", count, "primes in", elapsed, "seconds!\n"
            

            pythonw Primes.py

            在 12.799119 秒内找到 664579 个素数!

            #!/usr/bin/env python
            import time
            
            def GetPrimes2(n):
            
                Sieve = [1 for x in xrange(n)]
            
                for q in xrange (3, n, 2):
                    k = q
                    for y in xrange(k*3, n, k*2):
                        Sieve[y] = 0
            
                return Sieve
            
            
            
            start = time.clock()
            
            d = 10000000
            Primes = GetPrimes2(d)
            
            count = 1 #This is for 2
            
            for x in xrange (3, d, 2):
                if Primes[x]:
                    count+=1
            
            elapsed = (time.clock() - start)
            print "\nFound", count, "primes in", elapsed, "seconds!\n"
            

            pythonw Primes2.py

            在 10.230172 秒内找到 664579 个素数!

            #!/usr/bin/env python
            import time
            
            def GetPrimes3(n):
            
                Sieve = [1 for x in xrange(n)]
            
                for q in xrange (3, n, 2):
                    k = q
                    for y in xrange(k*k, n, k << 1):
                        Sieve[y] = 0
            
                return Sieve
            
            
            
            start = time.clock()
            
            d = 10000000
            Primes = GetPrimes3(d)
            
            count = 1 #This is for 2
            
            for x in xrange (3, d, 2):
                if Primes[x]:
                    count+=1
            
            elapsed = (time.clock() - start)
            print "\nFound", count, "primes in", elapsed, "seconds!\n"
            

            python Primes2.py

            在 7.113776 秒内找到 664579 个素数!

            【讨论】:

              【解决方案16】:

              我可能会迟到,但必须为此添加我自己的代码。它使用大约 n/2 空间,因为我们不需要存储偶数,而且我还使用了 bitarray python 模块,进一步大大减少了内存消耗并能够计算所有素数高达 1,000,000,000

              from bitarray import bitarray
              def primes_to(n):
                  size = n//2
                  sieve = bitarray(size)
                  sieve.setall(1)
                  limit = int(n**0.5)
                  for i in range(1,limit):
                      if sieve[i]:
                          val = 2*i+1
                          sieve[(i+i*val)::val] = 0
                  return [2] + [2*i+1 for i, v in enumerate(sieve) if v and i > 0]
              
              python -m timeit -n10 -s "import euler" "euler.primes_to(1000000000)"
              10 loops, best of 3: 46.5 sec per loop
              

              这是在 64 位 2.4GHZ MAC OSX 10.8.3 上运行的

              【讨论】:

              • 发布一个未知机器的时间说明什么都没有。这里接受的答案是“没有 psyco,对于 n=1000000,rwh_primes2 是最快的”。因此,如果您在同一台机器上提供该代码和您的代码的时间安排,并且也提供 2、4、1000 万个,那么它会提供更多信息。跨度>
              • -1,此代码依赖于用 C 实现的 bitarray 的特殊功能,这就是代码速度快的原因,因为大部分工作都是在切片分配中的本机代码中完成的。 bitarray 包 BREAKS 是可变序列的正确切片(在一个范围内索引)的标准定义,因为它允许将单个布尔值 0/1 或 True/False 分配给切片的所有元素,而纯 Python 的标准行为似乎是不允许这样做,只允许赋值 0 在这种情况下,它被视为序列/数组中所有切片元素的 del。
              • cont'd:如果要比较调用非标准的原生代码,我们不妨写一个基于C代码的“fastprimes”序列生成器包,例如Kim Walisch's primesieve,并生成所有只需调用一次序列生成器,即可在几秒钟内完成 40 亿加 32 位数字范围内的素数。这也将几乎不使用内存,因为链接代码基于分段的 Eratosthenes 筛,因此仅使用几十千字节的 RAM,如果生成序列,则不需要列表存储。
              【解决方案17】:

              我知道比赛已经关闭了几年。 …

              尽管如此,这是我对纯 python 素数筛的建议,基于在向前处理筛子时使用适当的步骤省略 2、3 和 5 的倍数。尽管如此,对于 N

              10^6

              $ python -mtimeit -s"import primeSieveSpeedComp" "primeSieveSpeedComp.primeSieveSeq(1000000)" 10 个循环,最好的 3 个:每个循环 46.7 毫秒

              比较:$ python -mtimeit -s"import primeSieveSpeedComp" “primeSieveSpeedComp.rwh_primes1(1000000)” 10 个循环,最好的 3 个:43.2 每循环毫秒 比较: $ python -m timeit -s"import primeSieveSpeedComp" “primeSieveSpeedComp.rwh_primes2(1000000)” 10 个循环,最好的 3 个:34.5 每循环毫秒

              10^7

              $ python -mtimeit -s"import primeSieveSpeedComp" "primeSieveSpeedComp.primeSieveSeq(10000000)" 10 个循环,3 个循环中的最佳:每个循环 530 毫秒

              比较:$ python -mtimeit -s"import primeSieveSpeedComp" “primeSieveSpeedComp.rwh_primes1(10000000)” 10 个循环,最好的 3 个:494 每循环毫秒 比较: $ python -m timeit -s"import primeSieveSpeedComp" “primeSieveSpeedComp.rwh_primes2(10000000)” 10 个循环,最好的 3 个:375 每循环毫秒

              10^8

              $ python -mtimeit -s"import primeSieveSpeedComp" "primeSieveSpeedComp.primeSieveSeq(100000000)" 10 个循环,3 个循环中的最佳:每个循环 5.55 秒

              比较: $ python -mtimeit -s"import primeSieveSpeedComp" “primeSieveSpeedComp.rwh_primes1(100000000)” 10 个循环,最好的 3 个:5.33 每循环秒 比较: $ python -m timeit -s"import primeSieveSpeedComp" “primeSieveSpeedComp.rwh_primes2(100000000)” 10 个循环,3 个循环中最好的:3.95 每个循环的秒数

              10^9

              $ python -mtimeit -s"import primeSieveSpeedComp" "primeSieveSpeedComp.primeSieveSeq(1000000000)" 10 个循环,3 个循环中的最佳:61.2 秒/循环

              比较: $ python -mtimeit -n 3 -s"import primeSieveSpeedComp" "primeSieveSpeedComp.rwh_primes1(1000000000)" 3 个循环,3 个循环中最好的:97.8 每个循环的秒数

              比较: $ python -m timeit -s"import primeSieveSpeedComp" “primeSieveSpeedComp.rwh_primes2(1000000000)” 10 个循环,3 个中最好的: 每个循环 41.9 秒

              您可以将以下代码复制到 ubuntus primeSieveSpeedComp 以查看此测试。

              def primeSieveSeq(MAX_Int):
                  if MAX_Int > 5*10**8:
                      import ctypes
                      int16Array = ctypes.c_ushort * (MAX_Int >> 1)
                      sieve = int16Array()
                      #print 'uses ctypes "unsigned short int Array"'
                  else:
                      sieve = (MAX_Int >> 1) * [False]
                      #print 'uses python list() of long long int'
                  if MAX_Int < 10**8:
                      sieve[4::3] = [True]*((MAX_Int - 8)/6+1)
                      sieve[12::5] = [True]*((MAX_Int - 24)/10+1)
                  r = [2, 3, 5]
                  n = 0
                  for i in xrange(int(MAX_Int**0.5)/30+1):
                      n += 3
                      if not sieve[n]:
                          n2 = (n << 1) + 1
                          r.append(n2)
                          n2q = (n2**2) >> 1
                          sieve[n2q::n2] = [True]*(((MAX_Int >> 1) - n2q - 1) / n2 + 1)
                      n += 2
                      if not sieve[n]:
                          n2 = (n << 1) + 1
                          r.append(n2)
                          n2q = (n2**2) >> 1
                          sieve[n2q::n2] = [True]*(((MAX_Int >> 1) - n2q - 1) / n2 + 1)
                      n += 1
                      if not sieve[n]:
                          n2 = (n << 1) + 1
                          r.append(n2)
                          n2q = (n2**2) >> 1
                          sieve[n2q::n2] = [True]*(((MAX_Int >> 1) - n2q - 1) / n2 + 1)
                      n += 2
                      if not sieve[n]:
                          n2 = (n << 1) + 1
                          r.append(n2)
                          n2q = (n2**2) >> 1
                          sieve[n2q::n2] = [True]*(((MAX_Int >> 1) - n2q - 1) / n2 + 1)
                      n += 1
                      if not sieve[n]:
                          n2 = (n << 1) + 1
                          r.append(n2)
                          n2q = (n2**2) >> 1
                          sieve[n2q::n2] = [True]*(((MAX_Int >> 1) - n2q - 1) / n2 + 1)
                      n += 2
                      if not sieve[n]:
                          n2 = (n << 1) + 1
                          r.append(n2)
                          n2q = (n2**2) >> 1
                          sieve[n2q::n2] = [True]*(((MAX_Int >> 1) - n2q - 1) / n2 + 1)
                      n += 3
                      if not sieve[n]:
                          n2 = (n << 1) + 1
                          r.append(n2)
                          n2q = (n2**2) >> 1
                          sieve[n2q::n2] = [True]*(((MAX_Int >> 1) - n2q - 1) / n2 + 1)
                      n += 1
                      if not sieve[n]:
                          n2 = (n << 1) + 1
                          r.append(n2)
                          n2q = (n2**2) >> 1
                          sieve[n2q::n2] = [True]*(((MAX_Int >> 1) - n2q - 1) / n2 + 1)
                  if MAX_Int < 10**8:
                      return [2, 3, 5]+[(p << 1) + 1 for p in [n for n in xrange(3, MAX_Int >> 1) if not sieve[n]]]
                  n = n >> 1
                  try:
                      for i in xrange((MAX_Int-2*n)/30 + 1):
                          n += 3
                          if not sieve[n]:
                              r.append((n << 1) + 1)
                          n += 2
                          if not sieve[n]:
                              r.append((n << 1) + 1)
                          n += 1
                          if not sieve[n]:
                              r.append((n << 1) + 1)
                          n += 2
                          if not sieve[n]:
                              r.append((n << 1) + 1)
                          n += 1
                          if not sieve[n]:
                              r.append((n << 1) + 1)
                          n += 2
                          if not sieve[n]:
                              r.append((n << 1) + 1)
                          n += 3
                          if not sieve[n]:
                              r.append((n << 1) + 1)
                          n += 1
                          if not sieve[n]:
                              r.append((n << 1) + 1)
                  except:
                      pass
                  return r
              

              【讨论】:

              • 可视化您的测试结果,以对数比例绘制它们,以查看并比较empirical orders of growth
              • @将感谢您的输入,下次我需要这样的比较时我会记住这一点
              【解决方案18】:

              这是一种使用存储列表查找素数的优雅且简单的解决方案。从 4 个变量开始,您只需要测试除数的奇数素数,并且您只需测试要测试的数的一半作为素数(测试 9、11、13 是否可以分为 17 没有意义) .它将先前存储的素数测试为除数。`

                  # Program to calculate Primes
               primes = [1,3,5,7]
              for n in range(9,100000,2):
                  for x in range(1,(len(primes)/2)):
                      if n % primes[x] == 0:
                          break
                  else:
                      primes.append(n)
              print primes
              

              【讨论】:

                【解决方案19】:

                随着时间的推移,我收集了几个素数筛子。我电脑上最快的是这个:

                from time import time
                # 175 ms for all the primes up to the value 10**6
                def primes_sieve(limit):
                    a = [True] * limit
                    a[0] = a[1] = False
                    #a[2] = True
                    for n in xrange(4, limit, 2):
                        a[n] = False
                    root_limit = int(limit**.5)+1
                    for i in xrange(3,root_limit):
                        if a[i]:
                            for n in xrange(i*i, limit, 2*i):
                                a[n] = False
                    return a
                
                LIMIT = 10**6
                s=time()
                primes = primes_sieve(LIMIT)
                print time()-s
                

                【讨论】:

                  【解决方案20】:

                  这是您可以与他人比较的方式。

                  # You have to list primes upto n
                  nums = xrange(2, n)
                  for i in range(2, 10):
                      nums = filter(lambda s: s==i or s%i, nums)
                  print nums
                  

                  这么简单……

                  【讨论】:

                    【解决方案21】:

                    我回答这个问题的速度很慢,但这似乎是一个有趣的练习。我正在使用可能作弊的 numpy,我怀疑这种方法是最快的,但应该很清楚。它筛选一个仅引用其索引的布尔数组,并从所有 True 值的索引中引出素数。不需要模数。

                    import numpy as np
                    def ajs_primes3a(upto):
                        mat = np.ones((upto), dtype=bool)
                        mat[0] = False
                        mat[1] = False
                        mat[4::2] = False
                        for idx in range(3, int(upto ** 0.5)+1, 2):
                            mat[idx*2::idx] = False
                        return np.where(mat == True)[0]
                    

                    【讨论】:

                    • 不正确,例如ajs_primes3a(10) -> array([2, 3, 5, 7, 9])9 不是素数
                    • 你发现了一个我没有发现的边缘案例——干得好!问题出在'for idx in range(3, int(upto ** 0.5), 2):' 应该是'for idx in range(3, int(upto ** 0.5) + 1, 2):'。谢谢,但它现在可以工作了。
                    • 原因是 idx 循环上升到 'upto ** 05' 这对于 15 和包括 15 的情况。从 16 开始,它工作正常。这是一组我没有测试过的边缘案例。添加 1 意味着它应该适用于所有数字。
                    • 现在好像可以了。它是返回数组的基于numpy 的解决方案中最慢的。注意:没有真正的埃拉托色尼筛子实现使用模——无需提及。您可以使用mat[idx*idx::idx] 而不是mat[idx*2::idx]。还有np.nonzero(mat)[0] 而不是np.where(mat == True)[0]
                    • 谢谢 JF。我对 prime6() 进行了测试,并在 prime6() 接管时得到了更快的结果(IIRC)约 250k。 primesfrom2to() 更快。在高达 20m 处,ajs_primes3a() 花费了 0.034744977951ms,primes6() 花费了 0.0222899913788ms,primesfrom2to() 花费了 0.0104751586914ms(相同的机器,相同的负载,最好的 10 次计时)。老实说,它比我想象的要好!
                    【解决方案22】:

                    如果您不想重新发明轮子,可以安装符号数学库sympy(是的,它与 Python 3 兼容)

                    pip install sympy
                    

                    并使用primerange 函数

                    from sympy import sieve
                    primes = list(sieve.primerange(1, 10**6))
                    

                    【讨论】:

                    • 我注意到这会打印整个列表,而从 community wiki 回答 primesfrom2to(10000) 返回 [ 2 3 5 ... 9949 9967 9973]。这是缩短 NumPy nd.array 的事情吗?
                    【解决方案23】:

                    编写自己的素数查找代码很有指导意义,但手头有一个快速可靠的库也很有用。我在C++ library primesieve 周围写了一个包装器,将其命名为primesieve-python

                    试试pip install primesieve

                    import primesieve
                    primes = primesieve.generate_primes(10**8)
                    

                    我很想看看比较的速度。

                    【讨论】:

                    • 这不是 OP 所要求的,但我不明白为什么会投反对票。与其他一些外部模块不同,这是一个 2.8 秒的解决方案。我在源代码中注意到它是线程化的,是否对它的扩展性进行了测试?
                    • @ljetibo 欢呼。瓶颈似乎是将 C++ 向量复制到 Python 列表,因此 count_primes 函数比 generate_primes 快得多
                    • 在我的计算机上,它可以轻松地生成最多 1e8 的素数(它会为 1e9 提供 MemoryError),并且可以计算最多 1e10 的素数。上面的@HappyLeapSecond 比较了 1e6 的算法
                    【解决方案24】:

                    如果您接受 itertools 但不接受 numpy,这里是针对 Python 3 的 rwh_primes2 改编版本,在我的机器上运行速度大约是其两倍。唯一的实质性变化是使用 bytearray 而不是布尔值列表,并使用 compress 而不是列表推导来构建最终列表。 (如果可以的话,我会把它添加为 moarningsun 之类的评论。)

                    import itertools
                    izip = itertools.zip_longest
                    chain = itertools.chain.from_iterable
                    compress = itertools.compress
                    def rwh_primes2_python3(n):
                        """ Input n>=6, Returns a list of primes, 2 <= p < n """
                        zero = bytearray([False])
                        size = n//3 + (n % 6 == 2)
                        sieve = bytearray([True]) * size
                        sieve[0] = False
                        for i in range(int(n**0.5)//3+1):
                          if sieve[i]:
                            k=3*i+1|1
                            start = (k*k+4*k-2*k*(i&1))//3
                            sieve[(k*k)//3::2*k]=zero*((size - (k*k)//3 - 1) // (2 * k) + 1)
                            sieve[  start ::2*k]=zero*((size -   start  - 1) // (2 * k) + 1)
                        ans = [2,3]
                        poss = chain(izip(*[range(i, n, 6) for i in (1,5)]))
                        ans.extend(compress(poss, sieve))
                        return ans
                    

                    比较:

                    >>> timeit.timeit('primes.rwh_primes2(10**6)', setup='import primes', number=1)
                    0.0652179726976101
                    >>> timeit.timeit('primes.rwh_primes2_python3(10**6)', setup='import primes', number=1)
                    0.03267321276325674
                    

                    >>> timeit.timeit('primes.rwh_primes2(10**8)', setup='import primes', number=1)
                    6.394284538007014
                    >>> timeit.timeit('primes.rwh_primes2_python3(10**8)', setup='import primes', number=1)
                    3.833829450302801
                    

                    【讨论】:

                      【解决方案25】:

                      所有内容都经过编写和测试。所以没有必要重新发明轮子。

                      python -m timeit -r10 -s"from sympy import sieve" "primes = list(sieve.primerange(1, 10**6))"
                      

                      为我们提供了破纪录的 12.2 毫秒

                      10 loops, best of 10: 12.2 msec per loop
                      

                      如果这还不够快,你可以试试 PyPy:

                      pypy -m timeit -r10 -s"from sympy import sieve" "primes = list(sieve.primerange(1, 10**6))"
                      

                      导致:

                      10 loops, best of 10: 2.03 msec per loop
                      

                      247 票赞成的答案列出了 15.9 毫秒的最佳解决方案。 比较一下!!!

                      【讨论】:

                        【解决方案26】:

                        我测试了一些unutbu's functions,我用饥饿的数百万计算了它

                        获胜者是使用 numpy 库的函数,

                        注意:进行内存利用率测试也很有趣:)

                        示例代码

                        Complete code on my github repository

                        #!/usr/bin/env python
                        
                        import lib
                        import timeit
                        import sys
                        import math
                        import datetime
                        
                        import prettyplotlib as ppl
                        import numpy as np
                        
                        import matplotlib.pyplot as plt
                        from prettyplotlib import brewer2mpl
                        
                        primenumbers_gen = [
                            'sieveOfEratosthenes',
                            'ambi_sieve',
                            'ambi_sieve_plain',
                            'sundaram3',
                            'sieve_wheel_30',
                            'primesfrom3to',
                            'primesfrom2to',
                            'rwh_primes',
                            'rwh_primes1',
                            'rwh_primes2',
                        ]
                        
                        def human_format(num):
                            # https://stackoverflow.com/questions/579310/formatting-long-numbers-as-strings-in-python?answertab=active#tab-top
                            magnitude = 0
                            while abs(num) >= 1000:
                                magnitude += 1
                                num /= 1000.0
                            # add more suffixes if you need them
                            return '%.2f%s' % (num, ['', 'K', 'M', 'G', 'T', 'P'][magnitude])
                        
                        
                        if __name__=='__main__':
                        
                            # Vars
                            n = 10000000 # number itereration generator
                            nbcol = 5 # For decompose prime number generator
                            nb_benchloop = 3 # Eliminate false positive value during the test (bench average time)
                            datetimeformat = '%Y-%m-%d %H:%M:%S.%f'
                            config = 'from __main__ import n; import lib'
                            primenumbers_gen = {
                                'sieveOfEratosthenes': {'color': 'b'},
                                'ambi_sieve': {'color': 'b'},
                                'ambi_sieve_plain': {'color': 'b'},
                                 'sundaram3': {'color': 'b'},
                                'sieve_wheel_30': {'color': 'b'},
                        # # #        'primesfrom2to': {'color': 'b'},
                                'primesfrom3to': {'color': 'b'},
                                # 'rwh_primes': {'color': 'b'},
                                # 'rwh_primes1': {'color': 'b'},
                                'rwh_primes2': {'color': 'b'},
                            }
                        
                        
                            # Get n in command line
                            if len(sys.argv)>1:
                                n = int(sys.argv[1])
                        
                            step = int(math.ceil(n / float(nbcol)))
                            nbs = np.array([i * step for i in range(1, int(nbcol) + 1)])
                            set2 = brewer2mpl.get_map('Paired', 'qualitative', 12).mpl_colors
                        
                            print datetime.datetime.now().strftime(datetimeformat)
                            print("Compute prime number to %(n)s" % locals())
                            print("")
                        
                            results = dict()
                            for pgen in primenumbers_gen:
                                results[pgen] = dict()
                                benchtimes = list()
                                for n in nbs:
                                    t = timeit.Timer("lib.%(pgen)s(n)" % locals(), setup=config)
                                    execute_times = t.repeat(repeat=nb_benchloop,number=1)
                                    benchtime = np.mean(execute_times)
                                    benchtimes.append(benchtime)
                                results[pgen] = {'benchtimes':np.array(benchtimes)}
                        
                        fig, ax = plt.subplots(1)
                        plt.ylabel('Computation time (in second)')
                        plt.xlabel('Numbers computed')
                        i = 0
                        for pgen in primenumbers_gen:
                        
                            bench = results[pgen]['benchtimes']
                            avgs = np.divide(bench,nbs)
                            avg = np.average(bench, weights=nbs)
                        
                            # Compute linear regression
                            A = np.vstack([nbs, np.ones(len(nbs))]).T
                            a, b = np.linalg.lstsq(A, nbs*avgs)[0]
                        
                            # Plot
                            i += 1
                            #label="%(pgen)s" % locals()
                            #ppl.plot(nbs, nbs*avgs, label=label, lw=1, linestyle='--', color=set2[i % 12])
                            label="%(pgen)s avg" % locals()
                            ppl.plot(nbs, a * nbs + b, label=label, lw=2, color=set2[i % 12])
                        print datetime.datetime.now().strftime(datetimeformat)
                        
                        ppl.legend(ax, loc='upper left', ncol=4)
                        
                        # Change x axis label
                        ax.get_xaxis().get_major_formatter().set_scientific(False)
                        fig.canvas.draw()
                        labels = [human_format(int(item.get_text())) for item in ax.get_xticklabels()]
                        
                        ax.set_xticklabels(labels)
                        ax = plt.gca()
                        
                        plt.show()
                        

                        【讨论】:

                        【解决方案27】:

                        对于 Python 3

                        def rwh_primes2(n):
                            correction = (n%6>1)
                            n = {0:n,1:n-1,2:n+4,3:n+3,4:n+2,5:n+1}[n%6]
                            sieve = [True] * (n//3)
                            sieve[0] = False
                            for i in range(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+4*k-2*k*(i&1))//3::2*k]=[False]*((n//6-(k*k+4*k-2*k*(i&1))//6-1)//k+1)
                            return [2,3] + [3*i+1|1 for i in range(1,n//3-correction) if sieve[i]]
                        

                        【讨论】:

                          【解决方案28】:

                          这是最快的函数之一的两个更新(纯 Python 3.6)版本,

                          from itertools import compress
                          
                          def rwh_primes1v1(n):
                              """ Returns  a list of primes < n for n > 2 """
                              sieve = bytearray([True]) * (n//2)
                              for i in range(3,int(n**0.5)+1,2):
                                  if sieve[i//2]:
                                      sieve[i*i//2::i] = bytearray((n-i*i-1)//(2*i)+1)
                              return [2,*compress(range(3,n,2), sieve[1:])]
                          
                          def rwh_primes1v2(n):
                              """ Returns a list of primes < n for n > 2 """
                              sieve = bytearray([True]) * (n//2+1)
                              for i in range(1,int(n**0.5)//2+1):
                                  if sieve[i]:
                                      sieve[2*i*(i+1)::2*i+1] = bytearray((n//2-2*i*(i+1))//(2*i+1)+1)
                              return [2,*compress(range(3,n,2), sieve[1:])]
                          

                          【讨论】:

                          【解决方案29】:

                          这是一种使用 python 的列表推导生成素数的有趣技术(但不是最有效的):

                          noprimes = [j for i in range(2, 8) for j in range(i*2, 50, i)]
                          primes = [x for x in range(2, 50) if x not in noprimes]
                          

                          【讨论】:

                          • 示例链接已失效。
                          • 谢谢,我已经删除了链接。
                          【解决方案30】:

                          这是一个 numpy 版本的 Sieve of Eratosthenes,具有良好的复杂性(低于对长度为 n 的数组进行排序)和矢量化。与@unutbu 相比,这与具有 46 微秒的软件包一样快,可以找到所有低于一百万的素数。

                          import numpy as np 
                          def generate_primes(n):
                              is_prime = np.ones(n+1,dtype=bool)
                              is_prime[0:2] = False
                              for i in range(int(n**0.5)+1):
                                  if is_prime[i]:
                                      is_prime[i**2::i]=False
                              return np.where(is_prime)[0]
                          

                          时间:

                          import time    
                          for i in range(2,10):
                              timer =time.time()
                              generate_primes(10**i)
                              print('n = 10^',i,' time =', round(time.time()-timer,6))
                          
                          >> n = 10^ 2  time = 5.6e-05
                          >> n = 10^ 3  time = 6.4e-05
                          >> n = 10^ 4  time = 0.000114
                          >> n = 10^ 5  time = 0.000593
                          >> n = 10^ 6  time = 0.00467
                          >> n = 10^ 7  time = 0.177758
                          >> n = 10^ 8  time = 1.701312
                          >> n = 10^ 9  time = 19.322478
                          

                          【讨论】:

                            猜你喜欢
                            • 2015-04-14
                            • 2013-09-26
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多