Comment calculer le module de 5 ^ 55 module 221 sans utiliser beaucoup de calculatrice?
Je suppose qu'il existe des principes simples dans la théorie des nombres en cryptographie pour calculer de telles choses.
Bon, vous voulez donc calculer a^b mod m
. Nous allons d'abord adopter une approche naïve et ensuite voir comment nous pouvons l'affiner.
Tout d'abord, réduisez a mod m
. Cela signifie que vous devez trouver un numéro a1
pour que 0 <= a1 < m
et a = a1 mod m
. Puis à plusieurs reprises dans une boucle, multipliez par a1
et réduisez à nouveau mod m
. Ainsi, en pseudocode:
a1 = a reduced mod m
p = 1
for(int i = 1; i <= b; i++) {
p *= a1
p = p reduced mod m
}
Ainsi, nous évitons les nombres supérieurs à m^2
. Ceci est la clé. Nous évitons les nombres plus grands que m^2
parce que _ à chaque étape 0 <= p < m
et 0 <= a1 < m
.
Par exemple, calculons 5^55 mod 221
. Tout d'abord, 5
est déjà réduit mod 221
.
1 * 5 = 5 mod 221
5 * 5 = 25 mod 221
25 * 5 = 125 mod 221
125 * 5 = 183 mod 221
183 * 5 = 31 mod 221
31 * 5 = 155 mod 221
155 * 5 = 112 mod 221
112 * 5 = 118 mod 221
118 * 5 = 148 mod 221
148 * 5 = 77 mod 221
77 * 5 = 164 mod 221
164 * 5 = 157 mod 221
157 * 5 = 122 mod 221
122 * 5 = 168 mod 221
168 * 5 = 177 mod 221
177 * 5 = 1 mod 221
1 * 5 = 5 mod 221
5 * 5 = 25 mod 221
25 * 5 = 125 mod 221
125 * 5 = 183 mod 221
183 * 5 = 31 mod 221
31 * 5 = 155 mod 221
155 * 5 = 112 mod 221
112 * 5 = 118 mod 221
118 * 5 = 148 mod 221
148 * 5 = 77 mod 221
77 * 5 = 164 mod 221
164 * 5 = 157 mod 221
157 * 5 = 122 mod 221
122 * 5 = 168 mod 221
168 * 5 = 177 mod 221
177 * 5 = 1 mod 221
1 * 5 = 5 mod 221
5 * 5 = 25 mod 221
25 * 5 = 125 mod 221
125 * 5 = 183 mod 221
183 * 5 = 31 mod 221
31 * 5 = 155 mod 221
155 * 5 = 112 mod 221
112 * 5 = 118 mod 221
118 * 5 = 148 mod 221
148 * 5 = 77 mod 221
77 * 5 = 164 mod 221
164 * 5 = 157 mod 221
157 * 5 = 122 mod 221
122 * 5 = 168 mod 221
168 * 5 = 177 mod 221
177 * 5 = 1 mod 221
1 * 5 = 5 mod 221
5 * 5 = 25 mod 221
25 * 5 = 125 mod 221
125 * 5 = 183 mod 221
183 * 5 = 31 mod 221
31 * 5 = 155 mod 221
155 * 5 = 112 mod 221
Par conséquent, 5^55 = 112 mod 221
.
Maintenant, nous pouvons améliorer cela en utilisant exponentiation par quadrature ; C’est le fameux truc dans lequel nous réduisons l’exponentiation à la nécessité de ne multiplier que par log b
multiplications au lieu de b
. Notez qu'avec l'algorithme que j'ai décrit ci-dessus, l'exponentiation par amélioration au carré, vous vous retrouvez avec le méthode binaire de droite à gauche .
a1 = a reduced mod m
p = 1
while (b > 0) {
if (b is odd) {
p *= a1
p = p reduced mod m
}
b /= 2
a1 = (a1 * a1) reduced mod m
}
Ainsi, depuis 55 = 110111 en binaire
1 * (5^1 mod 221) = 5 mod 221
5 * (5^2 mod 221) = 125 mod 221
125 * (5^4 mod 221) = 112 mod 221
112 * (5^16 mod 221) = 112 mod 221
112 * (5^32 mod 221) = 112 mod 221
Par conséquent, la réponse est 5^55 = 112 mod 221
. Cela fonctionne parce que
55 = 1 + 2 + 4 + 16 + 32
pour que
5^55 = 5^(1 + 2 + 4 + 16 + 32) mod 221
= 5^1 * 5^2 * 5^4 * 5^16 * 5^32 mod 221
= 5 * 25 * 183 * 1 * 1 mod 221
= 22875 mod 221
= 112 mod 221
Dans l'étape où nous calculons 5^1 mod 221
, 5^2 mod 221
, etc., nous notons que 5^(2^k)
= 5^(2^(k-1)) * 5^(2^(k-1))
car 2^k = 2^(k-1) + 2^(k-1)
afin que nous puissions tout d'abord calculer 5^1
et réduire mod 221
, puis cocher ceci et réduisez mod 221
pour obtenir 5^2 mod 221
, etc.
L'algorithme ci-dessus formalise cette idée.
Pour ajouter à la réponse de Jason:
Vous pouvez accélérer le processus (ce qui peut être utile pour les très grands exposants) en utilisant le développement binaire de l'exposant. Calculez d’abord 5, 5, 2, 5, 4, 5, 8 mod 221 - vous le faites en répétant des quadratures:
5^1 = 5(mod 221)
5^2 = 5^2 (mod 221) = 25(mod 221)
5^4 = (5^2)^2 = 25^2(mod 221) = 625 (mod 221) = 183(mod221)
5^8 = (5^4)^2 = 183^2(mod 221) = 33489 (mod 221) = 118(mod 221)
5^16 = (5^8)^2 = 118^2(mod 221) = 13924 (mod 221) = 1(mod 221)
5^32 = (5^16)^2 = 1^2(mod 221) = 1(mod 221)
Maintenant nous pouvons écrire
55 = 1 + 2 + 4 + 16 + 32
so 5^55 = 5^1 * 5^2 * 5^4 * 5^16 * 5^32
= 5 * 25 * 625 * 1 * 1 (mod 221)
= 125 * 625 (mod 221)
= 125 * 183 (mod 183) - because 625 = 183 (mod 221)
= 22875 ( mod 221)
= 112 (mod 221)
Vous pouvez voir comment, pour de très grands exposants, ce sera beaucoup plus rapide (je crois que c'est log par opposition à linéaire en b, mais pas certain)
/* The algorithm is from the book "Discrete Mathematics and Its
Applications 5th Edition" by Kenneth H. Rosen.
(base^exp)%mod
*/
int modular(int base, unsigned int exp, unsigned int mod)
{
int x = 1;
int power = base % mod;
for (int i = 0; i < sizeof(int) * 8; i++) {
int least_sig_bit = 0x00000001 & (exp >> i);
if (least_sig_bit)
x = (x * power) % mod;
power = (power * power) % mod;
}
return x;
}
5^55 mod221
= ( 5^10 * 5^10 * 5^10 * 5^10 * 5^10 * 5^5) mod221
= ( ( 5^10) mod221 * 5^10 * 5^10 * 5^10 * 5^10 * 5^5) mod221
= ( 77 * 5^10 * 5^10 * 5^10 * 5^10 * 5^5) mod221
= ( ( 77 * 5^10) mod221 * 5^10 * 5^10 * 5^10 * 5^5) mod221
= ( 183 * 5^10 * 5^10 * 5^10 * 5^5) mod221
= ( ( 183 * 5^10) mod221 * 5^10 * 5^10 * 5^5) mod221
= ( 168 * 5^10 * 5^10 * 5^5) mod221
= ( ( 168 * 5^10) mod 221 * 5^10 * 5^5) mod221
= ( 118 * 5^10 * 5^5) mod221
= ( ( 118 * 5^10) mod 221 * 5^5) mod221
= ( 25 * 5^5) mod221
= 112
Cela fait partie du code que j'ai créé pour la validation IBAN. N'hésitez pas à utiliser.
static void Main(string[] args)
{
int modulo = 97;
string input = Reverse("100020778788920323232343433");
int result = 0;
int lastRowValue = 1;
for (int i = 0; i < input.Length; i++)
{
// Calculating the modulus of a large number Wikipedia http://en.wikipedia.org/wiki/International_Bank_Account_Number
if (i > 0)
{
lastRowValue = ModuloByDigits(lastRowValue, modulo);
}
result += lastRowValue * int.Parse(input[i].ToString());
}
result = result % modulo;
Console.WriteLine(string.Format("Result: {0}", result));
}
public static int ModuloByDigits(int previousValue, int modulo)
{
// Calculating the modulus of a large number Wikipedia http://en.wikipedia.org/wiki/International_Bank_Account_Number
return ((previousValue * 10) % modulo);
}
public static string Reverse(string input)
{
char[] arr = input.ToCharArray();
Array.Reverse(arr);
return new string(arr);
}
Théorème du reste chinois vient à l’esprit comme point initial comme 221 = 13 * 17. Donc, divisez cela en 2 parties qui se combinent à la fin, une pour le mod 13 et une pour le mod 17. Deuxièmement, je crois il existe des preuves de ^ (p-1) = 1 mod p pour tout non a nul, ce qui permet également de réduire votre problème, car 5 ^ 55 devient 5 ^ 3 pour le cas mod 13 et 13 * 4 = 52. Si vous examinez le sujet "Champs finis", vous obtiendrez peut-être de bons résultats pour résoudre ce problème.
EDIT: La raison pour laquelle je mentionne les facteurs est que cela crée un moyen de factoriser zéro en éléments non nuls, comme si vous essayiez quelque chose comme 13 ^ 2 * 17 ^ 4 mod 221, la réponse est zéro puisque 13 * 17 = 221. Un grand nombre de grands nombres ne seront pas des nombres premiers, bien qu'il existe des moyens de trouver des nombres premiers de grande taille, car ils sont beaucoup utilisés en cryptographie et dans d'autres domaines de la mathématique.
Ce que vous recherchez, c'est une exponentiation modulaire, en particulier une exponentiation binaire modulaire. Ce lien wikipedia a un pseudocode.
La réponse de Jason en Java (note i < exp
).
private static void testModulus() {
int bse = 5, exp = 55, mod = 221;
int a1 = bse % mod;
int p = 1;
System.out.println("1. " + (p % mod) + " * " + bse + " = " + (p % mod) * bse + " mod " + mod);
for (int i = 1; i < exp; i++) {
p *= a1;
System.out.println((i + 1) + ". " + (p % mod) + " * " + bse + " = " + ((p % mod) * bse) % mod + " mod " + mod);
p = (p % mod);
}
}
Cela s'appelle une exponentiation modulaire ( https://en.wikipedia.org/wiki/Modular_exponentiation ).
Supposons que vous avez l'expression suivante:
19 ^ 3 mod 7
Au lieu d’alimenter directement 19, vous pouvez effectuer les opérations suivantes:
(((19 mod 7) * 19) mod 7) * 19) mod 7
Mais cela peut aussi prendre beaucoup de temps en raison de nombreuses multipliations séquentielles et vous pouvez donc multiplier sur des valeurs au carré:
x mod N -> x ^ 2 mod N -> x ^ 4 mod -> ... x ^ 2 |log y| mod N
L’algorithme d’exponentiation modulaire suppose que:
x ^ y == (x ^ |y/2|) ^ 2 if y is even
x ^ y == x * ((x ^ |y/2|) ^ 2) if y is odd
Ainsi, l'algorithme d'exponentiation modulaire récursif ressemblera à ceci en Java:
/**
* Modular exponentiation algorithm
* @param x Assumption: x >= 0
* @param y Assumption: y >= 0
* @param N Assumption: N > 0
* @return x ^ y mod N
*/
public static long modExp(long x, long y, long N) {
if(y == 0)
return 1 % N;
long z = modExp(x, Math.abs(y/2), N);
if(y % 2 == 0)
return (long) ((Math.pow(z, 2)) % N);
return (long) ((x * Math.pow(z, 2)) % N);
}
Un merci spécial à @chux pour l'erreur trouvée avec une valeur de retour incorrecte en cas de comparaison entre y et 0.
Il suffit de fournir une autre implémentation de la réponse de Jason par C.
Après avoir discuté avec mes camarades de classe, à partir des explications de Jason, j'aime mieux la version récursive si vous vous souciez peu de la performance:
Par exemple:
#include<stdio.h>
int mypow( int base, int pow, int mod ){
if( pow == 0 ) return 1;
if( pow % 2 == 0 ){
int tmp = mypow( base, pow >> 1, mod );
return tmp * tmp % mod;
}
else{
return base * mypow( base, pow - 1, mod ) % mod;
}
}
int main(){
printf("%d", mypow(5,55,221));
return 0;
}