Quel est le moyen le plus efficace pour calculer les factorielles modulo un nombre premier?

20

Connaissez-vous un algorithme qui calcule efficacement la factorielle après module?

Par exemple, je veux programmer:

for(i=0; i<5; i++)
  sum += factorial(p-i) % p;

Mais, pest un grand nombre (premier) pour appliquer directement factorielle .(p108)

En Python, cette tâche est vraiment facile, mais je veux vraiment savoir comment optimiser.

jonaprieto
la source
6
On dirait que le problème veut que vous utilisiez le théorème de Wilson. Pour premier , (p-1)! = -1 \ mod p . Donc sans utiliser de langage de programmation: la réponse est 100 . Vous souhaitez peut-être généraliser votre problème? p100(p1)!=1modp100
Aryabhata
5
Pouvez-vous énoncer le problème plus clairement? Voulez-vous calculer (X!) (mod (X+1)), ou le plus général (X!) (mod Y)? Et je suppose que factorial(100!)cela ne signifie pas vraiment que vous souhaitez appliquer la fonction factorielle deux fois.
Keith Thompson
1
Même si vous n'aviez pas le théorème de Wilson, vous en avez (mn)modp=(mmodp)(nmodp) , ce qui permettrait au moins d'éviter les problèmes de débordement.
Dave Clarke
8
Notez que le théorème de Wilson ne s'applique que lorsque p est premier. Votre question ne dit pas que p est premier, donc ce que vous avez écrit n'est pas correct.
Dave Clarke

Réponses:

11

(Cette réponse a été initialement publiée par le demandeur Jonaprieto à l'intérieur de la question.)

Je me souviens du théorème de Wilson et j'ai remarqué de petites choses:

Dans le programme ci-dessus, il vaut mieux écrire:

(p1)!1(modp)(p2)!(p1)!(p1)11(modp)(p3)!(p2)!(p2)1(p2)1(modp)(p4)!(p3)!(p3)1(p2)1(p3)1(modp) (p5)!(p4)!(p4)1(p2)1(p3)1(p4)1(modp)

Et vous pouvez trouver parce que , donc avec l' algorithme euclidien étendu, vous pouvez trouver la valeur de , qui est le module inverse. pgcd ( p , p - i ) = 1 ( p - i ) - 1(pi)1gcd(p,pi)=1(pi)1

Vous pouvez aussi voir les mêmes congruences, comme: donc, la somme est égale: et si vous factorisez au début les factorielles vous obtenez Et, le tour est joué, le module inverse est plus efficace que les factorielles. (-24)-1+(6)-1+(-2)-18(-24)-1

(p5)!(p24)1(modp)(p4)!(p+6)1(modp)(p3)!(p2)1(modp)(p2)!1(modp)(p1)!1(modp)
(24)1+(6)1+(2)1
8(24)1(modp)
Gilles
la source
Donc en gros . Soigné! (pk)!(p+(k1)!(1)k)1(modp)
Thomas Ahle
Désolé mais quand je factorise , j'obtiens: 9 ( - 24 ) - 1 = - 3(24)1+61+(2)1
9(24)1=38
1

L'exemple que vous publiez est étroitement lié au problème Euler n ° 381. Je vais donc poster une réponse qui ne résout pas le problème d'Euler. Je posterai comment vous pouvez calculer un factoriel modulo un nombre premier.

Donc: Comment calculer n! modulo p?

Observation rapide: si n ≥ p, alors n! a un facteur p, donc le résultat est 0. Très rapide. Et si nous ignorons l'exigence que p soit un nombre premier, alors q soit le plus petit facteur premier de p, et n! modulo p vaut 0 si n ≥ q. Il n'y a pas non plus beaucoup de raisons d'exiger que p soit un nombre premier pour répondre à votre question.

Maintenant dans votre exemple (n - i)! pour 1 ≤ i ≤ 5 est venu. Vous n'avez pas à calculer cinq factorielles: vous calculez (n - 5) !, multipliez par (n - 4) allez chercher (n - 4) !, multipliez par (n - 3) pour obtenir (n - 3)! etc. Cela réduit le travail de près d'un facteur 5. Ne résolvez pas le problème littéralement.

La question est de savoir comment calculer n! modulo m. La façon la plus évidente est de calculer n !, un nombre avec approximativement n log n chiffres décimaux, et de calculer le reste modulo p. C'est un travail difficile. Question: Comment pouvons-nous obtenir ce résultat plus rapidement? En ne faisant pas la chose évidente.

Nous savons que ((a * b * c) modulo p = ((((a * b) modulo p) * c) modulo p.

Pour calculer n !, nous commencerions normalement par x = 1, puis multiplierions x par 1, 2, 3, ... n. En utilisant la formule modulo, nous calculons n! modulo p sans calculer n !, en commençant par x = 1, puis pour i = 1, 2, 3, .., n on remplace x par (x * i) modulo p.

Nous avons toujours x <p et i <n, nous n'avons donc besoin que de suffisamment de précision pour calculer x * p, et non d'une précision beaucoup plus élevée pour calculer n !. Donc, pour calculer n! modulo p pour p ≥ 2 nous prenons les mesures suivantes:

Step 1: Find the smallest prime factor q of p. If n ≥ q then the result is 0.
Step 2: Let x = 1, then for 1 ≤ i ≤ n replace x with (x * i) modulo p, and x is the result. 

(Certaines réponses mentionnent le théorème de Wilson, qui ne répond à la question que dans le cas très particulier de l'exemple donné, et est très utile pour résoudre le problème d'Euler # 381, mais en général n'est pas utile pour résoudre la question qui a été posée).

gnasher729
la source
-1

Ceci est mon utilisation de mise en œuvre du théorème de Wilson:

La fonction factMOD est celle à appeler pour calculer (n!)% MOD lorsque MOD-n est peu contre n.

Est-ce que quelqu'un connaît une autre approche efficace quand ce n'est pas le cas (par exemple: n = 1e6 et MOD = 1e9 + 7)?

ll powmod(ll a, ll b){//a^b % MOD
  ll x=1,y=a;
  while(b){
    if(b&1){
      x*=y; if(x>=MOD)x%=MOD;
    }
    y*=y; if(y>=MOD)y%=MOD;
    b>>=1;
  }
  return x;
} 
ll InverseEuler(ll n){//modular inverse of n
  return powmod(n,MOD-2);
}
ll factMOD(ll n){ //n! % MOD efficient when MOD-n<n
   ll res=1,i;
   for(i=1; i<MOD-n; i++){
     res*=i;
     if(res>=MOD)res%=MOD;
   }
   res=InverseEuler(res);   
    if(!(n&1))
      res= -res +MOD;
  }
  return res%MOD;
}
JeanClaudeDaudin
la source
1
Le code n'est pas vraiment sur le sujet, ici. Une description de l'algorithme est beaucoup plus utile car elle ne nécessite pas que les gens comprennent le langage dans lequel vous avez décidé d'écrire votre code, et parce que les implémentations réelles sont souvent optimisées d'une manière qui les rend plus difficiles à comprendre. Et posez vos questions séparément, plutôt que dans votre réponse. Stack Exchange est un site de questions et réponses, pas un forum de discussion, et les questions sont difficiles à trouver si elles sont cachées dans les réponses. Merci!
David Richerby