Raiz quadrada de um número

13

A tarefa é a seguinte: Dado um número inteiro positivo xe um prime n > x, produz o menor número inteiro positivo ytal que (y * y) mod n = x. Uma parte importante desta questão é o prazo especificado abaixo, que exclui as soluções de força bruta.

Se não houver esse valor y, seu código deverá ser exibido N.

Casos de teste

(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 e saída

Você pode receber e fornecer resultados da maneira que for conveniente. Se você não gostar da saída N, qualquer Falseyvalor será suficiente .

Restrições

Seu código deve responder a todos os casos de teste em menos de 1 minuto em uma área de trabalho padrão. Esse prazo é apenas para evitar respostas de força bruta e espero que boas respostas sejam executadas quase instantaneamente. Você não pode usar nenhuma biblioteca ou componente interno que resolva esse problema ou que teste se um número é um resíduo quadrático.


fonte
2
Portanto, os idiomas sem suporte para o tipo de dados grande inteiro são descartados. Pena
Luis Mendo
1
@LuisMendo Não, se você pode codificar o suporte para 1267650600228229401496703205653si mesmo ou se possui suporte de 128 bits, como __int128no gcc. Há também várias bibliotecas int de 256 bits disponíveis para vários idiomas. Por fim, muitos idiomas têm uma biblioteca int de precisão arbitrária.

Respostas:

7

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

Apega uma tupla de 2 como entrada e atribui os valores a Ge Hrespectivamente, e retorna sua entrada. Qé a entrada inicial. eretorna o último elemento de uma sequência. Após esse trecho, Gé ne He Qé p.

M.^GHQ

Mdefine uma função de 2 entradas g, onde as entradas são Ge H. .^é a rápida função de exponenciação modular de Pyth. Esse snippet define gcomo significando mod de exponenciação Q.

Kf%=/H=2;1

fdefine um loop de repetição até falso e retorna o número de iterações para as quais é executado, fornecido 1como sua entrada. Durante cada iteração do loop, dividimos Hpor 2, configuramos Hpara esse valor e verificamos se o resultado é ímpar. Uma vez que paramos. Karmazena 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, Tdentro de um floop está o contador de iterações, então usamos ;para obter o valor do Tambiente 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é Sdo artigo da wikipedia (wiki) e Hé Qdo 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;1encontra o primeiro número inteiro Tonde T ^ ((p-1)/2) != 1, conforme desejado. Lembre-se de que ;extrai novamente Tdo ambiente global, que ainda é 2. Esse resultado é zdo wiki.

Em seguida, para criar a cpartir do wiki, precisamos z^Q, então envolvemos o item acima g ... He atribuímos o resultado a T. Agora Té cdo 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 é ndo wiki.

Isso atribui Jo valor de n^((Q+1)/2), que é Rdo wiki.

Agora, o seguinte entra em vigor:

~gGH

Isso atribui Go valor n^Q, que é tdo wiki.

Agora, temos nossas variáveis ​​de loop configuradas. M, c, t, Rdo 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 ital 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 Tpara esse mod de poder Qe depois atribuímos o resultado a T. Isso torna Tigual ao bdo 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 Kserá igual ao antigo valor de K2 será aumentado para -1e a exponenciação modular gerará um erro.

=*J

Em seguida, multiplicamos Jpelo resultado acima e o armazenamos novamente J, mantendo-o Ratualizado.

=^T2

Em seguida, agrupamos Te armazenamos o resultado novamente T, Tretornando ao cwiki.

=%*G=^T2Q

Então multiplicamos Gpor esse resultado, pegamos o mod Qe armazenamos o resultado novamente G.

;

E encerramos o loop.

Depois que o loop termina, Jexiste uma raiz quadrada de nmod p. Para encontrar o menor, usamos o seguinte código:

hS%_BJ

_BJcria a lista Je sua negação, %assume implicitamente Qcomo seu segundo argumento e usa o comportamento padrão de Pyth para aplicar % ... Qa cada membro da sequência. Em seguida, Sclassifica a lista e hleva seu primeiro membro, o mínimo.

isaacg
fonte
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
fonte
%timeit Q(1267650600228229401496703204676,1267650600228229401496703205653) 100 loops, best of 3: 2.83 ms per loop :)
3
Que ótima resposta! Você restaurou minha fé no PPCG.
5
Desculpe a pergunta do novato, mas o que é PPCG? Grupo polonês de codificadores Python?
Reversed Engineer
@DaveBoltman Programação de quebra-cabeças e código de golfe.
orlp
3

Haskell , 326 bytes

Normalmente, eu gosto de respostas de força bruta. Como isso é fortemente desencorajado pelo prazo, aqui está a maneira mais eficiente que eu conheço:

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)

Experimente online!

Tenho certeza de que isso pode ser melhorado, mas isso deve ser feito por enquanto.

ბიმო
fonte
Você pode modificar o código TIO para que ele dê as respostas como saída? Acabei de ser "verdadeiro" no momento.
1
Experimente online!
Ørjan Johansen
@Lembik Você precisa substituir testCasesos do TIO original, que mal cabem em um comentário, mesmo sem eles.
Ørjan Johansen
@ ØrjanJohansen Muito obrigado! Ajustei minha resposta com o seu código e substituí testCases.
ბიმო
Estou vendo um bug bizarro com esse link TIO - se eu clicar nele, ele tem o código, mas não funciona nem obtém o URL nas opções de menu - mas se eu copiar o URL da barra de endereços e colá-lo em um guia diferente, então ele funciona.
Ørjan Johansen