Raíz cuadrada de un número

13

La tarea es la siguiente: dado un entero positivo xy un primo n > x, genera el entero positivo más pequeño de ytal manera (y * y) mod n = x. Una parte importante de esta pregunta es el límite de tiempo especificado a continuación que excluye las soluciones de fuerza bruta.

Si no existe dicho valor y, su código debería salir N.

Casos de prueba

(2, 5, N), 
(3, 5, N), 
(4, 5, 2),
(524291, 1048583, N),
(529533, 1048583, N),
(534775, 1048583, 436853),
(540017, 1048583, 73675),
(536870913, 1073741827, 375394238),
(542239622, 1073741827, 267746399),
(547608331, 1073741827, N),
(552977040, 1073741827, 104595351),
(1099511627676, 1099511627791, N),
(1099511627677, 1099511627791, 269691261521),
(1099511627678, 1099511627791, 413834069585),
(1267650600228229401496703204376, 1267650600228229401496703205653, 5312823546347991512233563776),
(1267650600228229401496703204476, 1267650600228229401496703205653, N)
(1267650600228229401496703204576, 1267650600228229401496703205653, N)
(1267650600228229401496703204676, 1267650600228229401496703205653, 79905476259539917812907012931)

Entrada y salida

Puede tomar entrada y dar salida de cualquier manera que sea conveniente. Si no le gusta la salida N, cualquier Falseyvalor lo hará.

Restricciones

Su código debe responder a todos los casos de prueba en menos de 1 minuto en un escritorio estándar. Este límite de tiempo es solo para evitar respuestas de fuerza bruta y espero que las buenas respuestas se ejecuten casi instantáneamente. No puede usar ninguna biblioteca o biblioteca que resuelva este problema o que pruebe si un número es un residuo cuadrático.


fuente
2
Por lo tanto, se descartan idiomas sin soporte para el tipo de datos de entero grande. Lástima
Luis Mendo
1
@LuisMendo No si puedes codificar el soporte para 1267650600228229401496703205653ti o si tienes soporte de 128 bits como __int128en gcc. También hay una serie de bibliotecas int de 256 bits para varios idiomas. Por último, muchos idiomas tienen una biblioteca int de precisión arbitraria.

Respuestas:

7

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.

isaacg
fuente
11

Python 2, 166 bytes

def Q(x,n,a=0):
 e=n/2
 while pow(a*a-x,e,n)<2:a+=1
 w=a*a-x;b=r=a;c=s=1
 while e:
    if e%2:r,s=(r*b+s*c*w)%n,r*c+s*b
    b,c=(b*b+c*c*w)%n,2*b*c;e/=2
 return min(r,-r%n)
orlp
fuente
%timeit Q(1267650600228229401496703204676,1267650600228229401496703205653) 100 loops, best of 3: 2.83 ms per loop :)
3
¡Qué gran respuesta! Has restaurado mi fe en PPCG.
55
Disculpe la pregunta de novato, pero ¿qué es PPCG? Grupo de codificadores de Python polaco?
Ingeniero invertido
@DaveBoltman Puzzles de programación y golf de código.
orlp
3

Haskell , 326 bytes

Por lo general, me gustan las respuestas de fuerza bruta. Como el límite de tiempo no recomienda esto, esta es la forma más eficiente que conozco:

r p=((\r->min(mod(-r)p)r)$).f p
f p x|p==2=x|q x=f 0 0|mod p 4==3=x&div(p+1)4|let(r,s)=foldl i(p-1,0)[1..t 1o]=m$x&(d$r+1)*(b&d s)where q a=1/=a&o;m=(`mod`p);a&0=1;a&e|even e=m$a&d e^2|0<1=m$(a&(e-1))*a;b=[n|n<-[2..],q n]!!0;i(a,b)_|m(x&d a*b&d b)==p-1=(d a,d b+o)|0<1=(d a,d b);o=d p;t y x|even x=t(y+1)(d x)|0<1=y;d=(`div`2)

Pruébalo en línea!

Estoy seguro de que esto se puede reducir aún más, pero esto debería hacerlo por ahora.

ბიმო
fuente
¿Podría modificar el código TIO para que dé las respuestas como salida? Acabo de obtener "Verdadero" en este momento.
1
Pruébalo en línea!
Ørjan Johansen
@Lembik Debe reemplazarlo testCasescon los del TIO original, apenas cabe en un comentario, incluso sin ellos.
Ørjan Johansen
@ ØrjanJohansen ¡Muchas gracias! Ajusté mi respuesta con su código y lo reemplacé testCases.
ბიმო
Eh, estoy viendo un extraño error con ese enlace TIO: si hago clic en él, tiene el código pero no funciona ni obtengo la URL de las opciones del menú, pero si copio la URL de la barra de direcciones y la pego en un pestaña diferente, entonces funciona.
Ørjan Johansen