Pyth, 83 82 octets
=eAQM.^GHQKf%=/H=2;1=gftgT/Q;1HJg~gGHh/H2WtG=*J=gT^2t-K=Kfq1gG^2T1=%*G=^T2Q;hS%_BJ
Suite de tests
Ce programme implémente l' algorithme Tonelli-Shanks . Je l'ai écrit en suivant de près la page Wikipedia. Il faut en entrée (n, p)
.
L'absence d'une racine carrée est signalée par l'erreur suivante:
TypeError: pow() 3rd argument not allowed unless all arguments are integers
Il s'agit d'un code très complexe, écrit dans le style impératif, par opposition au style fonctionnel plus courant de Pyth.
Le seul aspect subtil de Pyth que j'utilise est celui =
qui, s'il n'est pas immédiatement suivi d'une variable, recherche dans le programme la variable suivante, puis assigne le résultat de l'expression suivante à cette variable, puis renvoie ce résultat. Je ferai référence tout au long de l'explication à la page wikipedia: algorithme Tonelli-Shanks , car c'est l'algorithme que j'implémente.
Explication:
=eAQ
A
prend un tuple à 2 en entrée, attribue les valeurs à G
et H
respectivement, et renvoie son entrée. Q
est l'entrée initiale. e
renvoie le dernier élément d'une séquence. Après cet extrait, G
est n
, H
et Q
est p
.
M.^GHQ
M
définit une fonction à 2 entrées g
, où les entrées sont G
et H
. .^
est la fonction d'exponentiation modulaire rapide de Pyth. Cet extrait définit g
comme signifiant le mod d'exponentiation Q
.
Kf%=/H=2;1
f
définit une répétition jusqu'à une fausse boucle et renvoie le nombre d'itérations pour lesquelles il s'exécute, donné 1
en entrée. À chaque itération de la boucle, nous divisons H
par 2, réglons H
sur cette valeur et vérifions si le résultat est impair. Une fois qu'il est, nous nous arrêtons. K
stocke le nombre d'itérations nécessaires.
Une chose très délicate est le =2;
bit. =
recherche à l'avance la variable suivante, qui est T
donc T
définie sur 2. Cependant, à l' T
intérieur d'une f
boucle se trouve le compteur d'itérations, que nous utilisons ;
pour obtenir la valeur de T
de l'environnement global. Ceci est fait pour économiser quelques octets d'espace blanc qui seraient autrement nécessaires pour séparer les nombres.
Après cet extrait, K
provient S
de l'article de wikipedia (wiki), et H
est Q
du wiki, et l' T
est 2
.
=gftgT/Q;1H
Maintenant, nous devons trouver un mod quadratique non résiduel p
. Nous allons forcer cela en utilisant le critère d'Euler. /Q2
est (p-1)/2
, puisque /
est la division au sol, ftgT/Q;1
trouve donc le premier entier T
où T ^ ((p-1)/2) != 1
, comme vous le souhaitez. Rappelez-vous que ;
tire à nouveau T
de l'environnement mondial, qui est toujours 2. Ce résultat est z
du wiki.
Ensuite, pour créer à c
partir du wiki, nous avons besoin z^Q
, donc nous enveloppons ce qui précède g ... H
et attribuons le résultat à T
. Maintenant, T
c'est c
du wiki.
Jg~gGHh/H2
Séparons ce: ~gGH
. ~
est similaire =
, mais renvoie la valeur d'origine de la variable, pas sa nouvelle valeur. Ainsi, il revient G
, qui provient n
du wiki.
Cela affecte J
la valeur de n^((Q+1)/2)
, qui provient R
du wiki.
Maintenant, ce qui suit prend effet:
~gGH
Cela affecte G
la valeur n^Q
, qui provient t
du wiki.
Maintenant, nous avons nos variables de boucle configurées. M, c, t, R
du wiki sont K, T, G, J
.
Le corps de la boucle est compliqué, donc je vais le présenter avec les espaces, comme je l'ai écrit:
WtG
=*J
=
gT^2
t-
K
=Kfq1gG^2T1
=%*G=^T2Q;
Tout d'abord, nous vérifions si G
est 1. Si oui, nous quittons la boucle.
Le code suivant qui s'exécute est:
=Kfq1gG^2T1
Ici, nous recherchons la première valeur de i
telle sorte que G^(2^i) mod Q = 1
, à partir de 1. Le résultat est enregistré dans K
.
=gT^2t-K=Kfq1gG^2T1
Ici, nous prenons l'ancienne valeur de K
, soustrayons la nouvelle valeur de K
, soustrayons 1, augmentons 2 à cette puissance, puis augmentons T
à ce mod de puissance Q
, puis attribuons le résultat à T
. Cela T
équivaut à b
partir du wiki.
C'est également la ligne qui termine la boucle et échoue s'il n'y a pas de solution, car dans ce cas, la nouvelle valeur de K
sera égale à l'ancienne valeur de K
, 2 sera élevée à la -1
, et l'exponentiation modulaire générera une erreur.
=*J
Ensuite, nous multiplions J
par le résultat ci-dessus et le stockons à nouveau J
, en restant à R
jour.
=^T2
Ensuite, nous corrigeons T
et stockons le résultat dans T
, en T
revenant au c
wiki.
=%*G=^T2Q
Ensuite, nous multiplions G
par ce résultat, le prenons mod Q
et stockons le résultat G
.
;
Et nous terminons la boucle.
Une fois la boucle terminée, J
est une racine carrée de n
mod p
. Pour trouver le plus petit, nous utilisons le code suivant:
hS%_BJ
_BJ
crée la liste de J
et sa négation, %
prend implicitement Q
comme deuxième argument et utilise le comportement par défaut de Pyth pour s'appliquer % ... Q
à chaque membre de la séquence. S
Trie ensuite la liste et h
prend son premier membre, le minimum.