n peut être arbitrairement grand
Eh bien, n
ne peut pas être arbitrairement grand - si n >= m
, alors n! ≡ 0 (mod m)
(parce qu' m
est l'un des facteurs, par la définition de la factorielle).
En supposant n << m
et vous avez besoin d'une exacte valeur, votre algorithme ne peut aller plus vite, à ma connaissance. Toutefois, si n > m/2
, vous pouvez utiliser l'identité suivante (le théorème de Wilson - Merci @Daniel Fischer!)
pour limiter le nombre de multiplications à propos de m-n
(m-1)! ≡ -1 (mod m)
1 * 2 * 3 * ... * (n-1) * n * (n+1) * ... * (m-2) * (m-1) ≡ -1 (mod m)
n! * (n+1) * ... * (m-2) * (m-1) ≡ -1 (mod m)
n! ≡ -[(n+1) * ... * (m-2) * (m-1)]-1 (mod m)
Cela nous donne une façon simple de calculer n! (mod m)
en m-n-1
multiplications, plus modulaire inverse:
def factorialMod(n, module):
ans=1
si n <= module//2:
#calculer la factorielle normalement (à droite argument de gamme() est exclusive)
for i in range(1,n+1):
ans = (ans * i) % module
autre chose:
#Fancypants méthode pour n grand
for i in range(n+1,le module):
ans = (ans * i) % module
ans = modinv(sna, le module)
ans = -1*ans + module
retour sna % module
Nous pouvons reformuler l'équation ci-dessus, d'une autre façon, qui peut ou peut-effectuez pas un peu plus rapide. À l'aide de l'identité suivante:
nous pouvons reformuler l'équation
n! ≡ -[(n+1) * ... * (m-2) * (m-1)]-1 (mod m)
n! ≡ -[(n+1-m) * ... * (m-2-m) * (m-1-m)]-1 (mod m)
(inverser l'ordre des termes)
n! ≡ -[(-1) * (-2) * ... * -(m-n-2) * -(m-n-1)]-1 (mod m)
n! ≡ -[(1) * (2) * ... * (m-n-2) * (m-n-1) * (-1)(m-n-1)]-1 (mod m)
n! ≡ [(m-n-1)!]-1 * (-1)(m-n) (mod m)
Cela peut être écrit en Python comme suit:
def factorialMod(n, module):
ans=1
si n <= module//2:
#calculer la factorielle normalement (à droite argument de gamme() est exclusive)
for i in range(1,n+1):
ans = (ans * i) % module
autre chose:
#Fancypants méthode pour n grand
for i in range(1,module-n):
ans = (ans * i) % module
ans = modinv(sna, le module)
#Puisque m est impair-le premier, (-1)^(m-n) = -1 si n est pair, +1 si n est impair
si n % 2 == 0:
ans = -1*ans + module
retour sna % module
Si vous n'avez pas besoin d'une exacte valeur, la vie devient un peu plus facile - vous pouvez utiliser Stirling approximation pour calculer une valeur approchée de la valeur en O(log n)
du temps (en utilisant l'exponentiation par la quadrature).
Enfin, je dois mentionner que si c'est le temps critique et vous êtes à l'aide de Python, essayez de passer à C++. Par expérience personnelle, vous devriez vous attendre environ un ordre de grandeur augmentation de la vitesse ou plus, simplement parce que c'est exactement le genre de CPU serré en boucle que natif du code compilé excelle à (aussi, pour quelque raison que ce soit, GMP, semble beaucoup plus fine que Python Bignum).