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
A
nimmt eine 2-Tupel als Eingabe, und ordnet die Werte G
und H
bzw. und kehrt seine Eingabe. Q
ist die anfängliche Eingabe. e
Gibt das letzte Element einer Sequenz zurück. Nach diesem Ausschnitt G
ist n
und H
und Q
sind p
.
M.^GHQ
M
definiert eine 2-Eingangsfunktion g
, bei der die Eingänge G
und sind H
. .^
ist Pyths schnelle modulare Exponentiationsfunktion. Dieses Snippet definiert g
als Exponentiation Mod Q
.
Kf%=/H=2;1
f
Definiert eine Wiederholung bis zur falschen Schleife und gibt die Anzahl der Iterationen zurück, für die sie ausgeführt wird, angegeben 1
als Eingabe. Während jeder Iteration der Schleife teilen wir H
durch 2, setzen H
diesen Wert und prüfen, ob das Ergebnis ungerade ist. Sobald es soweit ist, hören wir auf. K
speichert die Anzahl der Iterationen, die dies dauerte.
Eine sehr schwierige Sache ist das =2;
bisschen. =
sucht im Voraus nach der nächsten Variablen, die T
also T
auf 2 gesetzt ist. In T
einer f
Schleife befindet sich jedoch der Iterationszähler, mit dem wir ;
den Wert von T
aus 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 K
ist S
aus dem Wikipedia-Artikel (Wiki) und H
ist Q
aus dem Wiki, und T
ist 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. /Q2
ist , (p-1)/2
da /
Boden Teilung ist, so dass ftgT/Q;1
die erste ganze Zahl findet , T
wo T ^ ((p-1)/2) != 1
, wie gewünscht. Denken Sie daran, dass dies ;
wieder T
aus der globalen Umgebung stammt, die immer noch 2 ist. Dieses Ergebnis stammt z
aus dem Wiki.
Als nächstes c
müssen z^Q
wir aus dem Wiki erstellen , also wickeln wir das Obige ein g ... H
und weisen das Ergebnis zu T
. Jetzt T
kommt c
aus 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 n
aus dem Wiki stammt.
Dies weist J
den Wert von zu n^((Q+1)/2)
, der R
aus dem Wiki stammt.
Nun wird Folgendes wirksam:
~gGH
Dies weist G
den Wert zu n^Q
, der t
aus dem Wiki stammt.
Jetzt haben wir unsere Schleifenvariablen eingerichtet. M, c, t, R
aus 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 G
ist. Wenn ja, verlassen wir die Schleife.
Der nächste Code, der ausgeführt wird, ist:
=Kfq1gG^2T1
Hier suchen wir nach dem ersten Wert , i
so 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 T
zu dieser Potenzmodifikation Q
und weisen dann das Ergebnis zu T
. Das macht T
gleich b
aus 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 K
gleich dem alten Wert von ist K
, 2 auf das erhöht -1
wird und die modulare Exponentiation einen Fehler auslöst.
=*J
Als nächstes multiplizieren wir J
mit dem obigen Ergebnis und speichern es zurück J
, wobei wir es auf dem neuesten R
Stand halten .
=^T2
Dann haben wir Platz T
und das Ergebnis zurück in T
, Einstellung T
zurück zu c
aus dem Wiki.
=%*G=^T2Q
Dann multiplizieren wir G
mit diesem Ergebnis, nehmen den Mod Q
und speichern das Ergebnis wieder in G
.
;
Und wir beenden die Schleife.
Nachdem die Schleife vorbei ist, J
ist eine Quadratwurzel von n
mod p
. Um den kleinsten zu finden, verwenden wir den folgenden Code:
hS%_BJ
_BJ
Erstellt die Liste J
und deren Negation, %
nimmt implizit Q
als zweites Argument und wendet das Standardverhalten von Pyth % ... Q
auf jedes Mitglied der Sequenz an. Dann S
sortiert die Liste und h
nimmt sein erstes Mitglied, das Minimum.