Qual è il modo più efficiente per calcolare fattoriali modulo a prime?


20

Conosci qualche algoritmo che calcola efficacemente il modulo fattoriale dopo il modulo?

Ad esempio, voglio programmare:

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

Ma pè un grande numero (primo) per l'applicazione diretta fattoriale (p108) .

In Python, questo compito è davvero semplice, ma voglio davvero sapere come ottimizzare.


6
Sembra che il problema voglia che tu usi il teorema di Wilson. Per prime p , (p1)!=1modp . Quindi senza usare alcun linguaggio di programmazione: la risposta è100 . Forse vorresti generalizzare il tuo problema?
Aryabhata,

5
Puoi affermare il problema più chiaramente? Vuoi calcolare (X!) (mod (X+1)), o più in generale (X!) (mod Y)? E presumo che factorial(100!)ciò non significhi davvero che tu voglia applicare la funzione fattoriale due volte.
Keith Thompson,

1
Anche se non avessi il teorema di Wilson, hai quella (mn)modp=(mmodp)(nmodp) , che almeno aiuterebbe a evitare problemi di overflow.
Dave Clarke,

8
Si noti che il teorema di Wilson si applica solo quando p è primo. La tua domanda non afferma che p sia primo, quindi ciò che hai scritto non è corretto.
Dave Clarke,

Risposte:


11

(Questa risposta è stata inizialmente pubblicata dal jonaprieto del richiedente all'interno della domanda.)

Ricordo il teorema di Wilson e ho notato piccole cose:

Nel programma sopra, è meglio se scrivo:

(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)

E puoi trovare perché gcd ( p , p - i ) = 1 , quindi con l' algoritmo euclideo esteso puoi trovare il valore di ( p - i(pi)1gcd(p,pi)=1 , che è il modulo inverso.(pi)1

Puoi anche vedere le stesse congruenze, come: quindi, la somma è uguale:(-24)-1+(6)-1+(-2)-1 e se si fattorizzano all'inizio i fattoriali si ottiene 8(-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
E, voilà, il modulo inverso è più efficiente dei fattoriali.
8(24)1(modp)

(pk)!(p+(k1)!(1)k)1(modp)

Scusa ma quando fattorizzo (-24)-1+6-1+(-2)-1, Ottengo :
9(-24)-1=-38

1

L'esempio che stai postando è strettamente correlato al problema n. 381 di Eulero. Quindi posterò una risposta che non risolve il problema di Eulero. Pubblicherò come calcolare i fattoriali modulo a prime.

Quindi: come calcolare n! modulo p?

Osservazione rapida: se n ≥ p, quindi n! ha un fattore p, quindi il risultato è 0. Molto veloce. E se ignoriamo il requisito che p dovrebbe essere un numero primo, allora lascia che q sia il fattore primo più piccolo di p, e n! modulo p è 0 se n ≥ q. Inoltre, non ci sono molte ragioni per richiedere che p sia un numero primo per rispondere alla tua domanda.

Ora nel tuo esempio (n - i)! per 1 ≤ i ≤ 5 è venuto fuori. Non devi calcolare cinque fattoriali: calcoli (n - 5) !, moltiplica per (n - 4) vai a ottenere (n - 4) !, moltiplica per (n - 3) per ottenere (n - 3)! ecc. Questo riduce il lavoro di quasi un fattore 5. Non risolvere il problema alla lettera.

La domanda è come calcolare n! modulo m. Il modo ovvio è calcolare n !, un numero con circa n log n cifre decimali e calcolare il resto modulo p. È un duro lavoro. Domanda: come possiamo ottenere questo risultato più velocemente? Non facendo la cosa ovvia.

Sappiamo che ((a * b * c) modulo p = (((a * b) modulo p) * c) modulo p.

Per calcolare n !, normalmente inizieremo con x = 1, quindi moltiplicheremo x per 1, 2, 3, ... n. Usando la formula del modulo, calcoliamo n! modulo p senza calcolare n !, iniziando con x = 1, quindi per i = 1, 2, 3, .., n sostituiamo x con (x * i) modulo p.

Abbiamo sempre x <p e i <n, quindi abbiamo bisogno solo di una precisione sufficiente per calcolare x * p, non della precisione molto più elevata per calcolare n !. Quindi per calcolare n! modulo p per p ≥ 2 prendiamo i seguenti passi:

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. 

(Alcune risposte menzionano il teorema di Wilson, che risponde alla domanda solo nel caso molto particolare dell'esempio fornito, ed è molto utile per risolvere il problema di Eulero # 381, ma in generale non è utile per risolvere la domanda che è stata posta).


-1

Questo è il mio uso dell'implementazione del teorema di Wilson:

La funzione factMOD è quella da chiamare per calcolare (n!)% MOD quando MOD-n è piccolo rispetto a n.

Qualcuno conosce un altro approccio efficiente quando non è il caso (ad esempio: n = 1e6 e 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;
}

1
Il codice non è proprio in tema, qui. Una descrizione dell'algoritmo è molto più utile perché non richiede alle persone di capire in quale lingua hai deciso di scrivere il codice e perché le implementazioni effettive sono spesso ottimizzate in modo da renderle più difficili da comprendere. E per favore, poni le tue domande come domande separate, piuttosto che nella tua risposta. Stack Exchange è un sito di domande e risposte, non un forum di discussione, e le domande sono difficili da trovare se sono nascoste tra le risposte. Grazie!
David Richerby,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.