Nach der guten Tradition von Fragen wie Finden der größten Primzahl, deren Länge, Summe und Produkt die Primzahl ist , ist dies eine Variante einer größten Primzahlherausforderung.
Eingang
Ihr Code sollte keine Eingabe annehmen.
Definition
Wir sagen , eine Primzahl p
ist , good
wenn p-1
genau hat 2
verschiedene Primfaktoren.
Ausgabe
Ihr Code sollte den absoluten Unterschied zwischen aufeinanderfolgenden guten Primzahlen ausgeben q
und p
so |q-p|
groß wie möglich und q
die kleinste gute Primzahl größer als p
. Sie können beliebig viele gute Paare ausgeben und Ihre letzte Ausgabe wird als Punktzahl gewertet.
Beispiel
Die Reihenfolge der ersten 55 guten Primzahlen lautet https://oeis.org/A067466 .
Ergebnis
Ihre Punktzahl bezieht sich einfach |q-p|
auf das Paar guter Primzahlen, die Sie ausgeben.
Sprachen und Bibliotheken
Sie können eine beliebige Sprache oder Bibliothek verwenden (die nicht für diese Herausforderung entwickelt wurde), mit Ausnahme von Bibliotheksfunktionen zum Testen der Primalität oder zum Berücksichtigen von Ganzzahlen. Zum Zwecke der Bewertung werde ich jedoch Ihren Code auf meinem Computer ausführen. Geben Sie daher bitte klare Anweisungen für die Ausführung unter Ubuntu.
Mein Computer Die Timings werden auf meinem Computer ausgeführt. Dies ist eine Standard-Ubuntu-Installation auf einem 8-GB-AMD FX-8350-Prozessor mit acht Kernen. Dies bedeutet auch, dass ich in der Lage sein muss, Ihren Code auszuführen.
Einzelheiten
- Ich werde Ihren Code nach 2 Minuten töten, es sei denn, ihm geht vorher der Speicherplatz aus. Es sollte daher darauf geachtet werden, vor dem Abschneiden etwas auszugeben.
- Sie dürfen keine externe Quelle für Primzahlen verwenden.
- Sie können probabilistische Prime-Testmethoden anwenden, obwohl Mego mir sagt, dass Miller-Rabin mit guten Tabellen deterministisch bis zu 341.550.071.728.321 (oder sogar höher) testen kann. Siehe auch http://miller-rabin.appspot.com/ .
Beste Einträge, die alle ganzen Zahlen von 1 prüfen
- 756 von Katze in Go
- 756 von El'endia Starman in Python
- 1932 von Adnan in C # (mit Mono 3.2.8)
- 2640 von yeti in Python (mit pypy 4.01)
- 2754 von Reto Koradi in C ++
- 3486 von Peter Taylor in Java
- 3900 von primo in RPython (mit pypy 4.01)
- 4176 von The Coder in Java
Beste Einträge, bei denen eine große Anzahl von ganzen Zahlen übersprungen werden kann, um eine große Lücke zu finden
- 14226 von Reto Koradi in C ++
- 22596 von primo in RPython (mit pypy 4.01). Rekord nach 5 Sekunden erreicht!
quelle
Antworten:
RPython (PyPy 4.0.1), 4032
RPython ist eine eingeschränkte Teilmenge von Python, die in C übersetzt und dann mit der RPython-Toolchain kompiliert werden kann. Ihr ausdrücklicher Zweck besteht darin, die Erstellung von Sprachinterpreten zu unterstützen, sie können jedoch auch zum Kompilieren einfacher Programme verwendet werden.
Laden Sie zum Kompilieren die aktuelle PyPy-Quelle (PyPy 4.0.1) herunter und führen Sie Folgendes aus:
Die resultierende ausführbare Datei wird
good-primes-c
im aktuellen Arbeitsverzeichnis benannt oder ähnlich.Implementierungshinweise
Der Primzahlengenerator
primes
ist ein unbegrenztes Eratosthenes-Sieb, bei dem ein Rad verwendet wird, um Vielfache von 2 , 3 , 5 oder 7 zu vermeiden . Es ruft sich auch rekursiv auf, um den nächsten Wert für die Markierung zu generieren. Ich bin sehr zufrieden mit diesem Generator. Die Linienprofilerstellung zeigt, dass die langsamsten zwei Linien sind:Daher denke ich, dass es nicht viel Raum für Verbesserungen gibt, außer vielleicht ein größeres Rad zu verwenden.
Für die "Güte" -Prüfung werden zuerst alle Faktoren von zwei aus n-1 entfernt , wobei ein Bit-Twiddling-Hack verwendet wird, um die größte Zweierpotenz zu finden, die ein Divisor ist
(n-1 & 1-n)
. Da p-1 notwendigerweise für jede Primzahl p> 2 gilt , muss 2 einer der unterschiedlichen Primfaktoren sein. Was übrig bleibt, wird an dieis_prime_power
Funktion gesendet, die das tut, was der Name impliziert. Die Prüfung, ob ein Wert eine Primzahl ist, ist "nahezu frei", da sie gleichzeitig mit der Primalitätsprüfung mit höchstens O (log p n) -Operationen durchgeführt wird, wobei p der kleinste Primfaktor von n ist. Die Teilung der Versuche mag ein bisschen naiv erscheinen, aber meiner Meinung nach ist sie die schnellste Methode für Werte unter 2 32 . Ich spare ein bisschen, indem ich das Rad vom Sieb wieder benutze. Bestimmtes:Wenn Sie über ein Rad der Länge 48 iterieren, wird der
p*p < n
Scheck tausende Male übersprungen, und zwar zu dem niedrigen Preis von nicht mehr als 48 zusätzlichen Modulo-Operationen. Außerdem werden mehr als 77% aller Kandidaten übersprungen, anstatt 50%, wenn nur Quoten berücksichtigt werden.Die letzten Ausgaben sind:
Der Code ist auch in Python gültig und sollte 3588 ~ 3900 erreichen, wenn er mit einem aktuellen PyPy-Interpreter ausgeführt wird.
RPython (PyPy 4.0.1), 22596
Diese Vorlage unterscheidet sich geringfügig von den anderen bisher veröffentlichten, da sie nicht alle guten Primzahlen überprüft, sondern stattdessen relativ große Sprünge ausführt. Ein Nachteil dabei ist, dass Siebe nicht verwendet werden können [ich stehe korrigiert da?] , So dass man sich ausschließlich auf Primärtests verlassen muss, die in der Praxis etwas langsamer sind. Es gibt auch ein glückliches Medium zwischen der Wachstumsrate und der Anzahl der Werte, die jedes Mal überprüft werden. Kleinere Werte lassen sich viel schneller überprüfen, größere Werte weisen jedoch mit größerer Wahrscheinlichkeit größere Lücken auf.
Um die mathematischen Götter zu besänftigen, habe ich mich für eine Fibonacci-ähnliche Sequenz entschieden, bei der der nächste Startpunkt die Summe der beiden vorherigen ist. Wenn nach der Prüfung von 10 Paaren keine neuen Datensätze gefunden werden, wird das Skript beim nächsten fortgesetzt.
Die letzten Ausgaben sind:
Beim Kompilieren werden 64-Bit-Ganzzahlen verwendet, obwohl an einigen Stellen davon ausgegangen wird, dass zwei Ganzzahlen ohne Überlauf hinzugefügt werden können, sodass in der Praxis nur 63 verwendbar sind. Bei Erreichen von 62 signifikanten Bits wird der aktuelle Wert zweimal halbiert, um einen Überlauf in der Berechnung zu vermeiden. Das Ergebnis ist, dass das Skript Werte im Bereich von 2 60 - 2 62 durchmischt. Wenn Sie die native Ganzzahlgenauigkeit nicht übertreffen, wird das Skript auch bei der Interpretation schneller.
Das folgende PARI / GP-Skript kann verwendet werden, um dieses Ergebnis zu bestätigen:
quelle
Vermutlich 4032, gemischtes Atkin-Bernstein-Sieb und "deterministisches" Miller-Rabin
Radfaktorisierung und gute Primzahlen
Es ist sehr offensichtlich, dass mit Ausnahme von 2, 3 und 5 jede Primzahl Coprime zu 2 * 3 * 5 = 60 ist. Es gibt 16 Äquivalenzklassen modulo 60, die Coprime zu 60 sind, sodass jeder Primalitätstest nur diese prüfen muss 16 Fälle.
Wenn wir jedoch nach "guten" Primzahlen suchen, können wir die Herde noch weiter ausdünnen. Wenn
gcd(x, 60) = 1
, stellen wir fest, dass in den meisten Fällengcd(x-1, 60)
entweder 6 oder 10 ist. Es gibt 6 Werte,x
für die es 2 ist:Deshalb können wir die „guten“ Primzahlen der Form vorauszuberechnen
2^a 3^b + 1
und2^a 5^b + 1
und verschmelzen sie zu dem Ergebnis eines Strichs Generator, der nur 10% der Zahlen als auch der Auffassung , potentielle Primzahlen.Implementierungshinweise
Da ich bereits eine Java-Implementierung des Atkin-Bernstein-Siebs hatte, die bereits ein Rad als Schlüsselkomponente verwendet, schien es naheliegend, die unnötigen Speichen herauszulösen und den Code anzupassen. Ich habe ursprünglich versucht, eine Producer-Consumer-Architektur zu verwenden, um die 8 Kerne auszunutzen, aber die Speicherverwaltung war zu unübersichtlich.
Um zu testen, ob eine Primzahl eine "gute" Primzahl ist, verwende ich einen "deterministischen" Miller-Rabin-Test (was wirklich einen Miller-Rabin-Test bedeutet, den eine andere Person anhand einer deterministisch generierten Liste vorab überprüft hat). Dies kann sicherlich umgeschrieben werden, um auch Atkin-Bernstein zu verwenden, mit etwas Zwischenspeicherung, um die Bereiche abzudecken, die sqrt, cbrt usw. entsprechen, aber ich bin nicht sicher, ob es eine Verbesserung sein würde (weil es viele Zahlen testen würde, die Ich muss nicht testen), das ist also ein Experiment für einen anderen Tag.
Auf meinem ziemlich alten Computer läuft dies zu
in ziemlich genau 2 Minuten und dann
um 3:10, 3:20 bzw. 3:30.
Speichern als
PPCG65876.java
, Kompilieren alsjavac PPCG65876.java
und Ausführen alsjava -Xmx1G PPCG65876
.quelle
isGood
Scheck zu beschleunigen .C ++, 2754 (alle Werte, Brute-Force-Primalitätstest)
Das ist rohe Gewalt, aber es ist ein Anfang, bevor unsere ansässigen Mathematiker mit etwas Effizienterem arbeiten können.
Ich kann bei Bedarf weitere Erklärungen hinzufügen, aber dies ist wahrscheinlich aus dem Code sehr offensichtlich. Da if
p
eine andere Primzahl als 2 ist, wissen wir, dass sie geradep - 1
ist, und einer der beiden Faktoren ist immer 2. Also zählen wir die Primzahlen auf, reduzieren siep - 1
um alle Faktoren 2 und überprüfen, ob der verbleibende Wert entweder eine Primzahl oder eine solche ist Alle seine Faktoren sind die gleichen Primzahlen.Code:
Das Programm gibt die Differenz sowie die entsprechenden zwei guten Primzahlen jedes Mal aus, wenn eine neue maximale Differenz gefunden wird. Beispielausgabe des Testlaufs auf meinem Computer, bei dem der gemeldete Wert von 2754 nach ca. 1:20 Minuten gefunden wird:
quelle
C ++, 14226 (nur hohe Werte, Miller-Rabin-Test)
Dies separat zu posten, da es sich völlig von meiner ursprünglichen Lösung unterscheidet und ich einen Beitrag, der eine Reihe von positiven Stimmen erhalten hatte, nicht vollständig ersetzen wollte.
Vielen Dank an @primo für den Hinweis auf ein Problem mit der Originalversion. Beim Primzahlentest gab es einen Überlauf für große Zahlen.
Dies nutzt einige Erkenntnisse aus der Entwicklung anderer Lösungen. Die wichtigsten Beobachtungen sind:
Auf dieser Grundlage ist die hier verwendete Methode ziemlich einfach:
Ergebnisse:
Code:
quelle
PyPy-2.4.0
Python-2
Die
x
Dateis...Folge: "Sieh mal Mama! Keine einzige Division!"
;-)
Ich habe es auf Debian8 mit PyPy-2.4.0 getestet und Python2 hat wie folgt begonnen:
Wenn wirklich viel RAM vorhanden ist, kann die
del L[n]
Zeile gelöscht werden.Der grundlegende Primzahlengenerator lautet wie folgt:
Es macht im Grunde genau das, was das Sieb des Eratosthenes tut, aber in einer anderen Reihenfolge.
L
ist ein Wörterbuch, kann aber als Liste (Band) von Nummernlisten angesehen werden. Nicht vorhandene ZellenL[n]
werden so interpretiert, dassn
bisher keine Primteiler bekannt sind.Die
while
Schleife führt bei jeder Runde eine Prim- oder Nicht-Prim-Entscheidung durchL[n]
.Wenn
L[n]
existiert (dasselbe wien in L
),P = L[n]
ist eine Liste verschiedener Primteiler vonn
. Istn
also keine Primzahl.Falls
L[n]
nicht vorhanden, wurde kein Hauptteiler gefunden. Alson
muss der erste sein,P = [n]
wenn man der bekannte Teiler ist.Jetzt
P
ist die Liste der bekannten Primteiler für beide Fälle.Die
for p in P
Schleife bewegt jeden EintragP
vorwärts um den Abstand seines Wertes auf dem Zahlenband.So springen die Divisoren auf das Band und aus diesem Grund müssen diese Sprungzahlen Primzahlen sein. Neue Zahlen werden erst durch die
else
obige Entscheidung auf dem Band gespeichert, und dies sind Zahlen ohne andere bekannte Teiler als sie selbst. Nonprimes kommen nie in diese ListenL[n]
.Die Primzahlen in der Liste sind alle verschieden, da jede Zahl
n
nur einmal betrachtet und nur als Divisor hinzugefügt wird (wenn keine Primzahl :)0
oder (wenn Primzahl :))1
. Bekannte Hauptteiler bewegen sich nur vorwärts, werden jedoch niemals dupliziert. Hält alsoL[n]
immer eindeutige Primteiler oder ist leer.Zurück zum oberen Programm für die guten Primzahlenlücken:
... behält die Primteiler von
n
in,B
wenn bekanntn
ist, dass sie nicht prim sind.Wenn
n
als Primzahl erkannt wird,B
enthält die Liste der Primteiler des vorherigen Schleifendurchlaufs Folgendesn-1
:... so
len(B) == 2
Mitteln - 1
haben zwei verschiedene Primfaktoren.g
Erinnert sich nur an die zuletzt gesehene gute Primzahl vor der neuen,M
ist die Länge der vorherigen maximalen guten Primzahllücke undm
die Länge der neu gefundenen Lücke.Glückliches Ende.
quelle
C #, wahrscheinlich 1932
Ich habe herausgefunden, dass je schneller Ihr Algorithmus zum Auffinden von Primzahlen ist, desto besser ist Ihre Punktzahl. Ich bin mir auch ziemlich sicher, dass mein Algorithmus nicht die optimalste Methode für die Primärsuche ist.
quelle
Python 3, 546
... in zwei Minuten auf meiner Maschine, die meiner Meinung nach bedeutend weniger leistungsstark ist als Ihre.
Ich könnte dies wahrscheinlich effizienter machen, indem ich für den
x=2
Fall optimiere , aber eh. Gut genug. : Pquelle
p: 2, q: 3, |q-p|: 1
für mich ausgegeben.Gehen Sie wahrscheinlich 756
Zum Schämen! Ich bin so ein Anfänger, dass ich nur naiv alten Code wiederverwendet habe und erwartet habe, dass er funktioniert und schnell ist! Wenn ich dies neu implementieren und es tatsächlich um gute Primzahlen herum aufbauen würde, wäre es so viel schneller, aber leider lerne ich. (Ich werde wahrscheinlich morgen mit einer vollständig neu erstellten Lösung antworten, die speziell für diesen Zweck entwickelt wurde.)
Verwendet offensichtlich Parallelität.
quelle
Java, 4224 (99,29 s)
Stark angepasstes Sieb von Eratosthenes mit dem Vorteil von BitSet
Die benötigte Zeit hängt von der Höchstgrenze der zu berechnenden Primzahlen ab.
Zum
quelle
Python 3, 1464
Mit der Hilfe von Lembik , dessen Idee es war, nach einer Potenz von zwei auf die ersten beiden guten Primzahlen zu prüfen und sofort mit der nächsten Potenz von zwei fortzufahren . Wenn jemand dies als Sprungpunkt verwenden kann, fühlen Sie sich frei. Ein Teil meiner Ergebnisse ist unten, nachdem dies in IDLE ausgeführt wurde. Der Code folgt.
Dank an primo, als ich mir die Liste der kleinen Primzahlen für diesen Code schnappte.
Bearbeiten: Ich habe den Code so bearbeitet, dass er den tatsächlichen Spezifikationen des Problems entspricht (zwei unterschiedliche Primteiler, nicht genau zwei unterschiedliche Primteiler), und ich habe implementiert, dass ich nicht zur nächsten Zweierpotenz übergehe, bis die guten Primzahlen, die das Programm gefunden hat, a haben Lücke größer als die der letzten beiden guten gefundenen Primzahlen. Ich sollte auch Peter Taylor meinen Dank aussprechen , da ich seine Idee benutzte, dass gute Primzahlen nur wenige Werte mod 60 sein könnten.
Wieder habe ich dies auf einem langsamen Computer im Leerlauf ausgeführt, so dass die Ergebnisse in etwas wie PyPy möglicherweise schneller sind, aber ich konnte nicht überprüfen.
Eine Stichprobe meiner Ergebnisse (p, q, qp, time):
Mein Code:
quelle
j
um4
anstatt2
? Und Sie scheinen bedingungslos abzulehnen, wennj-1
es sich bei einer Primzahl nicht um eine Zweierpotenz handelt. Hier sollten Sie testen, ob es sich um eine Primzahl handelt , die eine Zweierpotenz ist.Gehe zu: Alle Ganzzahlen: 5112
good.go
:Ausgabe:
Zum Vergleich: peterSO max 5112 in 41.04s gegen The Coder max 4176 in 51.97s.
Coder: max | qp | 4176 q 1964330609 p 1964326433
Ausgabe:
quelle