Acho que a ideia principal é aproveitar o fato de não precisar recalcular tudo todas as vezes.
Por exemplo, vamos supor que eu já calculei 3! usando um loop (multiplicando todos os inteiros de 1 a 3). Depois, quando for calcular 4!, não preciso fazer todo o loop de 1 a 4. Eu posso fazer apenas 4\times 3! usando o resultado de 3! que já havia sido calculado anteriormente. E depois, se tiver um número maior (por exemplo, 8), eu posso multiplicar até o 5 (8\times 7\times 6\times 5) e depois multiplicar isso por 4! que já foi calculado.
Eu posso inclusive usar o próprio objeto final para isso. Pois ele está justamente armazenando os fatoriais que eu já calculei. Algo assim:
function fat(n, result) {
if (n in result) {
return;
}
var fat = n;
for (var i = n - 1; i > 1; i--) {
if (i in result) {
result[n] = fat * result[i];
return;
}
fat *= i;
}
result[n] = fat;
}
function calculeFactorial(...nums) {
var result = {};
for (var num of nums) {
fat(num, result);
}
return result;
}
console.log(calculeFactorial(3, 4, 5));
Assim só tem retrabalho quando os primeiros números são maiores. Por exemplo, se calcular primeiro 20!, somente este valor ficará em result
. Depois, se tiver que calcular 4!, não há resultado anterior calculado para reaproveitar, então vai ter que fazer o loop de 1 a 4.
Então outra forma seria pré-calcular todos os fatoriais até o maior dos números, e depois retornar apenas os que precisar:
function calculeFactorial(...nums) {
var maior = Math.max(...nums);
var fatoriais = [ 1 , 1 ];
var fat = 1;
for (var i = 2; i <= maior; i++) {
fat *= i;
fatoriais[i] = fat;
}
var result = {};
for (var n of nums) {
result[n] = fatoriais[n];
}
return result;
}
Por exemplo, se os números forem 3, 4 e 5, primeiro ele vai calcular todos os fatoriais até 5, que é o maior valor de todos. Afinal, uma hora vai precisar fazer o loop até 5, então eu faço apenas uma vez, e aproveito para ir guardando os resultados intermediários. No final, fatoriais
terá todos os fatoriais de 1 a 5.
Depois, basta pegar os fatoriais dos números que foram passados e retornar.
Fiz uns testes com o Benchmark.js e a primeira solução se mostrou mais rápida. Quando os primeiros números são maiores, a diferença diminui um pouco, por causa do que já foi dito (precisa recalcular os seguintes), mas ainda sim continua sendo mais rápida.