วิธีที่เร็วที่สุดในการแสดงรายการช่วงเวลาทั้งหมดที่ต่ำกว่า N


357

นี่เป็นอัลกอริธึมที่ดีที่สุดที่ฉันสามารถทำได้

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

โค้ด sniplet ที่เป็นปัญหานั้นเร็วกว่ามากถ้าตัวเลขประกาศเช่น numbers = set (ช่วง (n, 2, -2)) แต่ไม่สามารถเอาชนะ sundaram3 ได้ ขอบคุณสำหรับคำถาม
Shekhar

3
มันจะดีถ้ามีฟังก์ชั่น Python 3 ในคำตอบ
Michael Foukarakis

แน่นอนมีห้องสมุดการทำเช่นนี้เพื่อให้เราไม่ต้องม้วนของเราเอง> xkcd import antigravityสัญญางูหลามเป็นง่ายๆเป็น มีอะไรที่เหมือนrequire 'prime'; Prime.take(10)(Ruby) หรือไม่?
พันเอก Panic

2
@ColonelPanic มันเกิดขึ้นฉันอัพเดทgithub.com/jaredks/pyprimesieveสำหรับ Py3 และเพิ่มไปยัง PyPi แน่นอนว่าเร็วกว่าสิ่งเหล่านี้ แต่ไม่ใช่คำสั่งของขนาด - เร็วกว่า ~ 5x ที่ดีที่สุดสำหรับรุ่น numpy
Jared

3
@ColonelPanic: ฉันคิดว่าการแก้ไขคำตอบเก่าเพื่อให้ทราบว่าพวกเขามีอายุมากขึ้นเหมาะสมแล้วเนื่องจากมันทำให้เป็นทรัพยากรที่มีประโยชน์มากขึ้น หากคำตอบ "ยอมรับ" ไม่ใช่คำตอบที่ดีที่สุดอีกต่อไปอาจแก้ไขบันทึกย่อในคำถามพร้อมอัปเดตปี 2015 เพื่อชี้ผู้คนด้วยวิธีที่ดีที่สุดในปัจจุบัน
Peter Cordes

คำตอบ:


366

คำเตือน: timeitผลลัพธ์อาจแตกต่างกันเนื่องจากความแตกต่างของฮาร์ดแวร์หรือเวอร์ชั่นของ Python

ด้านล่างเป็นสคริปต์ที่เปรียบเทียบจำนวนการใช้งาน:

ขอบคุณมากที่สเตฟานสำหรับการนำ sieve_wheel_30 ความสนใจของฉัน เครดิตไปที่Robert William Hanksสำหรับช่วงเวลาจาก 2 ถึง, primes จาก 3 ถึง, rwh_primes, rwh_primes1 และ rwh_primes2

ในการทดสอบด้วยวิธี Python แบบธรรมดาด้วย psycoสำหรับ 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):
    # /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)

การรันสคริปต์ทดสอบว่าการใช้งานทั้งหมดให้ผลลัพธ์เหมือนกัน


4
หากคุณสนใจในรหัสที่ไม่บริสุทธิ์ - ไพ ธ อนคุณควรตรวจสอบgmpy- มันมีการสนับสนุนที่ดีสำหรับ primes ผ่านnext_primeวิธีการของmpzประเภท
Alex Martelli

1
หากคุณกำลังใช้ pypy มาตรฐานเหล่านี้ (คน psyco) ดูเหมือนค่อนข้างออก น่าแปลกที่พอฉันพบ sieveOfEratosthenes และ ambi_sieve_plain ให้เร็วที่สุดด้วย pypy นี่คือสิ่งที่ฉันพบสำหรับคนที่ไม่ได้เป็นคนโต gist.github.com/5bf466bb1ee9e5726a52
Ehsan Kia

1
หากมีคนสงสัยว่าฟังก์ชั่นการทำงานที่นี่เทียบกับPG7.8 ของ Wikibooksสำหรับไพ ธ อนบริสุทธิ์โดยไม่มี psyco หรือ pypy: สำหรับ n = 1000000: PG7.8: 4.93 s ต่อลูป; rwh_primes1: 69 ms ต่อลูป; rwh_primes2: 57.1 ms ต่อลูป
gaborous

8
คุณสามารถอัปเดตสิ่งนี้ด้วย PyPy ตอนนี้ psyco นั้นตายแล้วและ PyPy ได้แทนที่มันแล้วหรือไม่
noɥʇʎԀʎzɐɹƆ

3
จะดีมากถ้าฟังก์ชั่นและการกำหนดเวลาเหล่านี้สามารถอัพเดตสำหรับ python3
cs95

135

รหัสงูหลามที่ชาญฉลาดและเร็วขึ้นและจำได้ง่ายขึ้น:

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

การเปลี่ยนแปลงที่เร็วขึ้นเริ่มต้นด้วยหนึ่งในสามของตะแกรง:

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

รหัส Pure-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 ไม่ได้ใช้วิธีการมอบหมายที่ง่ายและเร็วกว่าและการโทรlen()ภายในลูป[False]*len(sieve[((k*k)//3)::2*k])นั้นช้าเกินไป ดังนั้นฉันจึงต้องแก้ไขการป้อนข้อมูล (& หลีกเลี่ยงคณิตศาสตร์มากขึ้น) และทำคณิตศาสตร์เวทมนต์ (เจ็บปวด) มาก

โดยส่วนตัวแล้วฉันคิดว่ามันเป็นความอัปยศที่ numpy (ซึ่งใช้กันอย่างแพร่หลาย) ไม่ได้เป็นส่วนหนึ่งของไลบรารีมาตรฐานของ Python และการปรับปรุงทางไวยากรณ์และความเร็วดูเหมือนจะถูกมองข้ามโดยนักพัฒนา Python


2
ขณะนี้ Numpy เข้ากันได้กับ Python 3 ความจริงที่ว่ามันไม่ได้อยู่ในไลบรารี่มาตรฐานนั้นดีพวกเขาสามารถมีวงจรการปล่อยของตนเองได้
Adam

เพื่อเก็บค่าไบนารีไว้ในอาร์เรย์ที่ฉันแนะนำbitarray- ตามที่ใช้ที่นี่ (สำหรับตะแกรงนายกรัฐมนตรีที่ง่ายที่สุดไม่ใช่คู่แข่งในการแข่งขันที่นี่!) stackoverflow.com/questions/31120986/ …
hiro ตัวเอก

เมื่อทำการหล่อในprimesfrom2to()วิธีการแบ่งควรอยู่ในวงเล็บหรือไม่
355durch113

3
สำหรับเวอร์ชั่นไพ ธ อนแท้ๆที่เข้ากันได้กับ python 3 ให้ไปที่ลิงค์นี้: stackoverflow.com/a/33356284/2482582
Moebius

1
ก้นศักดิ์สิทธิ์ไม่มีเครื่องดูดนี้เร็ว
สกอตต์

42

มีตัวอย่างที่ค่อนข้างเรียบร้อยจาก Python Cookbook ที่นี่ - รุ่นที่เร็วที่สุดที่เสนอบน 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 ฉันสังเกตเห็น:

$ 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 จะเร็วกว่าสองเท่า


1
@jbochi ยินดีต้อนรับ - แต่อย่าลืมดู URL นั้นรวมถึงเครดิตด้วย: เราใช้เวลาสิบคนในการปรับแต่งโค้ดรวมถึงจุดนี้รวมถึงผู้ทรงคุณวุฒิ Python ที่มีประสิทธิภาพเช่น Tim Peters และ Raymond Hettinger (ฉันเขียน ข้อความสุดท้ายของสูตรตั้งแต่ฉันแก้ไข Cookbook ที่พิมพ์ออกมา แต่ในแง่ของการเขียนโค้ดของฉันมีส่วนร่วมกับคนอื่น ๆ ) - สุดท้ายแล้วมันเป็นรหัสที่ละเอียดและละเอียดมากและนั่นก็ไม่น่าแปลกใจ! -)
Alex Martelli

@Alex: การรู้ว่ารหัสของคุณเป็น "เพียง" สองเท่าเร็วเป็นของฉันทำให้ฉันภูมิใจมาก :) URL ก็น่าสนใจมากที่จะอ่าน ขอบคุณอีกครั้ง.
jbochi

และก็สามารถทำได้เร็วยิ่งขึ้นกับการเปลี่ยนแปลงเล็ก ๆ น้อย ๆ : ดูstackoverflow.com/questions/2211990/...
tzot

1
... และมันสามารถทำได้เร็วขึ้นด้วยการเพิ่มความเร็วประมาณ 1.2x-1.3x ลดรอยเท้าหน่วยความจำอย่างมากจาก O (n) ถึง O (sqrt (n)) และปรับปรุงความซับซ้อนของเวลาเชิงประจักษ์โดยเลื่อนการเพิ่ม เฉพาะช่วงเวลาที่จะอ่านจนกว่าจะเห็นสแควร์ของพวกเขาในอินพุต ทดสอบได้ที่นี่
Will Ness

28

ใช้ตะแกรงของ Sundaramฉันคิดว่าฉันทำลายสถิติของ 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)

Comparasion:

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
ฉันจัดการเพื่อเพิ่มความเร็วฟังก์ชั่นของคุณประมาณ 20% โดยการเพิ่ม "zero = 0" ที่ด้านบนของฟังก์ชั่นแล้วแทนที่แลมบ์ดาในตัวกรองของคุณด้วย "zero .__ sub__" ไม่ได้เป็นรหัสที่สวยที่สุดในโลก แต่บิตเร็วขึ้น :)
truppo

1
@truppo: ขอบคุณสำหรับความคิดเห็นของคุณ! ฉันเพิ่งรู้ว่าการส่งผ่านNoneแทนที่จะใช้ฟังก์ชั่นดั้งเดิมและมันก็เร็วกว่าzero.__sub__
jbochi

7
คุณรู้ไหมว่าถ้าคุณผ่านsundaram3(9)มันจะกลับมา[2, 3, 5, 7, 9]? ดูเหมือนว่าจะทำเช่นนี้กับจำนวนมาก - บางทีทั้งหมด - ตัวเลขคี่ (แม้ในขณะที่พวกเขาไม่ได้เป็นนายก)
wrhall

1
มันมีปัญหา: sundaram3 (7071) รวมถึง 7071 ในขณะที่มันไม่ได้เป็นนายก
ใหญ่อื่น ๆ

18

อัลกอริทึมนั้นเร็ว แต่มีข้อบกพร่องร้ายแรง:

>>> 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()ลบและส่งคืนองค์ประกอบโดยพลการดังนั้นจึงไม่สามารถใช้เพื่อเลือกนายกถัดไปจากตัวเลขที่เหลือ


17

สำหรับวิธีแก้ปัญหาที่เร็วที่สุดอย่างแท้จริงด้วย N ที่มีขนาดใหญ่พอที่จะดาวน์โหลดรายการ primes ที่คำนวณล่วงหน้าให้เก็บไว้เป็น tuple และทำสิ่งที่ชอบ:

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

หากN > primes[-1] เพียงแล้วคำนวณช่วงเวลาที่มากขึ้นและประหยัดรายการใหม่ในรหัสของคุณในครั้งต่อไปดังนั้นมันจึงเป็นอย่างเท่าเทียมกันเป็นไปอย่างรวดเร็ว

คิดนอกกรอบเสมอ


9
เพื่อความเป็นธรรมคุณจะต้องนับเวลาการดาวน์โหลดการคลายซิปและการจัดรูปแบบช่วงเวลาและเปรียบเทียบกับเวลาในการสร้างช่วงเวลาโดยใช้อัลกอริทึม - อัลกอริทึมใด ๆ เหล่านี้สามารถเขียนผลลัพธ์ลงในไฟล์ได้อย่างง่ายดายในภายหลัง ใช้. ฉันคิดว่าในกรณีที่ให้หน่วยความจำเพียงพอที่จะคำนวณเฉพาะช่วงเวลาน้อยกว่า 982,451,653 จริง ๆ โซลูชัน numpy จะยังคงเร็วขึ้น
Daniel G

3
@Daniel ถูกต้อง อย่างไรก็ตามการจัดเก็บสิ่งที่คุณมีและดำเนินการต่อเมื่อใดก็ตามที่จำเป็นยังคงยืนอยู่ ...
Kimvais

@Daniel GI คิดว่าเวลาดาวน์โหลดไม่เกี่ยวข้อง ไม่ได้เกี่ยวกับการสร้างตัวเลขดังนั้นคุณต้องคำนึงถึงอัลกอริทึมที่ใช้ในการสร้างรายการที่คุณกำลังดาวน์โหลด และเมื่อใดก็ตามที่ความซับซ้อนจะเพิกเฉยต่อการถ่ายโอนไฟล์เพียงครั้งเดียวเนื่องจาก O (n)
Ross

คำถามที่พบบ่อยสำหรับหน้า UTM ที่สำคัญที่แสดงให้เห็นการคำนวณช่วงเวลาเล็ก ๆ ได้เร็วขึ้นกว่าการอ่านพวกเขาออกจากดิสก์ (คำถามคือสิ่งที่หมายถึงเล็ก)
แบทแมน

12

หากคุณไม่ต้องการที่จะบูรณาการล้อคุณสามารถติดตั้งคณิตศาสตร์สัญลักษณ์ห้องสมุดSymPy (ใช่มันเป็นงูหลาม 3 เข้ากันได้)

pip install sympy

และใช้ฟังก์ชั่นสีรองพื้น

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

8

หากคุณยอมรับ itertools แต่ไม่ใช่คนโง่นี่คือการปรับ rwh_primes2 สำหรับ Python 3 ที่ทำงานเร็วกว่าเครื่องของฉันประมาณสองเท่า การเปลี่ยนแปลงที่สำคัญเพียงอย่างเดียวคือการใช้ bytearray แทนรายการสำหรับบูลีนและใช้การบีบอัดแทนการเข้าใจรายการเพื่อสร้างรายการสุดท้าย (ฉันจะเพิ่มนี่เป็นความคิดเห็นเช่น 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

การใช้งานที่ยอดเยี่ยมมาก :)
Krish

7

มันเป็นคำแนะนำในการเขียนโค้ดการค้นหาเฉพาะของคุณเอง แต่มันก็มีประโยชน์ที่จะมีไลบรารี่ที่น่าเชื่อถือได้อย่างรวดเร็ว ฉันเขียน wrapper รอบ ๆC ++ library primesieveตั้งชื่อมัน primesieve-python

ลองมัน pip install primesieve

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

ฉันอยากรู้อยากเห็นเมื่อเทียบความเร็ว


มันไม่ใช่สิ่งที่ OP สั่ง แต่ฉันล้มเหลวที่จะดูว่าทำไม downvote มันเป็นโซลูชั่น 2.8sec ซึ่งแตกต่างจากโมดูลภายนอกอื่น ๆ ฉันสังเกตเห็นในแหล่งที่มาว่ามันเป็นเกลียวได้รับการทดสอบใด ๆ ว่ามันปรับขนาดได้อย่างไร
ljetibo

@ljetibo ไชโย คอขวดดูเหมือนว่าจะคัดลอกเวกเตอร์ C ++ ไปยังรายการ Python ดังนั้นcount_primesฟังก์ชั่นนี้เร็วกว่ามากgenerate_primes
Colonel Panic

ในคอมพิวเตอร์ของฉันสามารถสร้างช่วงเวลาได้ถึง 1e8 (ให้หน่วยความจำ MemoryError เป็นเวลา 1e9) และนับจำนวนครั้งสูงสุดถึง 1e10 @HappyLeapSecond ด้านบนเปรียบเทียบอัลกอริทึมสำหรับ 1e6
พันเอก Panic

7

นี่คือรุ่นที่ได้รับการปรับปรุง (pure 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:])]

1
ใน Python 3 ฉันใช้ฟังก์ชันนี้stackoverflow.com/a/3035188/7799269แต่แทนที่ / ด้วย // และ xrange ด้วยช่วงและพวกเขาดูเหมือนเร็วกว่านี้มาก
samerivertwice

4

การดำเนินการทดสอบเบื้องต้นของมิลเลอร์ - ราบินในการกำหนดข้อสันนิษฐานว่า 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)

ตามบทความใน Wikipedia ( http://en.wikipedia.org/wiki/Miller–Rabin_primality_test ) ทดสอบ N <9,080,191 สำหรับ a = 2,3,37 และ 73 เพียงพอที่จะตัดสินใจว่า N ประกอบกันหรือไม่

และฉันได้ดัดแปลงซอร์สโค้ดจากการใช้ความน่าจะเป็นของการทดสอบของ Miller-Rabin ดั้งเดิมที่นี่: http://en.literateprograms.org/Miller-Rabin_primality_test_(Python)


1
ขอบคุณสำหรับการทดสอบแบบดั้งเดิมของ Miller-Rabin แต่จริงๆแล้วรหัสนี้ช้ากว่าและไม่ได้ให้ผลลัพธ์ที่ถูกต้อง 37 เป็นเลิศและไม่ผ่านการทดสอบ
jbochi

ฉันเดาว่า 37 เป็นหนึ่งในกรณีพิเศษที่แย่ของฉัน ผมก็มีความหวังเกี่ยวกับรุ่นที่กำหนดแม้ว่า :)
Ruggiero Spearman

ไม่มีกรณีพิเศษสำหรับมิลเลอร์ rabin
เข้าใจผิด

2
คุณอ่านบทความผิด นี่คือ 31 ไม่ใช่ 37 นี่คือสาเหตุที่การดำเนินการของคุณล้มเหลว
โลแกน

4

หากคุณสามารถควบคุม N วิธีที่เร็วที่สุดที่จะแสดงรายการช่วงเวลาทั้งหมดคือการคำนวณล่วงหน้า อย่างจริงจัง. การคำนวณล่วงหน้าเป็นวิธีที่มองข้ามการเพิ่มประสิทธิภาพ


3
หรือดาวน์โหลดได้จากที่นี่primes.utm.edu/list/small/millionsแต่ความคิดคือการทดสอบขีด จำกัด ของงูใหญ่และดูว่ารหัสที่สวยงามเกิดจากการเพิ่มประสิทธิภาพ
jbochi

4

นี่คือรหัสที่ฉันใช้ในการสร้างช่วงเวลาใน 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)

มันไม่สามารถแข่งขันกับโซลูชั่นที่โพสต์ได้เร็วขึ้นที่นี่ แต่อย่างน้อยมันก็เป็นงูหลามบริสุทธิ์

ขอบคุณที่โพสต์คำถามนี้ วันนี้ฉันเรียนรู้มากจริงๆ


3

สำหรับโค้ดที่เร็วที่สุดโซลูชัน numpy นั้นดีที่สุด ด้วยเหตุผลทางวิชาการล้วนๆฉันกำลังโพสต์เวอร์ชั่นไพ ธ อนแท้ๆซึ่งเร็วกว่ารุ่นตำราทำอาหารด้านบนเล็กน้อยกว่า 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

3

การใช้งานครึ่งตะแกรงที่แตกต่างกันเล็กน้อยโดยใช้ Numpy:

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

คณิตศาสตร์นำเข้า
นำเข้าจำนวนมาก
def prime6 (ไม่เกิน):
    เฉพาะ = numpy.arange (3, ไม่เกิน + 1,2)
    isprime = numpy.ones ((ไม่เกิน-1) / 2 = dtype บูล)
    สำหรับปัจจัยในช่วงเวลา [: int (math.sqrt (ไม่เกิน))]:
        ถ้า isprime [(factor-2) / 2]: isprime [(factor * 3-2) / 2: (ไม่เกิน -1) / 2: factor] = 0
    return numpy.insert (ช่วงเวลา [isprime], 0,2)

ใครสามารถเปรียบเทียบสิ่งนี้กับเวลาอื่นได้หรือไม่ ในเครื่องของฉันดูเหมือนว่าจะเทียบเคียงได้กับ Numpy half-sieve


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

3

มันเขียนและทดสอบทั้งหมด ดังนั้นจึงไม่จำเป็นต้องบูรณาการล้อ

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 ms สำหรับทางออกที่ดีที่สุด เปรียบเทียบสิ่งนี้ !!!


3

ฉันทดสอบฟังก์ชั่นของ unutbuฉันคำนวณมันด้วยจำนวนล้านที่ถูกล่า

ผู้ชนะคือฟังก์ชั่นที่ใช้ไลบรารี numpy

หมายเหตุ : มันน่าสนใจที่จะทำการทดสอบการใช้งานหน่วยความจำด้วย :)

ผลการคำนวณเวลา

โค้ดตัวอย่าง

กรอกรหัสในที่เก็บ github ของฉัน

#!/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
เพื่อเปรียบเทียบการแสดงขั้นตอนจะดีกว่าที่พล็อตที่ขนาดเข้าสู่ระบบเข้าสู่ระบบ
Will Ness

3

สำหรับ 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

ตะแกรงเฉพาะที่เร็วที่สุดในPure Python :

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

ฉันปรับให้เหมาะที่สุดของ Eratosthenesสำหรับความเร็วและหน่วยความจำ

เกณฑ์มาตรฐาน

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)

เอาท์พุต

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

ครั้งแรกที่ใช้งูหลามดังนั้นวิธีการบางอย่างที่ฉันใช้ในการนี้อาจดูค่อนข้างยุ่งยาก ฉันเพิ่งแปลงรหัส 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

พบ 664579 ช่วงเวลาใน 12.799119 วินาที!

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

พบ 664579 ช่วงเวลาใน 10.230172 วินาที!

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

หลาม Primes2.py

พบ 664579 ช่วงเวลาใน 7.113776 วินาที!


2

ฉันรู้ว่าการแข่งขันถูกปิดเป็นเวลาหลายปี ...

อย่างไรก็ตามนี่เป็นข้อเสนอแนะของฉันสำหรับไพ ธ อนไพร์มเพรซเซอร์แบบพื้นฐานโดยไม่ต้องคูณหลายค่าเป็น 2, 3 และ 5 โดยใช้ขั้นตอนที่เหมาะสมขณะประมวลผลตะแกรงไปข้างหน้า อย่างไรก็ตามมันช้ากว่าสำหรับ N <10 ^ 9 มากกว่า @Robert William Hanks โซลูชั่นที่เหนือกว่า rwh_primes2 และ rwh_primes1 โดยการใช้อาร์เรย์ ctypes.c_ushort ที่สูงกว่า 1.5 * 10 ^ 8 จะสามารถปรับให้เข้ากับขีด จำกัด หน่วยความจำได้

10 ^ 6

$ python -mtimeit -s "import primeSieveSpeedComp" "primeSieveSpeedComp.primeSieveSeq (1000000)" 10 ลูปที่ดีที่สุดคือ 3: 46.7 msec ต่อวง

เพื่อเปรียบเทียบ: $ python -mtimeit -s "import primeSieveSpeedComp" "primeSieveSpeedComp.rwh_primes1 (1000000)" 10 ลูปที่ดีที่สุดคือ 3: 43.2 msec ต่อลูปเพื่อเปรียบเทียบ: $ python -m timeit -s "PrimeSieveSpeedComp.rwh_primes1" (1000000) (1000000) "10 ลูปที่ดีที่สุดคือ 3: 34.5 msec ต่อลูป

10 ^ 7

$ python -mtimeit -s "import primeSieveSpeedComp" "primeSieveSpeedComp.primeSieveSeq (10,000000)" 10 ลูปที่ดีที่สุดคือ 3: 530 msec ต่อวง

เพื่อเปรียบเทียบ: $ python -mtimeit -s "import primeSieveSpeedComp" "primeSieveSpeedComp.rwh_primes1 (10,000000000)" 10 ลูปที่ดีที่สุดคือ 3: 494 msec ต่อลูปเพื่อเปรียบเทียบ: $ python -m timeit -s "PrimeSieveSpeedComp.rwh_primes1" (10,000000) "10 ลูปที่ดีที่สุดคือ 3: 375 msec ต่อลูป

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

จะเห็นภาพผลการทดสอบของคุณพล็อตพวกเขาในระดับเข้าสู่ระบบเข้าสู่ระบบเพื่อดูและเปรียบเทียบคำสั่งเชิงประจักษ์ของการเจริญเติบโต
Will Ness

@ จะขอบคุณสำหรับการป้อนข้อมูลฉันจะมีสิ่งนี้อยู่ในใจในครั้งต่อไปที่ฉันต้องการการเปรียบเทียบเช่นนี้
ABri

1

นี่คือ Sump of Eratosthenes รุ่นที่มีความซับซ้อนทั้งคู่มีความซับซ้อนที่ดี (ต่ำกว่าการเรียงลำดับความยาว n) และ vectorization เมื่อเทียบกับ @unutbu เวลานี้เร็วเท่ากับแพ็คเกจที่มี 46 microsecons เพื่อค้นหาช่วงเวลาที่ต่ำกว่าล้านทั้งหมด

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

1

ฉันได้อัปเดตโค้ดส่วนใหญ่สำหรับ Python 3 แล้วโยนมันที่perfplot (โครงการของฉัน) เพื่อดูว่าอันไหนเร็วที่สุด ปรากฎว่าสำหรับขนาดใหญ่n, primesfrom{2,3}toนำเค้ก:

ป้อนคำอธิบายรูปภาพที่นี่


รหัสในการทำซ้ำพล็อต:

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

ฉันเดาว่าวิธีที่เร็วที่สุดของทั้งหมดคือการกำหนดรหัสในรหัสของคุณ

เหตุใดจึงไม่เพียงเขียนสคริปต์ช้าที่สร้างไฟล์ต้นฉบับอื่นที่มีตัวเลขทั้งหมดเดินสายอยู่แล้วนำเข้าไฟล์ต้นฉบับนั้นเมื่อคุณเรียกใช้โปรแกรมจริง

แน่นอนมันใช้งานได้ก็ต่อเมื่อคุณรู้ขอบเขตสูงสุดของ N ในเวลารวบรวม แต่ก็เป็นปัญหาสำหรับ (เกือบ) ปัญหาทั้งหมดของโครงการออยเลอร์

 

PS: ฉันอาจจะผิดแม้ว่า iff การแยกแหล่งที่มากับ primes แบบมีสายฮาร์ดจะช้ากว่าการคำนวณพวกเขาในสถานที่แรก แต่เท่าที่ฉันรู้ Python ทำงานจาก.pycไฟล์ที่คอมไพล์ดังนั้นการอ่านอาร์เรย์ไบนารีกับ primes ทั้งหมดถึง N ควรเลือด รวดเร็วในกรณีนั้น


0

ขออภัยที่รบกวน แต่ erat2 () มีข้อบกพร่องร้ายแรงในอัลกอริทึม

ในขณะที่ค้นหาคอมโพสิตต่อไปเราจำเป็นต้องทดสอบตัวเลขคี่เท่านั้น q, p ทั้งคู่แปลก q + p จึงเป็นเลขคู่และไม่จำเป็นต้องทำการทดสอบ แต่ q + 2 * p จะเป็นเลขคี่เสมอ สิ่งนี้จะกำจัดการทดสอบ "if if" ในขณะที่วนลูปเงื่อนไขและบันทึกประมาณ 30% ของรันไทม์

ขณะที่เราอยู่ที่นี่: แทนที่จะใช้ 'D.pop (q, None)' ที่ได้รับและลบวิธีการใช้ 'ถ้า q ใน 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

สำหรับนอกจากนี้ที่เลื่อนออกไปจากช่วงเวลาที่เข้า Dict นี้ (จนกว่าตารางที่สำคัญที่จะเห็นในการป้อนข้อมูล) ดูstackoverflow.com/a/10733621/849891
Will Ness

0

วิธีที่เร็วที่สุดที่ฉันเคยลองมาก็คือฟังก์ชั่นตำรา 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

ดูคำตอบนี้สำหรับคำอธิบายของการเร่งความเร็ว


0

ฉันอาจจะไปงานปาร์ตี้สาย แต่จะต้องเพิ่มรหัสของตัวเองสำหรับสิ่งนี้ มันใช้พื้นที่ประมาณ n / 2 เพราะเราไม่จำเป็นต้องเก็บเลขคู่และฉันยังใช้โมดูลหลาม bitarray อีกด้วยลดการใช้หน่วยความจำของ draStically และเปิดใช้งานการคำนวณทั้งหมดได้สูงสุด 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


1
โพสต์หนึ่งช่วงเวลาสำหรับเครื่องที่ไม่รู้จักไม่พูดอะไร คำตอบที่ยอมรับได้ที่นี่บอกว่า "ไม่มี psyco สำหรับ n = 1000000 rwh_primes2 นั้นเร็วที่สุด" ดังนั้นหากคุณต้องการให้การกำหนดเวลาของคุณสำหรับรหัสที่เช่นเดียวกับความนับถือในเครื่องเดียวกันและที่ 2, 4, 10 ล้านเป็นอย่างดีจากนั้นก็ต้องการจะให้ข้อมูลมากขึ้น
Will Ness

-1, รหัสนี้ขึ้นอยู่กับคุณสมบัติพิเศษของ bitarray ที่นำมาใช้ใน C ซึ่งเป็นสาเหตุที่รหัสนั้นเร็วเนื่องจากงานส่วนใหญ่ทำในโค้ดเนทีฟในการกำหนดชิ้น แพคเกจ BitArray แบ่งนิยามมาตรฐานสำหรับชิ้นที่เหมาะสม (การจัดทำดัชนีในช่วงก) สำหรับลำดับที่ไม่แน่นอนในการที่จะช่วยให้การกำหนดแบบบูเดียว 0/1 หรือถูก / ผิดไปทุกองค์ประกอบของชิ้นในขณะที่พฤติกรรมมาตรฐานหลามบริสุทธิ์ดูเหมือนว่าจะ จะไม่อนุญาตให้ทำสิ่งนี้และอนุญาตเฉพาะค่าการมอบหมายเป็น 0 ซึ่งในกรณีนี้จะถือว่าเป็นองค์ประกอบองค์ประกอบทั้งหมดจากลำดับ / อาร์เรย์
GordonBGood

ต่อไป: หากการเปรียบเทียบการเรียกใช้รหัสที่ไม่ได้มาตรฐานนั้นเราอาจเขียนแพ็คเกจตัวสร้างลำดับ "fastprimes" โดยใช้รหัส C เช่นรหัสของKim Walischและสร้าง primes ทั้งหมดในสี่พันล้านบวก 32 ช่วงเลขบิตในเวลาเพียงไม่กี่วินาทีด้วยการโทรไปยังตัวสร้างลำดับ สิ่งนี้จะใช้หน่วยความจำเกือบจะไม่เนื่องจากโค้ดที่เชื่อมโยงนั้นขึ้นอยู่กับ Sieve of Eratosthenes ที่แบ่งหมวดหมู่และใช้เพียงไม่กี่สิบกิโลไบต์ของ RAM และถ้าลำดับถูกสร้างขึ้นจะไม่มีการจัดเก็บรายการที่ต้องการ
GordonBGood

0

ฉันได้รวบรวมจำนวนเฉพาะจำนวนมากเมื่อเวลาผ่านไป เร็วที่สุดในคอมพิวเตอร์ของฉันคือ:

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

ฉันตอบคำถามนี้ช้า แต่ดูเหมือนการออกกำลังกายที่สนุก ฉันใช้ numpy ซึ่งอาจเป็นการโกงและฉันสงสัยว่าวิธีนี้เร็วที่สุด แต่ควรชัดเจน มันกรองอาเรย์บูลีนที่อ้างถึงดัชนีของมันเท่านั้นและนำจำนวนเฉพาะออกจากดัชนีของค่า True ทั้งหมด ไม่จำเป็นต้องมี 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]

มันเป็นเช่นไม่ถูกต้อง->ajs_primes3a(10) ไม่ใช่นายกarray([2, 3, 5, 7, 9])9
jfs

คุณเห็นกรณีขอบฉันไม่ได้ - ทำได้ดีมาก! ปัญหาอยู่ใน 'สำหรับ idx ในช่วง (3, int (สูงสุด ** 0.5), 2):' ซึ่งควรจะเป็น 'สำหรับ idx ในช่วง (3, int (ไม่เกิน ** 0.5) + 1, 2):' ขอบคุณ แต่ตอนนี้มันใช้งานได้
Alan James Salmoni

เหตุผลก็คือ idx loop ขึ้นไปที่ 'upto ** 05' ซึ่งสำหรับกรณีจนถึงและรวม 15 จาก 16 เป็นต้นไปมันใช้งานได้ดี นี่เป็นชุดเคสที่ฉันไม่ได้ทำการทดสอบ การเพิ่ม 1 หมายความว่าควรใช้กับหมายเลขทั้งหมดได้
Alan James Salmoni

ดูเหมือนว่าจะทำงานตอนนี้ มันช้าที่สุดในบรรดาnumpyโซลูชั่นพื้นฐานที่คืนค่าอาร์เรย์ หมายเหตุ: ไม่มีการใช้ Sieve of Eratosthenes ที่แท้จริงใช้โมดูโล่ - ไม่จำเป็นต้องพูดถึง คุณสามารถใช้แทนmat[idx*idx::idx] mat[idx*2::idx]และแทนnp.nonzero(mat)[0] np.where(mat == True)[0]
jfs

ขอบคุณเจเอฟ ฉันทดสอบกับ prime6 () และได้ผลลัพธ์เร็วขึ้นถึง (IIRC) ประมาณ 250k เมื่อ Prime6 () เข้ามา primesfrom2to () เร็วขึ้น ที่ความยาวสูงสุด 20 ม. ajs_primes3a () ใช้เวลา 0.034744977951ms, prime6 () ใช้เวลา 0.0222899913788ms และ primesfrom2to () ใช้เวลา 0.0104751586914ms (เครื่องเดียวกันโหลดเดียวกันดีที่สุด 10 ครั้ง) ดีกว่าที่ฉันคิดไว้จริง ๆ !
Alan James Salmoni

0

นี่คือเทคนิคที่น่าสนใจในการสร้างจำนวนเฉพาะ (แต่ไม่ได้มีประสิทธิภาพมากที่สุด) โดยใช้รายการความเข้าใจของ 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]

คุณสามารถหาตัวอย่างและคำอธิบายได้ที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.