2016-06-03 11 views
0

Ich habe eine Primzahl-Generator, war ich neugierig zu sehen, wie klein und wie schnell könnte ich einen Primzahl-Generator auf Optimierungen basieren und solche:Prime-Nummer Generator stürzt ab Speicherfehler, wenn es zu viele Zahlen in Array

from math import sqrt 
def p(n): 
    if n < 2: return [] 
    s = [True]*(((n/2)-1+n%2)+1) 
    for i in range(int(sqrt(n)) >> 1): 
    if not s[i]: continue 
    for j in range((i**i+(3*i) << 1) + 3, ((n/2)-1+n%2), (i<<1)+3): s[j] = False 
    q = [2]; q.extend([(i<<1) + 3 for i in range(((n/2)-1+n%2)) if s[i]]); return len(q), q 
print p(input()) 

Der Generator funktioniert gut! Es ist super schnell, fühlen Sie sich frei, es auszuprobieren. Wenn Sie jedoch Zahlen größer als 10^9 oder 10^10 eingeben (denke ich), wird es von einem Speicherfehler abstürzen. Ich kann nicht herausfinden, wie ich den Speicher erweitern kann, damit er so viel wie nötig braucht. Jeder Rat würde sehr geschätzt werden!

Meine Frage ist sehr ähnlich this ein, aber das ist Python, nicht C.

EDIT: Dies ist eine der speicherbezogenen Tracebacks ich für den Versuch, bekommen 10^9 zu laufen.

python prime.py 
1000000000 
Traceback (most recent call last): 
    File "prime.py", line 9, in <module> 
    print p(input()) 
    File "prime.py", line 7, in p 
    for j in range((i**i+(3*i) << 1) + 3, ((n/2)-1+n%2), (i<<1)+3): s[j] = False 
MemoryError 
+1

* Nehmen Sie so viel wie nötig *? Sie müssen erkennen, dass 'q' eine 'liste' ist und irgendwann kein Speicher mehr zur Verfügung steht. –

+0

@MosesKoledoye Ich schätze, das ist meine Frage - gibt es eine Möglichkeit, den Speicher zu erhöhen, bevor er abläuft? An diesem Punkt verwendet es nur ein paar tausend Megabyte. Ich habe das Gefühl, ich könnte es erhöhen. Sollte ich meine Frage aktualisieren, um das zu berücksichtigen? –

+0

Die Vorschläge in der C-verwandten Frage, die Sie verknüpften, sollten auch auf Python gelten ... – agnul

Antwort

5

Das Problem ist in Zeile 7.

for j in range((i**i+(3*i) << 1) + 3, ((n/2)-1+n%2), (i<<1)+3): s[j] = False 

insbesondere dieser Teil: i**i

1000000000^1000000000 ist ein 9 * 10^9-stellige Zahl lange. Die Speicherung dauert mehrere Gb, wenn nicht Tb (WolframAlpha konnte es nicht mehr nachweisen). Ich weiß, dass i die Quadratwurzel von n (maximal) ist, aber bei diesen großen Zahlen ist das kein großer Unterschied.

Sie müssen diese Komponente in kleinere Teile aufteilen, wenn es möglich ist, und auf einer Festplatte sichern. Dies macht den Prozess langsam, aber machbar.

+0

Vielen Dank !! Das habe ich nicht einmal bemerkt! –

+1

Hier ist ein schöner Eintrag zu Primzahlen, den Sie sich vielleicht einmal ansehen wollen: http://stackoverflow.com/questions/2211990/how-to-implement-an-efficient-infinite-generator-of-prime-number-in -python? lq = 1 – frameworker

1

Zuerst gibt es ein Problem, da der Generator sagt, dass Zahlen wie 33, 35 und 45 Primzahl sind.

Abgesehen davon, gibt es mehrere Strukturen hier Speicher Aufnahme:

  • s = [True]*(((n/2)-1+n%2)+1)

Ein Listenelement mehrere Bytes pro Element in Anspruch nimmt. Für n = 1 Milliarde benötigt das Array s Gigabytes.

  • range(...) erstellt eine Liste und iteriert dann über die Elemente. Verwenden Sie stattdessen xrange(...) wo möglich.
  • Konvertieren von range() zu xrange() hat Fallstricke - z.B. siehe das so beantworten:

    OverflowError Python int too large to convert to C long

    Eine bessere Umsetzung der s ist eine ganze Zahl Python als Bit-Array zu verwenden, die eine Dichte von 8 Elementen pro Byte hat. Hier ist eine Übersetzung zwischen einer Liste mit und einer ganzen Zahl:

    s = [True]*(((n/2)-1+n%2)+1)  t = (1 << (n/2)+1)-1 
    
    s[i]        (t & (1<<i)) 
    not s[i]       not (t & (1<<i)) 
    
    s[j] = False      m = 1<<j 
                if (t & m): t ^= m 
    

    aktualisiert

    Hier ist eine nicht optimierte Version, die yield und xrange verwendet.Bei größeren Werten von n sind die oben genannten Einschränkungen von xrange zu beachten.

    def primes(n): 
        if n < 2: return 
        yield 2 
        end = int(sqrt(n)) 
        t = (1 << n) -1 
    
        for p in xrange(3, end, 2): 
        if not (t & (1 << p)): continue 
        yield p 
        for q in xrange(p*p, n, p): 
         m = t & (1<<q) 
         if (t&m): t ^= m 
         continue 
    
        for p in xrange(end - (end%2) +1, n, 2): 
        if not (t & (1 << p)): continue 
        yield p 
    
    
    def test(n): 
        for p in primes(n): print p 
    
    test(100000) 
    
    +0

    Vielen Dank für Ihre Antwort! Ich habe versehentlich eine etwas ältere Version des Codes eingefügt, aber die Optimierungsprobleme sind davon nicht betroffen. Das eigentliche Programm funktioniert einwandfrei. Danke fürs Überprüfen und Bemerken! Wenn ich die Abschnitte meines Codes durch Ihre Bit-Array-Ersetzungen ersetzen werde, wird es dasselbe sein? –

    +0

    Es sollte - ich habe einen Test vor dem Posten durchgeführt. Lass es mich wissen, wenn es nicht ist. Ich würde auch die Primzahlen verwenden, anstatt sie in einer Liste zu speichern. Wenn der Aufrufer Ihrer Routine nur die Primzahlen ausdrucken muss, müssen Sie nicht alle in einer Liste berechnen und speichern. Der Anrufer entscheidet sich auch, sie in einer Liste zu speichern, wenn dies erforderlich ist. – ErikR

    +1

    Antwort mit Ertragsbeispiel aktualisiert. – ErikR