Pyth, 83 82 byte
=eAQM.^GHQKf%=/H=2;1=gftgT/Q;1HJg~gGHh/H2WtG=*J=gT^2t-K=Kfq1gG^2T1=%*G=^T2Q;hS%_BJ
Suite di test
Questo programma implementa l' algoritmo Tonelli-Shanks . L'ho scritto seguendo da vicino la pagina di Wikipedia. Ci vuole come input (n, p)
.
L'assenza di una radice quadrata è segnalata dal seguente errore:
TypeError: pow() 3rd argument not allowed unless all arguments are integers
Questo è un codice molto complicato, scritto nello stile imperativo, in contrapposizione allo stile funzionale più comune di Pyth.
L'unico aspetto sottile di Pyth che sto usando è =
, che, se non immediatamente seguito da una variabile, cerca in avanti nel programma la variabile successiva, quindi assegna il risultato della seguente espressione a quella variabile, quindi restituisce quel risultato. Farò riferimento in tutta la spiegazione alla pagina di Wikipedia: algoritmo Tonelli-Shanks , in quanto è l'algoritmo che sto implementando.
Spiegazione:
=eAQ
A
prende un 2-tuple come input, e assegna i valori G
e H
, rispettivamente, e restituisce il suo ingresso. Q
è l'input iniziale. e
restituisce l'ultimo elemento di una sequenza. Dopo questo frammento, G
è n
, H
e Q
sono p
.
M.^GHQ
M
definisce una funzione a 2 ingressi g
, dove sono gli ingressi G
e H
. .^
è la funzione esponenziale modulare rapida di Pyth. Questo frammento definisce g
mod esponenziale Q
.
Kf%=/H=2;1
f
definisce una ripetizione fino a false loop e restituisce il numero di iterazioni per cui viene eseguita, dato 1
come input. Durante ogni iterazione del loop, dividiamo H
per 2, impostiamo H
su quel valore e controlliamo se il risultato è dispari. Una volta che lo è, ci fermiamo. K
memorizza il numero di iterazioni necessarie.
Una cosa molto delicata è la parte =2;
. =
cerca in anticipo la prossima variabile, che è T
, quindi T
è impostata su 2. Tuttavia, T
all'interno di un f
ciclo si trova il contatore di iterazioni, quindi usiamo ;
per ottenere il valore T
dall'ambiente globale. Questo viene fatto per salvare un paio di byte di spazio bianco che altrimenti sarebbero necessari per separare i numeri.
Dopo questo frammento, K
viene S
dall'articolo di Wikipedia (wiki), ed H
è Q
dal wiki, e lo T
è 2
.
=gftgT/Q;1H
Ora, dobbiamo trovare un mod quadratico non residuo p
. Lo faremo forzare brutalmente usando il criterio di Eulero. /Q2
è (p-1)/2
, poiché /
è la divisione floored, quindi ftgT/Q;1
trova il primo numero intero T
dove T ^ ((p-1)/2) != 1
, come desiderato. Ricordiamo che ;
tira ancora T
dall'ambiente globale, che è ancora 2. Questo risultato è z
dal wiki.
Quindi, per creare c
dal wiki, abbiamo bisogno z^Q
, quindi avvolgiamo quanto sopra g ... H
e assegniamo il risultato a T
. Ora T
è c
dal wiki.
Jg~gGHh/H2
Diamo separano questo: ~gGH
. ~
è come =
, ma restituisce il valore originale della variabile, non il suo nuovo valore. Quindi, ritorna G
, che n
proviene dal wiki.
Questo assegna J
il valore di n^((Q+1)/2)
, che R
proviene dal wiki.
Ora, ha effetto:
~gGH
Questo assegna G
il valore n^Q
, che t
proviene dal wiki.
Ora, abbiamo impostato le nostre variabili loop. M, c, t, R
dal wiki sono K, T, G, J
.
Il corpo del loop è complicato, quindi lo presenterò con lo spazio bianco, come l'ho scritto:
WtG
=*J
=
gT^2
t-
K
=Kfq1gG^2T1
=%*G=^T2Q;
Innanzitutto, controlliamo se G
è 1. In tal caso, usciamo dal loop.
Il prossimo codice che viene eseguito è:
=Kfq1gG^2T1
Qui, cerchiamo il primo valore di i
tale che G^(2^i) mod Q = 1
, a partire da 1. Il risultato viene salvato in K
.
=gT^2t-K=Kfq1gG^2T1
Qui, prendiamo il vecchio valore di K
, sottraggiamo il nuovo valore di K
, sottraggiamo 1, aumentiamo 2 a quella potenza, quindi aumentiamo T
a quella mod potenza Q
, quindi assegniamo il risultato a T
. Ciò T
equivale a b
dal wiki.
Questa è anche la linea che termina il ciclo e fallisce se non c'è soluzione, perché in quel caso il nuovo valore di K
sarà uguale al vecchio valore di K
, 2 sarà elevato a -1
, e l'esponente modulare genererà un errore.
=*J
Successivamente, moltiplichiamo J
per il risultato sopra riportato e lo memorizziamo nuovamente J
, mantenendoci R
aggiornati.
=^T2
Quindi quadriamo T
e memorizziamo il risultato T
, T
tornando a c
dal wiki.
=%*G=^T2Q
Quindi moltiplichiamo G
per quel risultato, prendiamo mod Q
e memorizziamo il risultato G
.
;
E terminiamo il ciclo.
Dopo che il ciclo è finito, J
è una radice quadrata di n
mod p
. Per trovare il più piccolo, utilizziamo il seguente codice:
hS%_BJ
_BJ
crea l'elenco di J
e la sua negazione, %
prende implicitamente Q
come secondo argomento e usa il comportamento predefinito di Pyth da applicare % ... Q
a ciascun membro della sequenza. Quindi S
ordina l'elenco e h
accetta il suo primo membro, il minimo.