n può essere arbitrariamente grande
Beh, n
non può essere arbitrariamente grande - se n >= m
, quindi n! ≡ 0 (mod m)
(perché m
è uno dei fattori, dalla definizione di fattoriale).
Supponendo n << m
ed è necessario un esatto valore , l'algoritmo non può andare più veloce, a mia conoscenza. Tuttavia, se n > m/2
, è possibile utilizzare la seguente identità (Wilson's theorem - Grazie @Daniel Fischer!)

di limitare il numero di moltiplicazioni a circa 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)
Questo ci dà un modo semplice per calcolare n! (mod m)
in m-n-1
moltiplicazioni, oltre a un modular inverse:
def factorialMod(n, modulus):
ans=1
if n <= modulus//2:
#calculate the factorial normally (right argument of range() is exclusive)
for i in range(1,n+1):
ans = (ans * i) % modulus
else:
#Fancypants method for large n
for i in range(n+1,modulus):
ans = (ans * i) % modulus
ans = modinv(ans, modulus)
ans = -1*ans + modulus
return ans % modulus
Possiamo riformulare l'equazione di cui sopra in un altro modo, che potrebbe o meno eseguire leggermente più velocemente. Utilizzando la seguente identità:

possiamo riformulare l'equazione come
n! ≡ -[(n+1) * ... * (m-2) * (m-1)]-1 (mod m)
n! ≡ -[(n+1-m) * ... * (m-2-m) * (m-1-m)]-1 (mod m)
(reverse order of terms)
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)
Questo può essere scritto in Python come segue:
def factorialMod(n, modulus):
ans=1
if n <= modulus//2:
#calculate the factorial normally (right argument of range() is exclusive)
for i in range(1,n+1):
ans = (ans * i) % modulus
else:
#Fancypants method for large n
for i in range(1,modulus-n):
ans = (ans * i) % modulus
ans = modinv(ans, modulus)
#Since m is an odd-prime, (-1)^(m-n) = -1 if n is even, +1 if n is odd
if n % 2 == 0:
ans = -1*ans + modulus
return ans % modulus
Se don' t necessario un valore esatto , lif e diventa un po 'più facile - è possibile utilizzare Stirling's approximation per calcolare un valore approssimativo nel tempo O(log n)
(utilizzando exponentiation by squaring).
Infine, dovrei dire che se questo è time-critical e stai usando Python, prova a passare a C++. Dall'esperienza personale, dovresti aspettarti un aumento di velocità dell'ordine o maggiore, semplicemente perché questo è esattamente il tipo di limite stretto della CPU che il codice compilato in origine eccelle allo (anche, per qualsiasi motivo , GMP sembra molto più finemente accordato rispetto a Bignum di Python).
Quanto è grande? –
Arbitrariamente grande –
Quanto lento è lento? Dal tuo pseudocodice, deduco che stai calcolando questo in Python, giusto? –