Pyth, 83 82 bytes
=eAQM.^GHQKf%=/H=2;1=gftgT/Q;1HJg~gGHh/H2WtG=*J=gT^2t-K=Kfq1gG^2T1=%*G=^T2Q;hS%_BJ
Banco de pruebas
Este programa implementa el algoritmo Tonelli-Shanks . Lo escribí siguiendo de cerca la página de Wikipedia. Se toma como entrada (n, p)
.
El siguiente error informa la ausencia de una raíz cuadrada:
TypeError: pow() 3rd argument not allowed unless all arguments are integers
Este es un código muy complejo, escrito en el estilo imperativo, en oposición al estilo funcional más común de Pyth.
El único aspecto sutil de Pyth que estoy usando es =
que, si no es seguido inmediatamente por una variable, busca en el programa la siguiente variable, luego asigna el resultado de la siguiente expresión a esa variable, luego devuelve ese resultado. Me referiré a lo largo de la explicación a la página de Wikipedia: el algoritmo Tonelli-Shanks , ya que ese es el algoritmo que estoy implementando.
Explicación:
=eAQ
A
toma una tupla de 2 como entrada, y asigna los valores a G
y H
respectivamente, y devuelve su entrada. Q
es la entrada inicial e
Devuelve el último elemento de una secuencia. Después de este fragmento, G
es n
y H
y Q
son p
.
M.^GHQ
M
define una función de 2 entradas g
, donde están las entradas G
y H
. .^
es la función de exponenciación modular rápida de Pyth. Este fragmento se define g
como mod de exponenciación media Q
.
Kf%=/H=2;1
f
define una repetición hasta el bucle falso y devuelve el número de iteraciones para las que se ejecuta, dado 1
como su entrada. Durante cada iteración del ciclo, dividimos H
por 2, establecemos H
ese valor y verificamos si el resultado es impar. Una vez que es, nos detenemos. K
almacena el número de iteraciones que esto tomó.
Una cosa muy complicada es la =2;
parte. =
busca la siguiente variable, que es T
, por lo que T
se establece en 2. Sin embargo, T
dentro de un f
ciclo está el contador de iteraciones, por lo que usamos ;
para obtener el valor del T
entorno global. Esto se hace para guardar un par de bytes de espacios en blanco que de otra forma serían necesarios para separar los números.
Después de este fragmento, K
es S
del artículo de wikipedia (wiki), y H
es Q
del wiki, y T
es 2
.
=gftgT/Q;1H
Ahora, necesitamos encontrar un mod cuadrático no residual p
. Fuerza bruta esto usando el criterio de Euler. /Q2
es decir (p-1)/2
, dado que /
está dividida en pisos, entonces ftgT/Q;1
encuentra el primer número entero T
donde T ^ ((p-1)/2) != 1
, según lo deseado. Recordemos que ;
nuevamente se extrae T
del entorno global, que todavía es 2. Este resultado es z
de la wiki.
Luego, para crear c
desde el wiki, necesitamos z^Q
, así que envolvemos lo anterior g ... H
y le asignamos el resultado T
. Ahora T
es c
de la wiki.
Jg~gGHh/H2
Vamos a separar a cabo esto: ~gGH
. ~
es como =
, pero devuelve el valor original de la variable, no su nuevo valor. Por lo tanto, regresa G
, que es n
de la wiki.
Esto asigna J
el valor de n^((Q+1)/2)
, que es R
de la wiki.
Ahora, surte efecto lo siguiente:
~gGH
Esto asigna G
el valor n^Q
, que es t
de la wiki.
Ahora, tenemos nuestras variables de bucle configuradas. M, c, t, R
de la wiki son K, T, G, J
.
El cuerpo del bucle es complicado, así que lo presentaré con el espacio en blanco, tal como lo escribí:
WtG
=*J
=
gT^2
t-
K
=Kfq1gG^2T1
=%*G=^T2Q;
Primero, verificamos si G
es 1. Si es así, salimos del ciclo.
El siguiente código que se ejecuta es:
=Kfq1gG^2T1
Aquí, buscamos el primer valor de i
tal que G^(2^i) mod Q = 1
, comenzando en 1. El resultado se guarda en K
.
=gT^2t-K=Kfq1gG^2T1
Aquí, tomamos el valor anterior de K
, restamos el nuevo valor de K
, restamos 1, aumentamos 2 a ese poder, y luego aumentamos T
a ese mod de poder Q
, y luego asignamos el resultado a T
. Esto hace que sea T
igual a b
la wiki.
Esta es también la línea que termina el ciclo y falla si no hay solución, porque en ese caso el nuevo valor de K
será igual al valor anterior de K
2 -1
, y la exponenciación modular generará un error.
=*J
A continuación, multiplicamos J
por el resultado anterior y lo almacenamos nuevamente J
, manteniéndonos R
actualizados.
=^T2
Luego cuadramos T
y almacenamos el resultado nuevamente T
, T
volviendo a c
la wiki.
=%*G=^T2Q
Luego multiplicamos G
por ese resultado, tomamos mod Q
y almacenamos el resultado nuevamente G
.
;
Y terminamos el ciclo.
Una vez finalizado el ciclo, J
hay una raíz cuadrada de n
mod p
. Para encontrar el más pequeño, utilizamos el siguiente código:
hS%_BJ
_BJ
crea la lista J
y su negación, %
toma implícitamente Q
su segundo argumento y usa el comportamiento predeterminado de Pyth para aplicar % ... Q
a cada miembro de la secuencia. Luego S
ordena la lista y h
toma su primer miembro, el mínimo.