Pyth, 83 82 bajtów
=eAQM.^GHQKf%=/H=2;1=gftgT/Q;1HJg~gGHh/H2WtG=*J=gT^2t-K=Kfq1gG^2T1=%*G=^T2Q;hS%_BJ
Zestaw testowy
Ten program implementuje algorytm Tonelli-Shanksa . Napisałem to, uważnie śledząc stronę Wikipedii. Przyjmuje jako dane wejściowe (n, p)
.
Brak pierwiastka kwadratowego jest zgłaszany przez następujący błąd:
TypeError: pow() 3rd argument not allowed unless all arguments are integers
Jest to bardzo misternie golfowy kod, napisany w stylu imperatywnym, w przeciwieństwie do bardziej popularnego stylu funkcjonalnego Pytha.
Jedynym subtelnym aspektem Pytha, którego używam jest to =
, że jeśli nie następuje bezpośrednio po nim zmienna, szuka w programie następnej zmiennej do przodu, następnie przypisuje wynik następującego wyrażenia do tej zmiennej, a następnie zwraca ten wynik. W całym objaśnieniu będę odwoływał się do strony wikipedii: algorytm Tonelli-Shanksa , ponieważ jest to algorytm, który wdrażam.
Wyjaśnienie:
=eAQ
A
trwa 2-krotnego jako wejście i przydziela wartości G
i H
odpowiednio, i zwraca jego wejście. Q
jest początkowym wejściem. e
zwraca ostatni element sekwencji. Po tym fragmencie, G
to n
i H
i Q
są p
.
M.^GHQ
M
definiuje funkcję 2 wejść g
, gdzie wejściami są G
i H
. .^
jest szybką modułową funkcją potęgowania Pytha. Ten fragment kodu g
oznacza mod potęgowania Q
.
Kf%=/H=2;1
f
definiuje powtarzanie aż do fałszywej pętli i zwraca liczbę iteracji, dla których działa, podane 1
jako dane wejściowe. Podczas każdej iteracji pętli dzielimy H
przez 2, ustawiamy H
na tę wartość i sprawdzamy, czy wynik jest nieparzysty. Kiedy to nastąpi, przestajemy. K
przechowuje liczbę wykonanych iteracji.
Jedną z bardzo trudnych rzeczy jest =2;
nieco. =
wyszukiwania do przodu do następnej zmiennej, która jest T
, tak T
jest ustawiony na 2. Jednakże T
wewnątrz f
pętli licznik iteracji, więc używamy ;
, aby uzyskać wartość T
z globalnym środowisku. Ma to na celu zaoszczędzenie kilku bajtów białych znaków, które w innym przypadku byłyby potrzebne do oddzielenia liczb.
Po tym fragmencie K
pochodzi S
z artykułu w Wikipedii (wiki) i H
pochodzi Q
z wiki i T
jest 2
.
=gftgT/Q;1H
Teraz musimy znaleźć kwadratowy mod bez pozostałości p
. Wymusimy to brutalnie, stosując kryterium Eulera. /Q2
to (p-1)/2
, ponieważ /
jest podłogą podziału, tak ftgT/Q;1
znajdzie Pierwsza liczba T
w którym T ^ ((p-1)/2) != 1
, zgodnie z życzeniem. Przypomnij sobie, że ;
ponownie ściąga T
z globalnego środowiska, które wciąż ma 2. Ten wynik pochodzi z
z wiki.
Następnie, aby utworzyć c
z wiki, potrzebujemy z^Q
, więc zawijamy powyższe g ... H
i przypisujemy wynik T
. Teraz T
jest c
z wiki.
Jg~gGHh/H2
Załóżmy oddzielić to: ~gGH
. ~
jest podobny =
, ale zwraca oryginalną wartość zmiennej, a nie jej nową wartość. Zwraca więc, że G
pochodzi n
z wiki.
To przypisuje J
wartość n^((Q+1)/2)
, która pochodzi R
z wiki.
Teraz obowiązują następujące zasady:
~gGH
To przypisuje G
wartość n^Q
, która pochodzi t
z wiki.
Teraz mamy ustawione zmienne pętli. M, c, t, R
z wiki są K, T, G, J
.
Ciało pętli jest skomplikowane, więc przedstawię go z białą spacją, tak jak to napisałem:
WtG
=*J
=
gT^2
t-
K
=Kfq1gG^2T1
=%*G=^T2Q;
Najpierw sprawdzamy, czy G
jest 1. Jeśli tak, wychodzimy z pętli.
Kolejny działający kod to:
=Kfq1gG^2T1
Tutaj szukamy pierwszej i
takiej wartości G^(2^i) mod Q = 1
, zaczynając od 1. Wynik zostaje zapisany w K
.
=gT^2t-K=Kfq1gG^2T1
Tutaj bierzemy starą wartość K
, odejmujemy nową wartość K
, odejmujemy 1, podnosimy 2 do tej mocy, a następnie podnosimy T
do tej modów mocy Q
, a następnie przypisujemy wynik do T
. To T
równa się b
z wiki.
Jest to również linia, która kończy pętlę i kończy się niepowodzeniem, jeśli nie ma rozwiązania, ponieważ w takim przypadku nowa wartość K
będzie równa starej wartości K
, 2 zostanie podniesiona do -1
, a modułowe potęgowanie spowoduje błąd.
=*J
Następnie mnożymy J
przez powyższy wynik i przechowujemy go z powrotem J
, R
aktualizując go.
=^T2
Następnie poprawiamy T
i zapisujemy wynik z powrotem T
, ustawiając z T
powrotem c
na wiki.
=%*G=^T2Q
Następnie mnożymy G
przez ten wynik, bierzemy mod Q
i zapisujemy wynik z powrotem G
.
;
I kończymy pętlę.
Po zakończeniu pętli J
pierwiastek kwadratowy z n
mod p
. Aby znaleźć najmniejszy, używamy następującego kodu:
hS%_BJ
_BJ
tworzy listę J
i jej negację, %
domyślnie przyjmuje Q
za swój drugi argument i używa domyślnego zachowania Pytha do zastosowania % ... Q
do każdego elementu sekwencji. Następnie S
sortuje listę i h
bierze pierwszego członka, minimum.