Schreiben Sie ein Programm, um eine Semi-Primzahl in kürzester Zeit zu zerlegen.
Verwenden Sie zu Testzwecken Folgendes: 38! +1 (523022617466601111760007224100074291200000001)
Es ist gleich: 14029308060317546154181 × 37280713718589679646221
fastest-code
primes
Soham Chowdhury
quelle
quelle
12259243
testen möchten, wie schnell die Programme sind, sind die Ergebnisse so gering, dass Sie keine statistisch signifikanten Unterschiede feststellen.Antworten:
Python (mit PyPy JIT v1.9) ~ 1.9s
Verwenden eines quadratischen Siebs mit mehreren Polynomen . Ich nahm dies als eine Code-Herausforderung an und entschied mich daher, keine externen Bibliotheken zu verwenden (außer der Standardfunktion
log
, nehme ich an). Beim Timing sollte das PyPy-JIT verwendet werden, da es zu 4-5-mal schnelleren Timings als das von cPython führt .Update (29.07.2013):
Seit dem ursprünglichen Posting habe ich einige geringfügige, aber signifikante Änderungen vorgenommen, die die Gesamtgeschwindigkeit um den Faktor 2,5 erhöhen.
Update (27.08.2014):
Da dieser Beitrag immer noch Beachtung findet, wurde die
my_math.py
Korrektur von zwei Fehlern für alle Nutzer aktualisiert :isqrt
war fehlerhaft und erzeugte manchmal eine falsche Ausgabe für Werte, die einem perfekten Quadrat sehr nahe kommen. Dies wurde korrigiert und die Leistung durch die Verwendung eines viel besseren Samens gesteigert.is_prime
wurde aktualisiert. Mein vorheriger Versuch, perfekte quadratische 2-Sprps zu entfernen, war bestenfalls halbherzig. Ich habe eine 3-Sprp-Prüfung hinzugefügt - eine von Mathmatica verwendete Technik - um sicherzustellen, dass der getestete Wert quadratfrei ist.Update (24.11.2014):
Wenn am Ende der Berechnung keine nicht-trivialen Kongruenzen gefunden werden, siebt das Programm nun zusätzliche Polynome. Dies wurde zuvor im Code als markiert
TODO
.mpqs.py
my_math.py
Beispiel-E / A:
Hinweis:
--verbose
Wenn Sie diese Option nicht verwenden , erhalten Sie ein etwas besseres Timing:Grundlegendes Konzept
Im Allgemeinen basiert ein quadratisches Sieb auf der folgenden Beobachtung: Jedes ungerade Komposit n kann dargestellt werden als:
Dies ist nicht sehr schwer zu bestätigen. Da n ungerade ist, muss der Abstand zwischen zwei Cofaktoren von n gerade 2d sein , wobei x der Mittelpunkt zwischen ihnen ist. Darüber hinaus gilt die gleiche Beziehung für ein beliebiges Vielfaches von n
Es ist zu beachten, dass wenn ein solches x und d gefunden werden kann, dies sofort zu einem (nicht notwendigerweise primalen) Faktor von n führt , da x + d und x - d beide n durch Definition teilen . Diese Beziehung kann weiter geschwächt werden - als Folge möglicher unbedeutender Kongruenzen - auf die folgende Form:
Wenn wir also zwei perfekte Quadrate finden, die äquivalent zu mod n sind , ist es ziemlich wahrscheinlich, dass wir direkt einen Faktor von n a la gcd (x ± d, n) erzeugen können . Scheint ziemlich einfach, oder?
Außer es ist nicht. Wenn wir beabsichtigen, eine umfassende Suche über alle möglichen x durchzuführen , müssten wir den gesamten Bereich von [ √ n , √ ( 2n ) ] durchsuchen , der geringfügig kleiner ist als die vollständige Testdivision, der jedoch bei
is_square
jeder Iteration eine teure Operation erfordert Bestätigen Sie den Wert von d . Sofern nicht im Voraus bekannt ist, dass n Faktoren in der Nähe von √ n aufweist , ist die Teilung des Versuchs wahrscheinlich schneller.Vielleicht können wir diese Beziehung noch mehr schwächen. Angenommen, wir haben ein x gewählt , so dass für
Eine vollständige Primfaktorisierung von y ist ohne weiteres bekannt. Wenn wir genug solche Beziehungen hätten, sollten wir in der Lage sein, ein adäquates d zu konstruieren , wenn wir eine Anzahl von y so wählen, dass ihr Produkt ein perfektes Quadrat ist; Das heißt, alle Primfaktoren werden gerade oft verwendet. In der Tat, wenn wir mehr haben , so y als die Gesamtzahl der einzelnen Primfaktoren sie enthalten, eine Lösung existieren garantiert; Es wird ein lineares Gleichungssystem. Es stellt sich nun die Frage, wie wir uns für ein solches x entschieden haben . Hier kommt das Sieben ins Spiel.
Das Sieb
Betrachten Sie das Polynom:
Dann gilt für jede Primzahl p und ganze Zahl k :
Dies bedeutet, dass Sie nach dem Auflösen nach den Wurzeln des Polynoms mod p - das heißt, Sie haben ein x so gefunden, dass y (x) ≡ 0 (mod p) , ergo y durch p teilbar ist - dann haben Sie eine unendliche Zahl gefunden von solchen x . Auf diese Weise können Sie über einen Bereich von x sieben, wobei Sie kleine Primfaktoren von y identifizieren und hoffentlich einige finden, für die alle Primfaktoren klein sind. Solche Zahlen sind als k-glatt bekannt , wobei k der größte verwendete Primfaktor ist.
Bei diesem Ansatz gibt es jedoch einige Probleme. Nicht alle Werte von x sind angemessen. Tatsächlich gibt es nur sehr wenige von ihnen, die um √ n zentriert sind . Kleinere Werte werden (aufgrund des -n- Terms) größtenteils negativ , und größere Werte werden zu groß, so dass es unwahrscheinlich ist, dass ihre Primfaktorisierung nur aus kleinen Primzahlen besteht. Es gibt eine Reihe solcher x , aber es ist sehr unwahrscheinlich, dass Sie genügend Glättungen finden, um zu einer Faktorisierung zu führen, es sei denn, das zu faktorisierende Composite ist sehr klein. Und so wird es für ein größeres n notwendig, mehrere Polynome einer gegebenen Form zu sieben .
Mehrere Polynome
Wir brauchen also mehr Polynome zum Sieben? Wie wäre es damit:
Das wird klappen Beachten Sie, dass A und B buchstäblich ein ganzzahliger Wert sein können und die Mathematik weiterhin gilt. Wir müssen nur ein paar zufällige Werte auswählen, nach der Wurzel des Polynoms suchen und die Werte nahe Null sieben. An diesem Punkt könnten wir es einfach gut genug nennen: Wenn Sie genug Steine in zufällige Richtungen werfen, müssen Sie früher oder später ein Fenster zerbrechen.
Es gibt aber auch ein Problem damit. Wenn die Steigung des Polynoms am x-Achsenabschnitt groß ist, was dann der Fall ist, wenn es nicht relativ flach ist, gibt es nur wenige geeignete Werte zum Sieben pro Polynom. Es wird funktionieren, aber am Ende werden Sie eine ganze Reihe von Polynomen sieben, bevor Sie das bekommen, was Sie brauchen. Können wir es besser machen?
Wir können es besser machen. Eine Beobachtung als Ergebnis von Montgomery lautet wie folgt: Wenn A und B so gewählt sind, dass es ein gewisses C gibt, das zufriedenstellend ist
dann kann das gesamte Polynom als umgeschrieben werden
Wenn außerdem A als perfektes Quadrat gewählt wird, kann der führende A- Term beim Sieben vernachlässigt werden, was zu viel kleineren Werten und einer viel flacheren Kurve führt. Damit eine solche Lösung existiert, muss n ein quadratischer Rest mod √ A sein , der sofort durch Berechnung des Legendre-Symbols erkannt werden kann :
( n | √ A ) = 1 . Beachten Sie, dass zur Lösung von B eine vollständige Primfaktorisierung von √A bekannt sein muss (um die modulare Quadratwurzel √n (mod √A) zu erhalten ), weshalb √A normalerweise als Primzahl gewählt wird.
Es kann dann gezeigt werden, dass wenn , dann für alle Werte von x ∈ [ -M, M ] :
Und jetzt haben wir endlich alle notwendigen Komponenten, um unser Sieb zu implementieren. Oder wir?
Mächte der Primzahlen als Faktoren
Unser Sieb hat, wie oben beschrieben, einen Hauptfehler. Es kann identifizieren, welche Werte von x zu einem durch p teilbaren y führen , es kann jedoch nicht identifizieren, ob dieses y durch eine Potenz von p teilbar ist oder nicht . Um dies festzustellen, müssten wir den zu siebenden Wert probeweise teilen, bis er nicht mehr durch p teilbar ist . Wir schienen einen Stillstand erreicht zu haben: Der ganze Punkt des Siebs war so, dass wir das nicht tun mussten. Zeit, das Spielbuch zu überprüfen.
Das sieht ziemlich nützlich aus. Wenn die Summe der ln aller kleinen Primfaktoren von y nahe am erwarteten Wert von ln (y) liegt , ist es fast selbstverständlich, dass y keine anderen Faktoren hat. Wenn wir den erwarteten Wert ein wenig nach unten korrigieren, können wir außerdem Werte als glatt identifizieren, die mehrere Potenzen von Primzahlen als Faktoren haben. Auf diese Weise können wir das Sieb als "Vorsieb" -Prozess verwenden und nur die Werte berücksichtigen, die wahrscheinlich glatt sind.
Dies hat auch einige andere Vorteile. Beachten Sie, dass kleine Primzahlen sehr wenig zur beitragen ln Summe, aber doch erfordern sie die meiste Zeit Sieb. Das Sieben des Werts 3 erfordert mehr Zeit als 11, 13, 17, 19 und 23 zusammen . Stattdessen können wir einfach die ersten Primzahlen überspringen und den Schwellenwert entsprechend anpassen, vorausgesetzt, ein bestimmter Prozentsatz von ihnen wäre bestanden.
Ein weiteres Ergebnis ist, dass eine Reihe von Werten "durchrutschen" kann, die größtenteils glatt sind, aber einen einzigen großen Cofaktor enthalten. Wir könnten diese Werte einfach verwerfen, aber nehmen wir an, wir haben einen anderen größtenteils glatten Wert mit genau demselben Cofaktor gefunden. Wir können diese beiden Werte dann verwenden, um ein verwendbares y zu konstruieren ; Da ihr Produkt diesen großen Cofaktor im Quadrat enthält, muss er nicht mehr berücksichtigt werden.
Alles zusammen
Das Letzte, was wir tun müssen, ist, diese Werte von y zu verwenden, um ein angemessenes x und d zu konstruieren . Angenommen, wir betrachten nur die nichtquadratischen Faktoren von y , dh die Primfaktoren einer ungeraden Potenz. Dann kann jedes y folgendermaßen ausgedrückt werden:
was in der Matrixform ausgedrückt werden kann:
Das Problem wird dann, einen Vektor v zu finden, so dass vM = ⦳ (mod 2) , wobei ⦳ der Nullvektor ist. Das heißt, nach dem linken Nullraum von M zu lösen . Dies kann auf verschiedene Arten geschehen, wobei die einfachste darin besteht, die Gaußsche Eliminierung an M T durchzuführen und die Zeilenadditionsoperation durch eine Zeilenxor- Operation zu ersetzen . Dies führt zu einer Anzahl von Nullraum-Basisvektoren, von denen jede Kombination eine gültige Lösung ergibt.
Die Konstruktion von x ist ziemlich einfach. Es ist einfach das Produkt von Ax + B für jedes der verwendeten y . Die Konstruktion von d ist etwas komplizierter. Wenn wir das Produkt von allen y nehmen , erhalten wir einen Wert mit Zehntausenden, wenn nicht Hunderttausenden von Ziffern, für die wir die Quadratwurzel finden müssen. Diese Berechnung ist unpraktisch teuer. Stattdessen können wir die geraden Potenzen von Primzahlen während des Siebprozesses verfolgen und dann and und xor- Operationen für die Vektoren von nicht quadratischen Faktoren verwenden, um die Quadratwurzel zu rekonstruieren.
Ich habe anscheinend die maximale Zeichenanzahl von 30000 erreicht. Ahh gut, ich nehme an, das ist gut genug.
quelle
Nun, Ihre 38! +1 haben mein PHP-Skript gebrochen, nicht sicher warum. Tatsächlich bricht jedes Semi-Prime mit mehr als 16 Ziffern mein Skript.
Unter Verwendung von 8980935344490257 (86028157 * 104395301) verwaltete mein Skript auf meinem Heimcomputer (2,61 GHz AMD Phenom 9950) eine Zeit von 25,963 Sekunden . Viel schneller als mein Arbeitscomputer, der fast 31 Sekunden bei 2,93 GHz Core 2 Duo war.
PHP - 757 Zeichen inkl. neue Zeilen
Es würde mich interessieren, diesen Algorithmus in c oder einer anderen kompilierten Sprache zu sehen.
quelle
lcm(2, 3, 5, 7) == 210
sich das durch diese Faktoren eliminierte Zahlenmuster alle 210 Zahlen wiederholt und nur 48 übrig bleiben. Auf diese Weise können Sie 77% aller Zahlen aus der Probedivision entfernen, anstatt die 50%, indem Sie nur Quoten nehmen.