Pyth, 83 82 Bytes
=eAQM.^GHQKf%=/H=2;1=gftgT/Q;1HJg~gGHh/H2WtG=*J=gT^2t-K=Kfq1gG^2T1=%*G=^T2Q;hS%_BJ
Testsuite
Dieses Programm implementiert den Tonelli-Shanks-Algorithmus . Ich habe es geschrieben, indem ich der Wikipedia-Seite genau gefolgt bin. Es nimmt als Eingabe (n, p).
Das Fehlen einer Quadratwurzel wird durch den folgenden Fehler gemeldet:
TypeError: pow() 3rd argument not allowed unless all arguments are integers
Dies ist ein sehr komplexer Code, der im imperativen Stil geschrieben ist, im Gegensatz zu dem allgemeineren funktionalen Stil von Pyth.
Der eine subtile Aspekt von Pyth, den ich verwende, ist =, dass, wenn nicht unmittelbar gefolgt von einer Variablen, im Programm nach der nächsten Variablen gesucht wird, dann das Ergebnis des folgenden Ausdrucks dieser Variablen zugewiesen wird und dann dieses Ergebnis zurückgegeben wird. Ich werde während der Erklärung auf die Wikipedia-Seite verweisen: Tonelli-Shanks-Algorithmus , da dies der Algorithmus ist, den ich implementiere.
Erläuterung:
=eAQ
Animmt eine 2-Tupel als Eingabe, und ordnet die Werte Gund Hbzw. und kehrt seine Eingabe. Qist die anfängliche Eingabe. eGibt das letzte Element einer Sequenz zurück. Nach diesem Ausschnitt Gist nund Hund Qsind p.
M.^GHQ
Mdefiniert eine 2-Eingangsfunktion g, bei der die Eingänge Gund sind H. .^ist Pyths schnelle modulare Exponentiationsfunktion. Dieses Snippet definiert gals Exponentiation Mod Q.
Kf%=/H=2;1
fDefiniert eine Wiederholung bis zur falschen Schleife und gibt die Anzahl der Iterationen zurück, für die sie ausgeführt wird, angegeben 1als Eingabe. Während jeder Iteration der Schleife teilen wir Hdurch 2, setzen Hdiesen Wert und prüfen, ob das Ergebnis ungerade ist. Sobald es soweit ist, hören wir auf. Kspeichert die Anzahl der Iterationen, die dies dauerte.
Eine sehr schwierige Sache ist das =2;bisschen. =sucht im Voraus nach der nächsten Variablen, die Talso Tauf 2 gesetzt ist. In Teiner fSchleife befindet sich jedoch der Iterationszähler, mit dem wir ;den Wert von Taus der globalen Umgebung abrufen. Dies geschieht, um ein paar Bytes Whitespace zu sparen, die sonst zum Trennen der Zahlen benötigt würden.
Nach diesem Snippet Kist Saus dem Wikipedia-Artikel (Wiki) und Hist Qaus dem Wiki, und Tist 2.
=gftgT/Q;1H
Jetzt müssen wir einen quadratischen Mod ohne Rückstand finden p. Wir werden dies mit dem Euler-Kriterium brachial erzwingen. /Q2ist , (p-1)/2da /Boden Teilung ist, so dass ftgT/Q;1die erste ganze Zahl findet , Two T ^ ((p-1)/2) != 1, wie gewünscht. Denken Sie daran, dass dies ;wieder Taus der globalen Umgebung stammt, die immer noch 2 ist. Dieses Ergebnis stammt zaus dem Wiki.
Als nächstes cmüssen z^Qwir aus dem Wiki erstellen , also wickeln wir das Obige ein g ... Hund weisen das Ergebnis zu T. Jetzt Tkommt caus dem Wiki.
Jg~gGHh/H2
Lassen Sie uns das trennen: ~gGH. ~ist wie =, gibt aber den ursprünglichen Wert der Variablen zurück, nicht ihren neuen Wert. Somit kehrt es zurück G, was naus dem Wiki stammt.
Dies weist Jden Wert von zu n^((Q+1)/2), der Raus dem Wiki stammt.
Nun wird Folgendes wirksam:
~gGH
Dies weist Gden Wert zu n^Q, der taus dem Wiki stammt.
Jetzt haben wir unsere Schleifenvariablen eingerichtet. M, c, t, Raus dem wiki sind K, T, G, J.
Der Text der Schleife ist kompliziert, daher werde ich das Leerzeichen so darstellen, wie ich es geschrieben habe:
WtG
=*J
=
gT^2
t-
K
=Kfq1gG^2T1
=%*G=^T2Q;
Zuerst prüfen wir, ob 1 Gist. Wenn ja, verlassen wir die Schleife.
Der nächste Code, der ausgeführt wird, ist:
=Kfq1gG^2T1
Hier suchen wir nach dem ersten Wert , iso dass G^(2^i) mod Q = 1, beginnend bei 1. Das Ergebnis in wird gespeichert K.
=gT^2t-K=Kfq1gG^2T1
Hier nehmen wir den alten Wert von K, subtrahieren den neuen Wert von K, subtrahieren 1, erhöhen 2 zu dieser Potenz und erhöhen dann Tzu dieser Potenzmodifikation Qund weisen dann das Ergebnis zu T. Das macht Tgleich baus dem Wiki.
Dies ist auch die Zeile, die die Schleife beendet und fehlschlägt, wenn es keine Lösung gibt, da in diesem Fall der neue Wert von Kgleich dem alten Wert von ist K, 2 auf das erhöht -1wird und die modulare Exponentiation einen Fehler auslöst.
=*J
Als nächstes multiplizieren wir Jmit dem obigen Ergebnis und speichern es zurück J, wobei wir es auf dem neuesten RStand halten .
=^T2
Dann haben wir Platz Tund das Ergebnis zurück in T, Einstellung Tzurück zu caus dem Wiki.
=%*G=^T2Q
Dann multiplizieren wir Gmit diesem Ergebnis, nehmen den Mod Qund speichern das Ergebnis wieder in G.
;
Und wir beenden die Schleife.
Nachdem die Schleife vorbei ist, Jist eine Quadratwurzel von nmod p. Um den kleinsten zu finden, verwenden wir den folgenden Code:
hS%_BJ
_BJErstellt die Liste Jund deren Negation, %nimmt implizit Qals zweites Argument und wendet das Standardverhalten von Pyth % ... Qauf jedes Mitglied der Sequenz an. Dann Ssortiert die Liste und hnimmt sein erstes Mitglied, das Minimum.