Cách nhanh nhất để liệt kê tất cả các số nguyên tố dưới N


357

Đây là thuật toán tốt nhất tôi có thể đưa ra.

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

Nó có thể được thực hiện thậm chí nhanh hơn?

Mã này có một lỗ hổng: Vì numberslà một tập hợp không có thứ tự, không có gì đảm bảo numbers.pop()sẽ loại bỏ số thấp nhất khỏi bộ. Tuy nhiên, nó hoạt động (ít nhất là đối với tôi) đối với một số số đầu vào:

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

Mã sniplet trong câu hỏi nhanh hơn nhiều nếu các số được khai báo như số = set (phạm vi (n, 2, -2)). Nhưng không thể đánh bại sundaram3. Cảm ơn câu hỏi.
Shekhar

3
Sẽ thật tuyệt nếu có thể có 3 phiên bản hàm Python trong các câu trả lời.
Michael Foukarakis

Chắc chắn có một thư viện để làm điều này vì vậy chúng ta không phải tự tung ra> xkcd hứa với Python cũng đơn giản như vậy import antigravity. Không có gì giống như require 'prime'; Prime.take(10)(Ruby)?
Đại tá Panic

2
@ColonelPanic Vì vậy, tôi đã cập nhật github.com/jaredks/pyprimesieve cho Py3 và thêm vào PyPi. Nó chắc chắn nhanh hơn những thứ này nhưng không phải là thứ tự cường độ - nhanh hơn ~ 5x so với các phiên bản numpy tốt nhất.
Jared

3
@ColonelPanic: Tôi nghĩ rằng việc chỉnh sửa các câu trả lời cũ để lưu ý rằng chúng đã có tuổi là phù hợp, vì điều đó làm cho nó trở thành một tài nguyên hữu ích hơn. Nếu câu trả lời "được chấp nhận" không còn là câu trả lời hay nhất, có thể chỉnh sửa ghi chú vào câu hỏi bằng bản cập nhật năm 2015 để hướng mọi người theo phương pháp tốt nhất hiện tại.
Peter Cordes

Câu trả lời:


366

Cảnh báo: timeit kết quả có thể thay đổi do sự khác biệt về phần cứng hoặc phiên bản của Python.

Dưới đây là một kịch bản so sánh một số triển khai:

Rất cám ơn stephan vì đã chú ý đến sieve_wheel_30. Tín dụng dành cho Robert William Hanks cho primesfrom2to, primesfrom3to, rwh_primes, rwh_primes1 và rwh_primes2.

Trong số các phương thức Python đơn giản được thử nghiệm, với psyco , với n = 1000000, rwh_primes1 là thử nghiệm nhanh nhất.

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

Trong số các phương thức Python đơn giản được thử nghiệm, không có psyco , với n = 1000000, rwh_primes2 là nhanh nhất.

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

Trong tất cả các phương pháp được thử nghiệm, cho phép numpy , với n = 1000000, primesfrom2to là thử nghiệm nhanh nhất.

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

Thời gian được đo bằng lệnh:

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

với {method}thay thế bởi mỗi tên phương thức.

primes.py:

#!/usr/bin/env python
import psyco; psyco.full()
from math import sqrt, ceil
import numpy as np

def rwh_primes(n):
    # /programming/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):
    # /programming/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):
    # /programming/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):
    # /programming/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):
    # /programming/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):
    # /programming/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)

Chạy các bài kiểm tra kịch bản mà tất cả các triển khai đều cho kết quả như nhau.


4
Nếu bạn quan tâm đến mã Python không thuần túy, thì bạn nên kiểm tra gmpy- nó có hỗ trợ khá tốt cho các số nguyên tố, thông qua next_primephương thức mpzloại.
Alex Martelli

1
Nếu bạn đang sử dụng pypy, các điểm chuẩn này (các psyco) có vẻ khá tắt. Thật đáng ngạc nhiên, tôi thấy sieveOfEratosthenes và ambi_sieve_plain là nhanh nhất với pypy. Đây là những gì tôi tìm thấy cho những người không numpy gist.github.com/5bf466bb1ee9e5726a52
Ehsan Kia

1
Nếu ai đó tự hỏi làm thế nào các chức năng ở đây chống lại PG7.8 của Wikibooks cho trăn thuần không có psyco cũng không pypy: for n = 1000000: PG7.8: 4.93 s trên mỗi vòng lặp; rwh_primes1: 69 ms mỗi vòng lặp; rwh_primes2: 57,1 ms mỗi vòng lặp
gabious

8
Bạn có thể cập nhật điều này với PyPy không, bây giờ psyco đã chết và PyPy đã thay thế nó?
noɥʇʎԀʎzɐɹƆ

3
Sẽ thật tuyệt nếu các chức năng và thời gian này có thể được cập nhật cho python3.
cs95

135

Mã Python thuần nhanh hơn và nhiều bộ nhớ hơn:

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

hoặc bắt đầu với một nửa rây

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

Mã numpy nhanh hơn và nhiều bộ nhớ hơn:

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

một biến thể nhanh hơn bắt đầu bằng một phần ba sàng:

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

Một phiên bản python thuần (khó mã) của đoạn mã trên sẽ là:

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

Thật không may, trăn thuần không chấp nhận cách thức chuyển nhượng đơn giản và nhanh hơn và gọi len()bên trong vòng lặp như trong [False]*len(sieve[((k*k)//3)::2*k])quá chậm. Vì vậy, tôi đã phải ứng biến để sửa lỗi đầu vào (& tránh thêm toán học) và làm một số phép thuật cực đoan (& đau đớn).

Cá nhân tôi nghĩ rằng thật đáng tiếc khi numpy (được sử dụng rộng rãi) không phải là một phần của thư viện chuẩn Python và những cải tiến về cú pháp và tốc độ dường như bị các nhà phát triển Python bỏ qua hoàn toàn.


2
Numpy hiện tương thích với Python 3. Thực tế là nó không có trong thư viện chuẩn là tốt, theo cách đó họ có thể có chu kỳ phát hành riêng.
Adam

chỉ cần lưu trữ các giá trị nhị phân trong một mảng tôi đề nghị bitarray- như được sử dụng ở đây (đối với rây thủ đơn giản nhất; không phải là một ứng cử viên trong cuộc đua ở đây!) stackoverflow.com/questions/31120986/...
hiro nhân vật chính

Khi truyền trong primesfrom2to()phương thức, phân chia có nên nằm trong dấu ngoặc không?
355

3
Để có phiên bản python thuần tương thích với python 3, hãy theo liên kết sau: stackoverflow.com/a/33356284/2482582
Moebius

1
Holy buttsnacks kẻ hút này là nhanh chóng.
Scott

42

Có một mẫu khá gọn gàng từ Python Cookbook ở đây - phiên bản nhanh nhất được đề xuất trên URL đó là:

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

vì vậy sẽ cho

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

Đo lường tại dấu nhắc shell (như tôi muốn làm) với mã này trong phần mềm quảng cáo, tôi quan sát:

$ 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

vì vậy có vẻ như giải pháp Cookbook nhanh hơn gấp đôi.


1
@jbochi, bạn được chào đón - nhưng hãy xem URL đó, bao gồm các khoản tín dụng: phải mất mười người trong chúng tôi để tinh chỉnh mã cho đến thời điểm này, bao gồm cả các bộ đèn hiệu suất Python như Tim Peters và Raymond Hettinger (Tôi đã viết văn bản cuối cùng của công thức kể từ khi tôi chỉnh sửa Cookbook in, nhưng về mặt mã hóa, đóng góp của tôi ngang bằng với những người khác) - cuối cùng, nó thực sự tinh tế và tinh chỉnh mã, và điều đó không đáng ngạc nhiên! -)
Alex Martelli

@Alex: Biết rằng mã của bạn "chỉ" nhanh gấp đôi mã của tôi, khiến tôi khá tự hào. :) URL cũng rất thú vị để đọc. Cảm ơn một lần nữa.
jbochi

Và nó có thể được thực hiện nhanh hơn nữa với một thay đổi nhỏ: xem stackoverflow.com/questions/2211990/iêu
tzot

1
... Và nó có thể được thực hiện nhanh hơn với tốc độ tăng thêm ~ 1,2x-1,3x, giảm đáng kể dấu chân bộ nhớ từ O (n) đến O (sqrt (n)) và cải thiện độ phức tạp thời gian theo kinh nghiệm, bằng cách hoãn thêm vào các số nguyên tố cho dict cho đến khi hình vuông của chúng được nhìn thấy trong đầu vào. Kiểm tra nó ở đây .
Will Ness

28

Sử dụng Sàng của Sundaram , tôi nghĩ rằng tôi đã phá vỡ kỷ lục của Python thuần túy:

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)

So sánh:

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

1
Tôi đã quản lý để tăng tốc chức năng của bạn khoảng 20% ​​bằng cách thêm "zero = 0" ở đầu chức năng và sau đó thay thế lambda trong bộ lọc của bạn bằng "zero .__ sub__". Không phải là mã đẹp nhất trên thế giới, nhưng nhanh hơn một chút :)
truppo

1
@truppo: Cảm ơn bình luận của bạn! Tôi mới nhận ra rằng việc chuyển qua Nonethay vì chức năng ban đầu hoạt động và nó thậm chí còn nhanh hơnzero.__sub__
jbochi

7
Bạn có biết rằng nếu bạn vượt qua sundaram3(9)nó sẽ trở lại [2, 3, 5, 7, 9]? Nó dường như làm điều này với rất nhiều - có lẽ là tất cả - các số lẻ (ngay cả khi chúng không phải là số nguyên tố)
viết vào

1
nó có một vấn đề: sundaram3 (7071) bao gồm 7071 trong khi nó không phải là chính
bigOther

18

Thuật toán nhanh, nhưng có một lỗ hổng nghiêm trọng:

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

Bạn cho rằng numbers.pop()sẽ trả về số nhỏ nhất trong tập hợp, nhưng điều này không được đảm bảo. Các tập hợp không có thứ tự và pop()loại bỏ và trả về một phần tử tùy ý , vì vậy nó không thể được sử dụng để chọn số nguyên tố tiếp theo từ các số còn lại.


17

Để có giải pháp thực sự nhanh nhất với N đủ lớn sẽ tải xuống một danh sách các số nguyên tố được tính toán trước , lưu trữ dưới dạng một tuple và làm một cái gì đó như:

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

Nếu N > primes[-1] chỉ sau đó tính toán nhiều số nguyên tố hơn và lưu danh sách mới trong mã của bạn, vì vậy lần sau nó cũng nhanh như nhau.

Luôn luôn nghĩ bên ngoài hộp.


9
Mặc dù vậy, để công bằng, bạn phải tính thời gian tải xuống, giải nén và định dạng các số nguyên tố và so sánh với thời gian để tạo các số nguyên tố bằng thuật toán - bất kỳ thuật toán nào trong số này cũng có thể dễ dàng ghi kết quả vào một tệp cho lần sau sử dụng. Tôi nghĩ rằng trong trường hợp đó, được cung cấp đủ bộ nhớ để thực sự tính toán tất cả các số nguyên tố nhỏ hơn 982.451.653, giải pháp numpy vẫn sẽ nhanh hơn.
Daniel G

3
@Daniel đúng. Tuy nhiên, cửa hàng những gì bạn có và tiếp tục bất cứ khi nào cần vẫn còn ...
Kimvais

@Daniel GI nghĩ rằng thời gian tải xuống là không liên quan. Không thực sự tạo ra các số, vì vậy bạn sẽ muốn tính đến thuật toán được sử dụng để tạo danh sách mà bạn đang tải xuống. Và bất kỳ sự phức tạp thời gian nào cũng sẽ bỏ qua lần chuyển tập tin được đưa ra là O (n).
Ross

Câu hỏi thường gặp cho trang chính UTM cho thấy việc tính các số nguyên tố nhỏ nhanh hơn so với việc đọc chúng ra khỏi đĩa (câu hỏi đặt ra là phương tiện nhỏ nào).
dơi

12

Nếu bạn không muốn phát minh lại bánh xe, bạn có thể cài đặt các biểu tượng toán học thư viện sympy (vâng đó là Python 3 tương thích)

pip install sympy

Và sử dụng chức năng primerange

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

8

Nếu bạn chấp nhận itertools nhưng không numpy, đây là bản chuyển thể của rwh_primes2 cho Python 3 chạy nhanh gấp đôi trên máy của tôi. Sự thay đổi đáng kể duy nhất là sử dụng một phụ bản thay vì một danh sách cho boolean và sử dụng nén thay vì hiểu danh sách để xây dựng danh sách cuối cùng. (Tôi sẽ thêm nó dưới dạng một nhận xét như moarningsun nếu tôi có thể.)

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

So sánh:

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

Thực hiện rất mát mẻ. :)
KR

7

Hướng dẫn viết mã tìm kiếm chính của bạn là rất hữu ích, nhưng cũng rất hữu ích khi có một thư viện đáng tin cậy nhanh chóng. Tôi đã viết một trình bao bọc xung quanh thư viện C ++ , đặt tên là primesieve-python

Thử nó pip install primesieve

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

Tôi tò mò muốn xem tốc độ so sánh.


Đó không phải là chính xác những gì OP đã ra lệnh nhưng tôi không biết lý do tại sao downvote. Đó là một giải pháp 2.8 giây không giống như một số mô-đun bên ngoài khác. Tôi đã nhận thấy trong nguồn mà nó đã xâu chuỗi, có bất kỳ thử nghiệm nào về mức độ quy mô của nó không?
ljetibo 14/07/2015

@ljetibo chúc mừng. Nút cổ chai dường như đang sao chép vector C ++ vào danh sách Python, do đó count_primeschức năng này nhanh hơn nhiều so vớigenerate_primes
Đại tá Panic

Trên máy tính của tôi, nó có thể thoải mái tạo ra các số nguyên tố lên tới 1e8 (nó cung cấp cho MemoryError cho 1e9) và đếm các số nguyên tố lên tới 1e10. @HappyLeapSecond trên so sánh các thuật toán cho 1e6
Đại tá Panic

7

Đây là hai phiên bản cập nhật (thuần Python 3.6) của một trong những chức năng nhanh nhất,

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

1
Trong Python 3 tôi đã sử dụng hàm này stackoverflow.com/a/3035188/7799269 nhưng được thay thế / bằng // và xrange với phạm vi và chúng có vẻ nhanh hơn nhiều so với điều này.
samerivertwice

4

Một triển khai xác định của thử nghiệm Tính nguyên thủy của Miller-Rabin với giả định rằng N <9.080,191

import sys
import random

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 xrange(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):
    for a in [2, 3, 37, 73]:
      if not miller_rabin_pass(a, n):
        return False
    return True


n = int(sys.argv[1])
primes = [2]
for p in range(3,n,2):
  if miller_rabin(p):
    primes.append(p)
print len(primes)

Theo bài viết trên Wikipedia ( http://en.wikipedia.org/wiki/Miller HayRabin_primality_test HayRabin_primality_test) thử nghiệm N <9.080,191 cho a = 2,3,37 và 73 là đủ để quyết định liệu N có phải là hỗn hợp hay không.

Và tôi đã điều chỉnh mã nguồn từ việc triển khai xác suất của thử nghiệm ban đầu của Miller-Rabin được tìm thấy ở đây: http://en.literateprograms.org/Miller-Rabin_primality_test_(Python)


1
Cảm ơn bạn đã kiểm tra tính nguyên thủy của Miller-Rabin, nhưng mã này thực sự chậm hơn và không cung cấp kết quả chính xác. 37 là số nguyên tố và không vượt qua bài kiểm tra.
jbochi

Tôi đoán 37 là một trong những trường hợp đặc biệt, xấu của tôi. Tôi đã hy vọng về phiên bản xác định mặc dù :)
Ruggiero Spearman

Không có trường hợp đặc biệt nào cho máy nghiền rabin.
Sai lầm

2
Bạn đọc sai bài báo. Đó là 31, không phải 37. Đây là lý do tại sao việc thực hiện của bạn thất bại.
Logan

4

Nếu bạn có quyền kiểm soát N, cách nhanh nhất để liệt kê tất cả các số nguyên tố là tính toán trước chúng. Nghiêm túc. Tính toán trước là một cách tối ưu hóa bỏ qua.


3
Hoặc tải chúng từ đây primes.utm.edu/lists/small/millions , nhưng ý tưởng là kiểm tra giới hạn của python và xem liệu mã đẹp có xuất hiện từ tối ưu hóa không.
jbochi

4

Đây là mã tôi thường sử dụng để tạo các số nguyên tố trong 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)

Nó không thể cạnh tranh với các giải pháp nhanh hơn được đăng ở đây, nhưng ít nhất nó là trăn thuần túy.

Cảm ơn đã gửi câu hỏi này. Tôi thực sự đã học được rất nhiều ngày hôm nay.


3

Đối với mã nhanh nhất, giải pháp numpy là tốt nhất. Tuy nhiên, vì lý do học thuật thuần túy, tôi đang đăng phiên bản trăn thuần túy của mình, nhanh hơn một chút so với phiên bản sách nấu ăn được đăng ở trên. Vì tôi tạo toàn bộ danh sách trong bộ nhớ, bạn cần có đủ không gian để chứa mọi thứ, nhưng dường như nó có quy mô khá tốt.

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)

Và kết quả:

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

3

Cách thực hiện hơi khác một nửa rây bằng Numpy:

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

nhập môn toán
nhập khẩu numpy
def Prime6 (tối đa):
    số nguyên tố = numpy.arange (3, tối đa + 1,2)
    isprime = numpy.ones ((tối đa-1) / 2, dtype = bool)
    cho yếu tố trong số nguyên tố [: int (math.sqrt (tối đa))]:
        if isprime [(yếu tố-2) / 2]: isprime [(yếu tố * 3-2) / 2: (tối đa-1) / 2: yếu tố] = 0
    return numpy.insert (số nguyên tố [isprime], 0,2)

Ai đó có thể so sánh điều này với thời gian khác? Trên máy của tôi, nó có vẻ khá tương đương với nửa rây Numpy khác.


upto=10**6: primesfrom2to()- 7 ms; prime6()- 12 ms ideone.com/oDg2Y
JFS

3

Tất cả đều được viết và thử nghiệm. Vì vậy, không cần phải phát minh lại bánh xe.

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

cung cấp cho chúng tôi phá kỷ lục 12,2 msec !

10 loops, best of 10: 12.2 msec per loop

Nếu điều này không đủ nhanh, bạn có thể thử PyPy:

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

kết quả là:

10 loops, best of 10: 2.03 msec per loop

Câu trả lời với 247 phiếu bầu lên danh sách 15,9 ms cho giải pháp tốt nhất. So sánh cái này !!!


3

Tôi đã thử nghiệm một số chức năng của unutbu , tôi đã tính toán nó với hàng triệu số bị treo

Những người chiến thắng là các chức năng sử dụng thư viện numpy,

Lưu ý : Sẽ rất thú vị khi thực hiện kiểm tra sử dụng bộ nhớ :)

Kết quả tính toán thời gian

Mã mẫu

Hoàn thành mã trên kho github của tôi

#!/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):
    # /programming/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()

2
để so sánh các hiệu suất thuật toán , tốt hơn là vẽ theo tỷ lệ log-log .
Will Ness

3

Đối với 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]]

3

Sàng nguyên tố nhanh nhất trong Python nguyên chất :

from itertools import compress

def half_sieve(n):
    """
    Returns a list of prime numbers less than `n`.
    """
    if n <= 2:
        return []
    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)
    primes = list(compress(range(1, n, 2), sieve))
    primes[0] = 2
    return primes

Tôi đã tối ưu hóa Sàng của Eratosthenes cho tốc độ và bộ nhớ.

Điểm chuẩn

from time import clock
import platform

def benchmark(iterations, limit):
    start = clock()
    for x in range(iterations):
        half_sieve(limit)
    end = clock() - start
    print(f'{end/iterations:.4f} seconds for primes < {limit}')

if __name__ == '__main__':
    print(platform.python_version())
    print(platform.platform())
    print(platform.processor())
    it = 10
    for pw in range(4, 9):
        benchmark(it, 10**pw)

Đầu ra

>>> 3.6.7
>>> Windows-10-10.0.17763-SP0
>>> Intel64 Family 6 Model 78 Stepping 3, GenuineIntel
>>> 0.0003 seconds for primes < 10000
>>> 0.0021 seconds for primes < 100000
>>> 0.0204 seconds for primes < 1000000
>>> 0.2389 seconds for primes < 10000000
>>> 2.6702 seconds for primes < 100000000

2

Lần đầu tiên sử dụng python, vì vậy một số phương pháp tôi sử dụng trong này có vẻ hơi cồng kềnh. Tôi chỉ cần chuyển đổi mã c ++ của mình thành python và đây là những gì tôi có (mặc dù một chút chậm chạp trong 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

Tìm thấy 664579 số nguyên tố trong 12.799119 giây!

#!/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

Tìm thấy 664579 số nguyên tố trong 10.249572 giây!

#!/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

Tìm thấy 664579 số nguyên tố trong 7,113776 giây!


2

Tôi biết cuộc thi đã kết thúc trong một số năm. Giáo dục

Tuy nhiên, đây là gợi ý của tôi cho một rây nguyên chất trăn nguyên chất, dựa trên việc bỏ qua bội số của 2, 3 và 5 bằng cách sử dụng các bước thích hợp trong khi xử lý rây về phía trước. Tuy nhiên, nó thực sự chậm hơn đối với N <10 ^ 9 so với @Robert William Hanks các giải pháp ưu việt rwh_primes2 và rwh_primes1. Bằng cách sử dụng một mảng rây ctypes.c_ushort trên 1,5 * 10 ^ 8, bằng cách nào đó nó thích nghi với giới hạn bộ nhớ.

10 ^ 6

$ python -mtimeit -s "nhập PrimeSieveSpeedComp" "PrimeSieveSpeedComp.primeSieveSeq (1000000)" 10 vòng, tốt nhất là 3: 46,7 msec mỗi vòng

để so sánh: $ python -mtimeit -s "nhập PrimeSieveSpeedComp" "PrimeSieveSpeedComp.rwh_primes1 (1000000)" 10 vòng lặp, tốt nhất là 3: 43,2 msec trên mỗi vòng lặp để so sánh: $ python -m timeit -s "nhập Prime" (1000000) "10 vòng, tốt nhất là 3: 34,5 msec mỗi vòng

10 ^ 7

$ python -mtimeit -s "nhập PrimeSieveSpeedComp" "PrimeSieveSpeedComp.primeSieveSeq (10000000)" 10 vòng, tốt nhất là 3: 530 msec mỗi vòng

để so sánh: $ python -mtimeit -s "import PrimeSieveSpeedComp" "PrimeSieveSpeedComp.rwh_primes1 (10000000)" 10 vòng lặp, tốt nhất là 3: 494 msec trên mỗi vòng lặp để so sánh: $ python -m timeit -s "nhập Prime" (10000000) "10 vòng, tốt nhất là 3: 375 msec mỗi vòng

10 ^ 8

$ python -mtimeit -s "nhập PrimeSieveSpeedComp" "PrimeSieveSpeedComp.primeSieveSeq (100000000)" 10 vòng, tốt nhất là 3: 5,55 giây mỗi vòng

để so sánh: $ python -mtimeit -s "import PrimeSieveSpeedComp" "PrimeSieveSpeedComp.rwh_primes1 (100000000)" 10 vòng, tốt nhất là 3: 5,33 giây trên mỗi vòng lặp để so sánh: $ python -m timeit -s " (100000000) "10 vòng, tốt nhất là 3: 3,95 giây mỗi vòng

10 ^ 9

$ python -mtimeit -s "nhập PrimeSieveSpeedComp" "PrimeSieveSpeedComp.primeSieveSeq (1000000000)" 10 vòng, tốt nhất là 3: 61,2 giây mỗi vòng

để so sánh: $ python -mtimeit -n 3 -s "nhập PrimeSieveSpeedComp" "PrimeSieveSpeedComp.rwh_primes1 (1000000000)" 3 vòng, tốt nhất là 3: 97.8 giây mỗi vòng

để so sánh: $ python -m timeit -s "import PrimeSieveSpeedComp" "PrimeSieveSpeedComp.rwh_primes2 (1000000000)" 10 vòng, tốt nhất là 3: 41,9 giây mỗi vòng

Bạn có thể sao chép mã dưới đây vào ubuntus PrimeSieveSpeedComp để xem xét các thử nghiệm này.

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

để trực quan hóa kết quả kiểm tra của bạn, vẽ biểu đồ theo thang đo log-log, để xem và so sánh các thứ tự tăng trưởng theo kinh nghiệm .
Will Ness

@ Sẽ cảm ơn về đầu vào, tôi sẽ ghi nhớ điều này vào lần tới khi tôi cần so sánh như vậy
ABri

1

Đây là một phiên bản gọn gàng của Sàng của Eratosthenes có cả độ phức tạp tốt (thấp hơn so với việc sắp xếp một mảng có độ dài n) và vector hóa. So với @unutbu lần này nhanh như các gói với 46 microsecons để tìm tất cả các số nguyên tố dưới một triệu.

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]

Thời gian:

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

1

Tôi đã cập nhật nhiều mã cho Python 3 và ném nó vào perfplot (một dự án của tôi) để xem cái nào thực sự nhanh nhất. Hóa ra, cho lớn n, primesfrom{2,3}tolấy bánh:

nhập mô tả hình ảnh ở đây


Mã để tái tạo cốt truyện:

import perfplot
from math import sqrt, ceil
import numpy as np
import sympy


def rwh_primes(n):
    # /programming/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 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 rwh_primes1(n):
    # /programming/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 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]]


def rwh_primes2(n):
    # /programming/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188
    """Input n>=6, Returns a list of primes, 2 <= p < n"""
    assert n >= 6
    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]]


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 range(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 sieve_of_eratosthenes(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 = list(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 sieve_of_atkin(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 range(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 range((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 range(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 range((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 range(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 range(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 range(5 >> 1, (int(sqrt(end)) + 1) >> 1):
        if sieve[n]:
            primes.append((n << 1) + 1)
            aux = (n << 1) + 1
            aux *= aux
            for k in range(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 range(s, end, 2) if sieve[i >> 1]])

    return primes


def ambi_sieve_plain(n):
    s = list(range(3, n, 2))
    for m in range(3, int(n ** 0.5) + 1, 2):
        if s[(m - 3) // 2]:
            for t in range((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):
    # /programming/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 range(3, max_n + 1, 2):
        for i in range(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 range(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):
    # /programming/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188
    """ Returns an array of primes, p < n """
    assert n >= 2
    sieve = np.ones(n // 2, dtype=np.bool)
    for i in range(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):
    # /programming/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188
    """ Input n>=6, Returns an array of primes, 2 <= p < n """
    assert n >= 6
    sieve = np.ones(n // 3 + (n % 6 == 2), dtype=np.bool)
    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
            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)]


def sympy_sieve(n):
    return list(sympy.sieve.primerange(1, n))


perfplot.save(
    "prime.png",
    setup=lambda n: n,
    kernels=[
        rwh_primes,
        rwh_primes1,
        rwh_primes2,
        sieve_wheel_30,
        sieve_of_eratosthenes,
        sieve_of_atkin,
        # ambi_sieve_plain,
        # sundaram3,
        ambi_sieve,
        primesfrom3to,
        primesfrom2to,
        sympy_sieve,
    ],
    n_range=[2 ** k for k in range(3, 25)],
    logx=True,
    logy=True,
    xlabel="n",
)

0

Tôi đoán là cách nhanh nhất trong tất cả các cách là mã cứng các số nguyên tố trong mã của bạn.

Vậy tại sao không chỉ viết một tập lệnh chậm tạo ra một tệp nguồn khác có tất cả các số được gắn vào nó, sau đó nhập tệp nguồn đó khi bạn chạy chương trình thực tế của mình.

Tất nhiên, điều này chỉ hoạt động nếu bạn biết giới hạn trên của N tại thời gian biên dịch, nhưng đó là trường hợp cho (gần như) tất cả các vấn đề Euler của dự án.

 

PS: Tôi có thể sai mặc dù việc phân tích nguồn bằng các số nguyên tố có dây chậm hơn so với tính toán chúng ở nơi đầu tiên, nhưng theo tôi biết Python chạy từ .pyccác tệp được biên dịch nên việc đọc một mảng nhị phân với tất cả các số nguyên tố lên đến N nên đẫm máu nhanh trong trường hợp đó.


0

Xin lỗi đã làm phiền nhưng erat2 () có một lỗ hổng nghiêm trọng trong thuật toán.

Trong khi tìm kiếm hỗn hợp tiếp theo, chúng ta chỉ cần kiểm tra các số lẻ. q, p đều là số lẻ; thì q + p là chẵn và không cần phải kiểm tra, nhưng q + 2 * p luôn là số lẻ. Điều này giúp loại bỏ kiểm tra "nếu chẵn" trong điều kiện vòng lặp while và tiết kiệm khoảng 30% thời gian chạy.

Trong khi chúng ta đang ở đó: thay vì phương thức get và xóa thanh lịch 'D.pop (q, Không)' sử dụng 'if q in D: p = D [q], del D [q]' nhanh gấp đôi ! Ít nhất là trên máy của tôi (P3-1Ghz). Vì vậy, tôi đề nghị thực hiện thuật toán thông minh này:

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

để thêm một số nguyên tố bị hoãn vào dict (cho đến khi hình vuông của một số nguyên tố được nhìn thấy trong đầu vào) xem stackoverflow.com/a/10733621/849891 .
Will Ness

0

Phương pháp nhanh nhất tôi đã thử cho đến nay dựa trên chức năng sách nấu ăn Pythonerat2 :

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

Xem câu trả lời này để được giải thích về việc tăng tốc.


0

Tôi có thể bị trễ bữa tiệc nhưng sẽ phải thêm mã của riêng mình cho việc này. Nó sử dụng khoảng n / 2 trong không gian vì chúng tôi không cần lưu trữ số chẵn và tôi cũng sử dụng mô-đun python bitarray, tiếp tục cắt giảm tiêu thụ bộ nhớ và cho phép tính toán tất cả các số nguyên tố lên tới 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

Điều này đã được chạy trên MAC OSX 10.8.3 64 bit 2.4GHZ


1
đăng một thời gian cho một máy chưa biết nói gì. Câu trả lời được chấp nhận ở đây cho biết "không có psyco, với n = 1000000, rwh_primes2 là nhanh nhất". Vì vậy, nếu bạn cung cấp thời gian của mình cho mã đó cũng như của bạn, trên cùng một máy và ở mức 2, 4, 10 triệu, thì nó sẽ có nhiều thông tin hơn.
Will Ness

-1, Mã này phụ thuộc vào các tính năng đặc biệt của bitarray được triển khai trong C, đó là lý do tại sao mã nhanh vì hầu hết các công việc đang được thực hiện bằng mã gốc trong phép gán lát. Gói bitarray BREAKS định nghĩa chuẩn cho các lát cắt thích hợp (được lập chỉ mục trong một phạm vi) cho các chuỗi có thể thay đổi trong đó cho phép gán một boolean 0/1 hoặc True / false cho tất cả các phần tử của lát cắt, trong khi hành vi tiêu chuẩn cho Python thuần dường như không được cho phép điều này và chỉ cho phép giá trị gán là 0 trong trường hợp nó được coi là một del của tất cả các phần tử lát từ chuỗi / mảng.
GordonBood

cont'd: Nếu so sánh việc gọi mã gốc không chuẩn, chúng tôi cũng có thể viết gói trình tạo chuỗi "fastprimes" dựa trên mã C như số nguyên tố của Kim Walisch và tạo ra tất cả các số nguyên tố trong bốn tỷ cộng với 32 phạm vi số -bit chỉ trong vài giây với một cuộc gọi đến trình tạo chuỗi. Điều này cũng sẽ sử dụng hầu như không có bộ nhớ vì mã được liên kết dựa trên Sàng lọc Eratosthenes được phân đoạn và do đó chỉ sử dụng một vài mười Kilobyte RAM và nếu một chuỗi được tạo thì sẽ không cần lưu trữ danh sách.
GordonBood

0

Tôi đã thu thập một số sàng số nguyên tố theo thời gian. Nhanh nhất trên máy tính của tôi là thế này:

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

0

Tôi chậm trả lời câu hỏi này nhưng có vẻ như là một bài tập thú vị. Tôi đang sử dụng numpy có thể gian lận và tôi nghi ngờ phương pháp này là nhanh nhất nhưng cần phải rõ ràng. Nó sàng một mảng Boolean chỉ liên quan đến các chỉ số của nó và gợi ra các số nguyên tố từ các chỉ số của tất cả các giá trị True. Không cần modulo.

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]

nó không đúng, ví dụ: ajs_primes3a(10)-> array([2, 3, 5, 7, 9]). 9không phải là số nguyên tố
jfs

Bạn phát hiện ra một trường hợp cạnh tôi đã không - làm tốt lắm! Vấn đề nằm ở 'đối với idx trong phạm vi (3, int (tối đa ** 0,5), 2):' phải là 'đối với idx trong phạm vi (3, int (tối đa ** 0,5) + 1, 2):'. Cảm ơn nhưng nó hoạt động bây giờ.
Alan James Salmoni

Lý do là vòng lặp idx đã tăng lên 'tối đa ** 05', đối với các trường hợp lên đến và bao gồm 15. Từ 16 trở đi, nó hoạt động tốt. Đây là một bộ các trường hợp cạnh tôi đã thử nghiệm. Thêm 1 có nghĩa là nó sẽ hoạt động cho tất cả các số.
Alan James Salmoni

Nó dường như làm việc bây giờ. Đây là numpygiải pháp chậm nhất trong số các giải pháp dựa trên trả về một mảng. Lưu ý: không có triển khai Seat of Eratosthenes thực sự sử dụng modulo - không cần đề cập đến nó. Bạn có thể sử dụng mat[idx*idx::idx]thay vì mat[idx*2::idx]. Và np.nonzero(mat)[0]thay vì np.where(mat == True)[0].
jfs

Cảm ơn JF. Tôi đã thử nghiệm với Prime6 () và nhận được kết quả nhanh hơn lên tới (IIRC) khoảng 250k khi Prime6 () tiếp quản. primesfrom2to () đã nhanh hơn. Ở độ cao tối đa 20m, ajs_primes3a () mất 0,034744977951ms, Prime6 () mất 0,0222899913788ms và primesfrom2to () mất 0,0104751586914ms (cùng máy, cùng tải, tốt nhất trong 10 thời gian). Nó thực sự tốt hơn tôi nghĩ nó sẽ được!
Alan James Salmoni 16/2/2015

0

Đây là một kỹ thuật thú vị để tạo ra các số nguyên tố (nhưng không hiệu quả nhất) bằng cách hiểu danh sách của 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]

Bạn có thể tìm thấy ví dụ và một số giải thích ngay tại đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.