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
Atoma una tupla de 2 como entrada, y asigna los valores a Gy Hrespectivamente, y devuelve su entrada. Qes la entrada inicial eDevuelve el último elemento de una secuencia. Después de este fragmento, Ges ny Hy Qson p.
M.^GHQ
Mdefine una función de 2 entradas g, donde están las entradas Gy H. .^es la función de exponenciación modular rápida de Pyth. Este fragmento se define gcomo mod de exponenciación media Q.
Kf%=/H=2;1
fdefine una repetición hasta el bucle falso y devuelve el número de iteraciones para las que se ejecuta, dado 1como su entrada. Durante cada iteración del ciclo, dividimos Hpor 2, establecemos Hese valor y verificamos si el resultado es impar. Una vez que es, nos detenemos. Kalmacena 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 Tse establece en 2. Sin embargo, Tdentro de un fciclo está el contador de iteraciones, por lo que usamos ;para obtener el valor del Tentorno 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, Kes Sdel artículo de wikipedia (wiki), y Hes Qdel wiki, y Tes 2.
=gftgT/Q;1H
Ahora, necesitamos encontrar un mod cuadrático no residual p. Fuerza bruta esto usando el criterio de Euler. /Q2es decir (p-1)/2, dado que /está dividida en pisos, entonces ftgT/Q;1encuentra el primer número entero Tdonde T ^ ((p-1)/2) != 1, según lo deseado. Recordemos que ;nuevamente se extrae Tdel entorno global, que todavía es 2. Este resultado es zde la wiki.
Luego, para crear cdesde el wiki, necesitamos z^Q, así que envolvemos lo anterior g ... Hy le asignamos el resultado T. Ahora Tes cde 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 nde la wiki.
Esto asigna Jel valor de n^((Q+1)/2), que es Rde la wiki.
Ahora, surte efecto lo siguiente:
~gGH
Esto asigna Gel valor n^Q, que es tde la wiki.
Ahora, tenemos nuestras variables de bucle configuradas. M, c, t, Rde 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 Ges 1. Si es así, salimos del ciclo.
El siguiente código que se ejecuta es:
=Kfq1gG^2T1
Aquí, buscamos el primer valor de ital 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 Ta ese mod de poder Q, y luego asignamos el resultado a T. Esto hace que sea Tigual a bla 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 Kserá igual al valor anterior de K2 -1, y la exponenciación modular generará un error.
=*J
A continuación, multiplicamos Jpor el resultado anterior y lo almacenamos nuevamente J, manteniéndonos Ractualizados.
=^T2
Luego cuadramos Ty almacenamos el resultado nuevamente T, Tvolviendo a cla wiki.
=%*G=^T2Q
Luego multiplicamos Gpor ese resultado, tomamos mod Qy almacenamos el resultado nuevamente G.
;
Y terminamos el ciclo.
Una vez finalizado el ciclo, Jhay una raíz cuadrada de nmod p. Para encontrar el más pequeño, utilizamos el siguiente código:
hS%_BJ
_BJcrea la lista Jy su negación, %toma implícitamente Qsu segundo argumento y usa el comportamiento predeterminado de Pyth para aplicar % ... Qa cada miembro de la secuencia. Luego Sordena la lista y htoma su primer miembro, el mínimo.