Pitão, 83 82 bytes
=eAQM.^GHQKf%=/H=2;1=gftgT/Q;1HJg~gGHh/H2WtG=*J=gT^2t-K=Kfq1gG^2T1=%*G=^T2Q;hS%_BJ
Suíte de teste
Este programa implementa o algoritmo Tonelli-Shanks . Eu escrevi seguindo de perto a página da Wikipedia. Toma como entrada (n, p)
.
A ausência de uma raiz quadrada é relatada pelo seguinte erro:
TypeError: pow() 3rd argument not allowed unless all arguments are integers
Este é um código muito complexo, escrito no estilo imperativo, em oposição ao estilo funcional mais comum de Pyth.
O único aspecto sutil do Pyth que estou usando é o =
qual, se não for imediatamente seguido por uma variável, pesquisa no futuro a próxima variável no programa, atribui o resultado da expressão a seguir a essa variável e retorna esse resultado. Vou me referir em toda a explicação à página da wikipedia: algoritmo Tonelli-Shanks , pois esse é o algoritmo que estou implementando.
Explicação:
=eAQ
A
pega uma tupla de 2 como entrada e atribui os valores a G
e H
respectivamente, e retorna sua entrada. Q
é a entrada inicial. e
retorna o último elemento de uma sequência. Após esse trecho, G
é n
e H
e Q
é p
.
M.^GHQ
M
define uma função de 2 entradas g
, onde as entradas são G
e H
. .^
é a rápida função de exponenciação modular de Pyth. Esse snippet define g
como significando mod de exponenciação Q
.
Kf%=/H=2;1
f
define um loop de repetição até falso e retorna o número de iterações para as quais é executado, fornecido 1
como sua entrada. Durante cada iteração do loop, dividimos H
por 2, configuramos H
para esse valor e verificamos se o resultado é ímpar. Uma vez que paramos. K
armazena o número de iterações realizadas.
Uma coisa muito complicada é a parte =2;
. =
procura a próxima variável, que é T
, então, T
é definida como 2. No entanto, T
dentro de um f
loop está o contador de iterações, então usamos ;
para obter o valor do T
ambiente global. Isso é feito para salvar alguns bytes de espaço em branco que seriam necessários para separar os números.
Após esse trecho, K
é S
do artigo da wikipedia (wiki) e H
é Q
do wiki, e T
é 2
.
=gftgT/Q;1H
Agora, precisamos encontrar um mod quadrático sem resíduo p
. Forçaremos isso usando o critério de Euler. /Q2
é (p-1)/2
, uma vez que /
é a divisão com piso, ftgT/Q;1
encontra o primeiro número inteiro T
onde T ^ ((p-1)/2) != 1
, conforme desejado. Lembre-se de que ;
extrai novamente T
do ambiente global, que ainda é 2. Esse resultado é z
do wiki.
Em seguida, para criar a c
partir do wiki, precisamos z^Q
, então envolvemos o item acima g ... H
e atribuímos o resultado a T
. Agora T
é c
do wiki.
Jg~gGHh/H2
Vamos separar o seguinte: ~gGH
. ~
é como =
, mas retorna o valor original da variável, não seu novo valor. Assim, ele retorna G
, que é n
do wiki.
Isso atribui J
o valor de n^((Q+1)/2)
, que é R
do wiki.
Agora, o seguinte entra em vigor:
~gGH
Isso atribui G
o valor n^Q
, que é t
do wiki.
Agora, temos nossas variáveis de loop configuradas. M, c, t, R
do wiki são K, T, G, J
.
O corpo do loop é complicado, então eu vou apresentá-lo com o espaço em branco, da maneira que escrevi:
WtG
=*J
=
gT^2
t-
K
=Kfq1gG^2T1
=%*G=^T2Q;
Primeiro, verificamos se G
é 1. Se sim, saímos do loop.
O próximo código que é executado é:
=Kfq1gG^2T1
Aqui, procuramos o primeiro valor de i
tal modo que G^(2^i) mod Q = 1
, começando em 1. O resultado é salvo em K
.
=gT^2t-K=Kfq1gG^2T1
Aqui, pegamos o valor antigo de K
, subtraímos o novo valor de K
, subtraímos 1, aumentamos 2 para esse poder e depois aumentamos T
para esse mod de poder Q
e depois atribuímos o resultado a T
. Isso torna T
igual ao b
do wiki.
Essa também é a linha que encerra o loop e falha se não houver solução, porque nesse caso o novo valor de K
será igual ao antigo valor de K
2 será aumentado para -1
e a exponenciação modular gerará um erro.
=*J
Em seguida, multiplicamos J
pelo resultado acima e o armazenamos novamente J
, mantendo-o R
atualizado.
=^T2
Em seguida, agrupamos T
e armazenamos o resultado novamente T
, T
retornando ao c
wiki.
=%*G=^T2Q
Então multiplicamos G
por esse resultado, pegamos o mod Q
e armazenamos o resultado novamente G
.
;
E encerramos o loop.
Depois que o loop termina, J
existe uma raiz quadrada de n
mod p
. Para encontrar o menor, usamos o seguinte código:
hS%_BJ
_BJ
cria a lista J
e sua negação, %
assume implicitamente Q
como seu segundo argumento e usa o comportamento padrão de Pyth para aplicar % ... Q
a cada membro da sequência. Em seguida, S
classifica a lista e h
leva seu primeiro membro, o mínimo.