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
Aprend un tuple à 2 en entrée, attribue les valeurs à Get Hrespectivement, et renvoie son entrée. Qest l'entrée initiale. erenvoie le dernier élément d'une séquence. Après cet extrait, Gest n, Het Qest p.
M.^GHQ
Mdéfinit une fonction à 2 entrées g, où les entrées sont Get H. .^est la fonction d'exponentiation modulaire rapide de Pyth. Cet extrait définit gcomme signifiant le mod d'exponentiation Q.
Kf%=/H=2;1
fdéfinit une répétition jusqu'à une fausse boucle et renvoie le nombre d'itérations pour lesquelles il s'exécute, donné 1en entrée. À chaque itération de la boucle, nous divisons Hpar 2, réglons Hsur cette valeur et vérifions si le résultat est impair. Une fois qu'il est, nous nous arrêtons. Kstocke 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 Tdonc Tdéfinie sur 2. Cependant, à l' Tintérieur d'une fboucle se trouve le compteur d'itérations, que nous utilisons ;pour obtenir la valeur de Tde 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, Kprovient Sde l'article de wikipedia (wiki), et Hest Qdu wiki, et l' Test 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. /Q2est (p-1)/2, puisque /est la division au sol, ftgT/Q;1trouve donc le premier entier Toù T ^ ((p-1)/2) != 1, comme vous le souhaitez. Rappelez-vous que ;tire à nouveau Tde l'environnement mondial, qui est toujours 2. Ce résultat est zdu wiki.
Ensuite, pour créer à cpartir du wiki, nous avons besoin z^Q, donc nous enveloppons ce qui précède g ... Het attribuons le résultat à T. Maintenant, Tc'est cdu 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 ndu wiki.
Cela affecte Jla valeur de n^((Q+1)/2), qui provient Rdu wiki.
Maintenant, ce qui suit prend effet:
~gGH
Cela affecte Gla valeur n^Q, qui provient tdu wiki.
Maintenant, nous avons nos variables de boucle configurées. M, c, t, Rdu 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 Gest 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 itelle 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 à bpartir 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 Ksera é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 Jpar le résultat ci-dessus et le stockons à nouveau J, en restant à Rjour.
=^T2
Ensuite, nous corrigeons Tet stockons le résultat dans T, en Trevenant au cwiki.
=%*G=^T2Q
Ensuite, nous multiplions Gpar ce résultat, le prenons mod Qet stockons le résultat G.
;
Et nous terminons la boucle.
Une fois la boucle terminée, Jest une racine carrée de nmod p. Pour trouver le plus petit, nous utilisons le code suivant:
hS%_BJ
_BJcrée la liste de Jet sa négation, %prend implicitement Qcomme deuxième argument et utilise le comportement par défaut de Pyth pour s'appliquer % ... Qà chaque membre de la séquence. STrie ensuite la liste et hprend son premier membre, le minimum.