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
Aprende un 2-tuple come input, e assegna i valori Ge H, rispettivamente, e restituisce il suo ingresso. Qè l'input iniziale. erestituisce l'ultimo elemento di una sequenza. Dopo questo frammento, Gè n, He Qsono p.
M.^GHQ
Mdefinisce una funzione a 2 ingressi g, dove sono gli ingressi Ge H. .^è la funzione esponenziale modulare rapida di Pyth. Questo frammento definisce gmod esponenziale Q.
Kf%=/H=2;1
fdefinisce una ripetizione fino a false loop e restituisce il numero di iterazioni per cui viene eseguita, dato 1come input. Durante ogni iterazione del loop, dividiamo Hper 2, impostiamo Hsu quel valore e controlliamo se il risultato è dispari. Una volta che lo è, ci fermiamo. Kmemorizza 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, Tall'interno di un fciclo si trova il contatore di iterazioni, quindi usiamo ;per ottenere il valore Tdall'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, Kviene Sdall'articolo di Wikipedia (wiki), ed Hè Qdal 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;1trova il primo numero intero Tdove T ^ ((p-1)/2) != 1, come desiderato. Ricordiamo che ;tira ancora Tdall'ambiente globale, che è ancora 2. Questo risultato è zdal wiki.
Quindi, per creare cdal wiki, abbiamo bisogno z^Q, quindi avvolgiamo quanto sopra g ... He assegniamo il risultato a T. Ora Tè cdal 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 nproviene dal wiki.
Questo assegna Jil valore di n^((Q+1)/2), che Rproviene dal wiki.
Ora, ha effetto:
~gGH
Questo assegna Gil valore n^Q, che tproviene dal wiki.
Ora, abbiamo impostato le nostre variabili loop. M, c, t, Rdal 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 itale 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 Ta quella mod potenza Q, quindi assegniamo il risultato a T. Ciò Tequivale a bdal wiki.
Questa è anche la linea che termina il ciclo e fallisce se non c'è soluzione, perché in quel caso il nuovo valore di Ksarà uguale al vecchio valore di K, 2 sarà elevato a -1, e l'esponente modulare genererà un errore.
=*J
Successivamente, moltiplichiamo Jper il risultato sopra riportato e lo memorizziamo nuovamente J, mantenendoci Raggiornati.
=^T2
Quindi quadriamo Te memorizziamo il risultato T, Ttornando a cdal wiki.
=%*G=^T2Q
Quindi moltiplichiamo Gper quel risultato, prendiamo mod Qe memorizziamo il risultato G.
;
E terminiamo il ciclo.
Dopo che il ciclo è finito, Jè una radice quadrata di nmod p. Per trovare il più piccolo, utilizziamo il seguente codice:
hS%_BJ
_BJcrea l'elenco di Je la sua negazione, %prende implicitamente Qcome secondo argomento e usa il comportamento predefinito di Pyth da applicare % ... Qa ciascun membro della sequenza. Quindi Sordina l'elenco e haccetta il suo primo membro, il minimo.