L'algoritmo migliore che si conosca è quello di esprimere il fattoriale come prodotto dei poteri principali. È possibile determinare rapidamente i numeri primi e la giusta potenza per ciascun numero primo usando un approccio a setaccio. Il calcolo di ogni potenza può essere eseguito in modo efficiente utilizzando la quadratura ripetuta, quindi i fattori vengono moltiplicati insieme. Questo è stato descritto da Peter B. Borwein, On the Complexity of Calculating Factorials , Journal of Algorithms 6 376–380, 1985. ( PDF ) In breve, n!può essere calcolato in O(n(logn)3loglogn) tempo, rispetto al tempo richiesto quando si utilizza la definizione.Ω(n2logn)
Ciò che il libro di testo forse significava era il metodo di divisione e conquista. Si possono ridurre le moltiplicazioni usando il modello regolare del prodotto.n−1
Let denotare 1 ⋅ 3 ⋅ 5 ⋯ ( 2 n - 1 ) come notazione conveniente. Riorganizza i fattori di ( 2 n ) ! = 1 ⋅ 2 ⋅ 3 ⋯ ( 2 n ) come
( 2 n ) ! = n ! ⋅ 2 n ⋅ 3 ⋅ 5 ⋅ 7 ⋯ ( 2 n -n?1⋅3⋅5⋯(2n−1)(2n)!=1⋅2⋅3⋯(2n)
Supponiamo ora n = 2 k per un numero intero k > 0 . (Questo è un presupposto utile per evitare complicazioni nella discussione che segue, e l'idea può essere estesa al n generale.) Quindi ( 2 k ) ! = ( 2 k - 1 ) ! 2 2 k - 1 ( 2 k - 1 ) ? e espandendo questa ricorrenza,
( 2 k ) ! =
(2n)!=n!⋅2n⋅3⋅5⋅7⋯(2n−1).
n=2kk>0n(2k)!=(2k−1)!22k−1(2k−1)?
Informatica
( 2 k - 1 )?(2k)!=(22k−1+2k−2+⋯+20)∏i=0k−1(2i)?=(22k−1)∏i=1k−1(2i)?.
(2k−1)?e moltiplicare i prodotti parziali in ciascuna fase richiede
moltiplicazioni. Questo è un miglioramento di un fattore di quasi
2 da
2 k - 2 moltiplicazioni usando solo la definizione. Sono necessarie alcune operazioni aggiuntive per calcolare la potenza di
2 , ma nell'aritmetica binaria ciò può essere fatto in modo economico (a seconda di ciò che è esattamente richiesto, potrebbe essere necessario aggiungere un suffisso di
2 k - 1 zero).
(k−2)+2k−1−222k−222K- 1
Il seguente codice Ruby implementa una versione semplificata di questo. Questo non evita di ricalcolare anche dove potrebbe farlo:n ?
def oddprod(l,h)
p = 1
ml = (l%2>0) ? l : (l+1)
mh = (h%2>0) ? h : (h-1)
while ml <= mh do
p = p * ml
ml = ml + 2
end
p
end
def fact(k)
f = 1
for i in 1..k-1
f *= oddprod(3, 2 ** (i + 1) - 1)
end
2 ** (2 ** k - 1) * f
end
print fact(15)
Anche questo codice di primo passaggio migliora il banale
f = 1; (1..32768).map{ |i| f *= i }; print f
di circa il 20% nei miei test.
Con un po 'di lavoro, questo può essere ulteriormente migliorato, eliminando anche la necessità che sia una potenza di 2 (vedere la discussione approfondita ).n2