Schneller Weg, um n zu berechnen! mod m wobei m prim ist?
War ich neugierig, ob es war eine gute Möglichkeit, dies zu tun. Mein Aktueller code ist so etwas wie:
def factorialMod(n, modulus):
ans=1
for i in range(1,n+1):
ans = ans * i % modulus
return ans % modulus
Aber es scheint ziemlich langsam zu sein!
Ich kann auch nicht berechnen n! und dann gelten die prime modulus da manchmal n ist so groß, dass n! ist nur nicht möglich, explizit zu berechnen.
Stieß ich auch auf http://en.wikipedia.org/wiki/Stirling%27s_approximation und Frage mich, ob diese überhaupt verwendet werden kann hier in gewisser Weise?
Oder, wie kann ich erstellen Sie eine rekursive, memoized-Funktion in C++?
Kommentar zu dem Problem
Wie groß ist
n
? Beliebig große
Wie langsam ist langsam? Von Ihrem pseudocode, ich schlussfolgern, du bist computing dieses in Python, ist das richtig?
Es ist eine sehr schnelle Möglichkeit, dies zu tun mit Invarianten Multiplikation oder vielleicht Montgomery-Reduktion. Beide Methoden beseitigen, der E-Modul und ermöglicht eine loop-unrolling Techniken.
Sie können brechen-Modul in die wichtigsten Faktoren, um die Fälle zu identifizieren, die null leichter, obwohl das hilft nicht für die große prime - Faktoren, wie hilfreich dieser ist, hängt davon ab, was Sie wissen über den E-Modul, wenn überhaupt, und wenn prime factorisation kitzelt Ihre Phantasie.
InformationsquelleAutor der Frage John Smith | 2012-03-15
Du musst angemeldet sein, um einen Kommentar abzugeben.
Erweitern mein Kommentar zu einer Antwort:
Ja, da gibt es effizientere Wege dies zu tun. Aber Sie sind extrem chaotisch.
So, es sei denn, Sie wirklich brauchen, dass zusätzliche Leistung, die ich nicht empfehlen zu versuchen, diese.
Der Schlüssel ist zu beachten, dass das Modul (die im wesentlichen aus einer division) wird der Engpass. Glücklicherweise gibt es ein paar sehr schnelle algorithmen, die es ermöglichen, Sie zu führen-Modul über die gleiche Anzahl viele Male.
Diese Methoden sind schnell, da Sie im wesentlichen zu beseitigen, der E-Modul.
Diese Methoden allein sollte Sie eine moderate Beschleunigung. Um wirklich effizient ist, müssen Sie möglicherweise zu entrollen Sie die Schleife, um ermöglichen eine bessere IPC:
Etwas wie dieses:
aber unter Berücksichtigung der für eine ungerade Anzahl der Iterationen und kombinieren Sie es mit einer der Methoden, die ich im Zusammenhang mit oben.
Manche mögen argumentieren, dass die loop-unrolling sollte den compiler. Ich werde counter-argumentieren, dass die Compiler sind derzeit nicht schlau genug, zu entrollen dieser besonderen Schleife. Einen genaueren Blick und Sie werden sehen, warum.
Beachten Sie, dass, obwohl meine Antwort ist sprachunabhängig, es ist in Erster Linie für C oder C++.
InformationsquelleAutor der Antwort Mysticial
Gut,
n
können nicht willkürlich großen - wennn >= m
, dannn! ≡ 0 (mod m)
(weilm
ist einer der Faktoren, durch die definition der Fakultät).Vorausgesetzt
n << m
benötigen Sie genaue Wert, Ihren Algorithmus kann gar nicht schneller, meines Wissens nach. Allerdings, wennn > m/2
können, verwenden Sie die folgende Identität (Wilson ' s theorem - Danke @Daniel Fischer!)cap die Anzahl der Multiplikationen bei etwa
m-n
Dies gibt uns eine einfache Möglichkeit zur Berechnung
n! (mod m)
imm-n-1
Multiplikationen, plus ein modulare inverse:Können wir umformulieren der obigen Gleichung auf eine andere Weise, die möglicherweise oder möglicherweise nicht ausführen etwas schneller. Anhand der folgenden Identität:
können wir formulieren die Gleichung
Dies kann in Python geschrieben wie folgt:
Wenn Sie nicht brauchen, ein genaue Wert, wird das Leben ein bisschen leichter - Sie können Stirling ' s approximation zum berechnen einer ungefähren Wert in
O(log n)
Zeit (mit Potenzierung durch Quadratur).Schließlich sollte ich erwähnen, dass, wenn diese zeitkritisch ist und du bist mit Python, versuchen Sie die Umstellung zu C++. Aus persönlicher Erfahrung, Sie sollten erwarten, dass etwa eine Größenordnung höhere Geschwindigkeit oder mehr, einfach deshalb, weil dies genau die Art von CPU-bound tight-Schleife, die nativ kompilierten code zeichnet sich bei (auch, aus welchem Grund auch immer, GMP scheint viel feiner abgestimmt als Python-Bignum).
InformationsquelleAutor der Antwort BlueRaja - Danny Pflughoeft
n! mod m berechnet werden kann in O(n1/2 + ε) - Operationen anstelle des naiven O(n). Dies erfordert die Verwendung von FFT-Polynom-Multiplikation, und lohnt sich nur für sehr große n, z.B. n > 104.
Gliederung des Algorithmus und einige timings kann hier gesehen werden: http://fredrikj.net/blog/2012/03/factorials-mod-n-and-wilsons-theorem/
InformationsquelleAutor der Antwort Fredrik Johansson
Wenn wir berechnen wollen
M = a*(a+1) * ... * (b-1) * b (mod p)
können wir den folgenden Ansatz, wenn wir annehmen, können wir addieren, subtrahieren und multiplizieren schnell (mod p), und erhalten eine Laufzeit Komplexität vonO( sqrt(b-a) * polylog(b-a) )
.Zur Vereinfachung wird angenommen, dass
(b-a+1) = k^2
ist ein Quadrat. Jetzt können wir teilen unser Produkt in k Teile, D. H.M = [a*..*(a+k-1)] *...* [(b-k+1)*..*b]
. Jeder der Faktoren in diesem Produkt ist von der formp(x)=x*..*(x+k-1)
für entsprechendex
.Durch die Verwendung eines schnellen Algorithmus zur Matrixmultiplikation von Polynomen, wie Schönhage–Strassen-Algorithmus, in einem divide & conquer Weise, kann man die Koeffizienten des Polynoms
p(x) in O( k * polylog(k) )
. Nun, offenbar gibt es einen Algorithmus für das ersetzenk
Punkte im gleichen Maß-k Polynom inO( k * polylog(k) )
, was bedeutet, können wir berechnenp(a), p(a+k), ..., p(b-k+1)
schnell.Dieser Algorithmus der Substitution viele Punkte in ein Polynom beschrieben wird in dem Buch "Primzahlen" von C. Pomerance und R. Crandall. Schließlich, wenn Sie diese
k
Werte, Sie können multiplizieren Sie Sie inO(k)
und den gewünschten Wert.Beachten Sie, dass alle unsere Operationen wurden
(mod p)
.Die genaue Laufzeit ist
O(sqrt(b-a) * log(b-a)^2 * log(log(b-a)))
.InformationsquelleAutor der Antwort ohad
Erweiterung zu meinem Kommentar, das dauert etwa 50% der Zeit für alle n in [100, 100007], wo m=(117 | 1117):
InformationsquelleAutor der Antwort Andrew Morton
Wenn n = (m - 1) für prime-m dann durch http://en.wikipedia.org/wiki/Wilson's_theorem n! mod m = (m - 1)
Auch, wie schon darauf hingewiesen, n! mod m = 0, wenn n > m
InformationsquelleAutor der Antwort Philip Riebold
Fand ich dieses folgende Funktion auf quora:
Mit f(n,m) = n! mod m;
Wahrscheinlich schlagen mit einer zeitraubenden Schleife und die Multiplikation großer Anzahl gespeichert im string. Auch ist es für jede ganze Zahl m ist.
Den link wo ich diese Funktion : https://www.quora.com/How-do-you-calculate-n-mod-m-where-n-is-in-the-1000s-and-m-is-a-very-large-prime-number-eg-n-1000-m-10-9+7
InformationsquelleAutor der Antwort Cong Tran An
Dieser Algorithmus hat
O(n log log(n))
preprocessing-Zeit Komplexität (durch Sieb) und O(r(n)) Speicherplatz-Komplexität, wor(n)
ist die prime counting function. Nach der preprocessing -, Abfrage-x! wox <= N
istO(r(x) * log(x))
.Leider wird dies nicht machbar für 1010 da r(1e9) ist 455,052,511 die fast 2GB Speicher zum speichern der Primzahlen. Für 109 aber, wir brauchten nur rund 300MB Speicher.
Angenommen mod ist 1e9+7 um zu vermeiden, überlauf in C++, aber es kann alles sein.
Eine Art zu berechnen N! mod m schnell
Berechnen N! schnell, können wir zerlegen N in seine Primfaktoren. Es kann beobachtet werden, dass wenn p eine Primzahl Faktor von N!, p muss <= N, da N! ist ein Produkt der ganzen zahlen von 1 bis N. in Anbetracht dieser Tatsachen, können wir berechnen N! mit nur Primzahlen von 2 bis N (inklusive). Nach Wikipedia, es gibt 50,847,534 ungefähr (~5 * 10^7) Primzahlen kleiner oder gleich 109. Dies wäre die Formel zu verwenden:
vp(N!) ist die größte Zahl, so dass pvp(N!) teilt N!. Legendre-Formel berechnet vp(N!) in O(log(N)) Zeit. Hier ist die Formel aus Wikipedia.
fast_power(x, y)
gibt xy % mod O(log(y)) Zeit mit exponeniation durch Quadratur.Generierung Von Primzahlen
Ich bin Generierung von Primzahlen mit Sieb des Eratosthenes. In meiner Implementierung des Sieb, ich bin mit bitset um Speicher zu sparen. Meine Umsetzung dauerte 16-18seconds zum generieren von Primzahlen bis zu 10^9 wenn ich kompiliert habe, mit -O3 flag mit einer 2. Gen i3 Prozessor. Ich bin sicher, es gibt bessere algorithmen/Implementierungen für die Generierung von Primzahlen.
Speicherverbrauch
Laut Wikipedia, es gibt 50,847,534 Primzahlen, die kleiner oder gleich 10^9. Da ein 32-bit-Ganzzahl aus 4 bytes, die prime-Vektor muss 203.39 MB (50,847,534 * 4 bytes). Das Sieb bitset Bedürfnisse (125 MB) 10^9 bits.
Leistung
War ich in der Lage zu berechnen 1,000,000,000! in 1,17 Sekunden nach der Verarbeitung.
Hinweis: ich hab den Algorithmus mit dem -O3 flag aktiviert.
InformationsquelleAutor der Antwort Mercado
Unter der Annahme, dass die "mod" - operator von Ihrem gewählten Plattform ist ausreichend schnell, Sie sind begrenzt in Erster Linie durch die Geschwindigkeit, mit der Sie berechnen können
n!
und der Raum Sie zur Verfügung haben, berechnen Sie es.Dann ist es im wesentlichen eine 2-Schritt Bedienung:
Es gibt keine Notwendigkeit, complexify Dinge, vor allem, wenn die Geschwindigkeit ist die kritische Komponente. Im Allgemeinen, wie einige Vorgänge innerhalb der Schleife, wie Sie können.
Wenn Sie brauchen, um zu berechnen
n! mod m
wiederholt, dann möchten Sie vielleicht, um memoize die Werte kommen, die Funktion zu tun, die Berechnungen. Wie immer, es ist das klassische Raum - /Zeit-Kompromiss, aber die lookup-Tabellen sind sehr schnell.Schließlich können Sie kombinieren memoization mit Rekursion (und Trampoline sowie, wenn nötig), um die Dinge wirklich schnell.
InformationsquelleAutor der Antwort cdeszaq