398 Stimmen

Schnellster Weg, um alle Primzahlen unter N aufzulisten

Dies ist der beste Algorithmus, den ich entwickeln konnte.

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

Kann er noch schneller gemacht werden?

Dieser Code hat ein Problem: Da numbers eine ungeordnete Menge ist, gibt es keine Garantie dafür, dass numbers.pop() die niedrigste Zahl aus der Menge entfernt. Trotzdem funktioniert es (zumindest für mich) für einige Eingabelnummern:

>>> sum(get_primes(2000000))
142913828922L
#Das ist die korrekte Summe aller Zahlen unter 2 Millionen
>>> 529 in get_primes(1000)
False
>>> 529 in get_primes(530)
True

0 Stimmen

Der Code-Schnipsel ist viel schneller, wenn Zahlen wie Zahlen = set(range(n, 2, -2)) deklariert werden. Aber kann sundaram3 nicht schlagen. Danke für die Frage.

4 Stimmen

Es wäre schön, wenn es Python 3-Versionen der Funktionen in den Antworten geben könnte.

0 Stimmen

Sicher gibt es eine Bibliothek, um dies zu tun, damit wir nicht selbst programmieren müssen. Xkcd versprach, dass Python so einfach ist wie import antigravity. Gibt es nicht so etwas wie require 'prime'; Prime.take(10) (Ruby)?

8voto

Colonel Panic Punkte 125419

Es ist lehrreich, Ihren eigenen Code zur Primzahlfindung zu schreiben, aber es ist auch nützlich, eine schnelle und zuverlässige Bibliothek zur Hand zu haben. Ich habe einen Wrapper um die C++-Bibliothek primesieve geschrieben, ihn primesieve-python genannt.

Probieren Sie es aus pip install primesieve

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

Ich wäre neugierig zu sehen, wie sich die Geschwindigkeit im Vergleich dazu verhält.

0 Stimmen

Es ist nicht genau das, was OP bestellt hat, aber ich verstehe nicht, warum das Downvote. Es ist eine 2,8-Sekunden-Lösung im Gegensatz zu einigen anderen externen Modulen. Ich habe im Quelltext bemerkt, dass es threadbasiert ist. Haben Sie Tests, wie gut es skaliert?

0 Stimmen

@ljetibo Prost. Der Flaschenhals scheint das Kopieren des C++-Vektors in die Python-Liste zu sein, daher ist die Funktion count_primes viel schneller als generate_primes.

0 Stimmen

Auf meinem Computer können bequem Primzahlen bis 1e8 generiert werden (für 1e9 gibt es einen MemoryError) und Primzahlen bis 1e10 gezählt werden. @HappyLeapSecond vergleicht oben Algorithmen für 1e6.

4voto

nolfonzo Punkte 41

Eine etwas andere Implementierung eines Halbsiebs mit Numpy:

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

import math
import numpy
def prime6(upto):
    primes=numpy.arange(3,upto+1,2)
    isprime=numpy.ones((upto-1)/2,dtype=bool)
    for factor in primes\[:int(math.sqrt(upto))\]:
        if isprime\[(factor-2)/2\]: isprime\[(factor\*3-2)/2:(upto-1)/2:factor\]=0
    return numpy.insert(primes\[isprime\],0,2)

Kann jemand dies mit den anderen Zeiten vergleichen? Auf meinem Rechner scheint es ziemlich vergleichbar mit dem anderen Numpy-Halbsieb zu sein.

0 Stimmen

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

4voto

MrSeeker Punkte 131

Schnellstes Primzahlsieb in Pure Python:

from itertools import compress

def half_sieve(n):
    """
    Gibt eine Liste von Primzahlen zurück, die kleiner als `n` sind.
    """
    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

I optimierte das Sieb des Eratosthenes für Geschwindigkeit und Speicherplatz.

Leistungstest

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} Sekunden für Primzahlen < {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)

Ergebnis

>>> 3.6.7
>>> Windows-10-10.0.17763-SP0
>>> Intel64 Family 6 Model 78 Stepping 3, GenuineIntel
>>> 0.0003 Sekunden für Primzahlen < 10000
>>> 0.0021 Sekunden für Primzahlen < 100000
>>> 0.0204 Sekunden für Primzahlen < 1000000
>>> 0.2389 Sekunden für Primzahlen < 10000000
>>> 2.6702 Sekunden für Primzahlen < 100000000

4voto

MAK Punkte 25200

Hier ist der Code, den ich normalerweise verwende, um Primzahlen in Python zu generieren:

$ python -mtimeit -s'import sieve' 'sieve.sieve(1000000)' 
10 Schleifen, bestes von 3: 445 msec pro Schleife
$ 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)

Es kann nicht mit den schnelleren Lösungen konkurrieren, die hier gepostet wurden, aber zumindest ist es reines Python.

Danke für das Posten dieser Frage. Ich habe heute wirklich viel gelernt.

4voto

Ruggiero Spearman Punkte 6485

Ein deterministischer Implementierung des Miller-Rabin-Primzahltests unter der Annahme, dass N < 9.080.191 ist

import sys

def miller_rabin_pass(a, n):
    d = n - 1
    s = 0
    while d % 2 == 0:
        d >>= 1
        s += 1

    a_to_power = pow(a, d, n)
    if a_to_power == 1:
        return True
    for i in range(s-1):
        if a_to_power == n - 1:
            return True
        a_to_power = (a_to_power * a_to_power) % n
    return a_to_power == n - 1

def miller_rabin(n):
    if n <= 2:
        return n == 2

    if n < 2_047:
        return miller_rabin_pass(2, n)

    return all(miller_rabin_pass(a, n) for a in (31, 73))

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

Laut Artikel auf Wikipedia (http://en.wikipedia.org/wiki/Miller–Rabin_primality_test) ist es ausreichend, N für a = 31 und 73 zu testen, um zu entscheiden, ob N zusammengesetzt ist oder nicht.

Und ich habe den Quellcode von der probabilistischen Implementierung des originalen Miller-Rabin-Tests angepasst, die hier zu finden ist: https://www.literateprograms.org/miller-rabin_primality_test__python_.html

1 Stimmen

Vielen Dank für den Miller-Rabin-Primzahltest, aber dieser Code ist tatsächlich langsamer und liefert nicht die korrekten Ergebnisse. 37 ist eine Primzahl und besteht den Test nicht.

0 Stimmen

Ich denke, 37 ist einer der besonderen Fälle, mein Fehler. Ich war jedoch zuversichtlich bezüglich der deterministischen Version :)

0 Stimmen

Es gibt keinen speziellen Fall für Rabin-Miller.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X