O melhor algoritmo conhecido é expressar o fatorial como um produto de potências primárias. Pode-se determinar rapidamente os números primos, bem como a potência correta para cada número primo usando uma abordagem de peneira. O cálculo de cada potência pode ser feito com eficiência usando o quadrado repetido e, em seguida, os fatores são multiplicados juntos. Isso foi descrito por Peter B. Borwein, Sobre a complexidade dos fatores de cálculo , Journal of Algorithms 6 376–380, 1985. ( PDF ) Em resumo,pode ser calculado no tempo , comparado ao tempo necessário ao usar a definição.n!O(n(logn)3loglogn)Ω(n2logn)
O significado do livro talvez fosse o método de dividir e conquistar. Pode-se reduzir as multiplicações usando o padrão regular do produto.n−1
Letdenote como uma notação conveniente. Reorganize os fatores de como
Agora suponha que para algum número inteiro . (Essa é uma suposição útil para evitar complicações na discussão a seguir, e a idéia pode ser estendida ao general .) Entãoe expandindo essa recorrência,
Computando1 ⋅ 3 ⋅ 5 ⋯ ( 2 n - 1 ) ( 2 n ) ! = 1 ⋅ 2 ⋅ 3 ⋯ ( 2 N ) ( 2 N ) ! = n ! ⋅ 2 n ⋅ 3 ⋅ 5 ⋅ 7 ⋯ ( 2 n - 1 ) . n = 2 k k >n?1⋅3⋅5⋯(2n−1)(2n)!=1⋅2⋅3⋯(2n)
(2n)!=n!⋅2n⋅3⋅5⋅7⋯(2n−1).
n=2kn ( 2 k ) ! = ( 2 k - 1 ) ! 2 2 k - 1 ( 2 k - 1 ) ? ( 2 k ) ! = ( 2 2 k - 1 + 2 k - 2 + ⋯ + 2 0 ) k - 1 ∏ i = 0 ( 2 i )k>0n(2k)!=(2k−1)!22k−1(2k−1)?( 2 k - 1 ) ? ( k - 2 ) + 2 k - 1 - 2 2 2 k - 2 2 2 k - 1(2k)!=(22k−1+2k−2+⋯+20)∏i=0k−1(2i)?=(22k−1)∏i=1k−1(2i)?.
(2k−1)?e multiplicar os produtos parciais em cada estágio leva multiplicações. Isso é uma melhoria de um fator de quase das multiplicações usando apenas a definição. Algumas operações adicionais são necessárias para calcular a potência de , mas na aritmética binária, isso pode ser feito de forma barata (dependendo do que é precisamente necessário, pode ser necessário adicionar apenas um sufixo de zeros).
( k - 2 ) + 2k - 1- 222k- 222k−1
O código Ruby a seguir implementa uma versão simplificada disso. Isso não evita a recálculo demesmo onde poderia fazê-lo: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)
Mesmo esse código de primeira passagem melhora no trivial
f = 1; (1..32768).map{ |i| f *= i }; print f
em cerca de 20% nos meus testes.
Com um pouco de trabalho, isso pode ser melhorado ainda mais, removendo também o requisito de que seja uma potência de (consulte a ampla discussão ).2n2