Qual é a função fatorial mais rápida em JavaScript? [fechadas]

94

Procurando uma implementação realmente rápida de função fatorial em JavaScript. Algum sugere?

Ken
fonte
8
Qual é a gama de argumentos possível?
Nikita Rybak
5
Você considerou pré-calcular fatoriais e armazenar os valores em uma tabela de pesquisa?
Waleed Amjad,
2
Qual é a aplicação de tal função? Em outras palavras, para que você vai usá-lo?
Pointy de
@Nikita Rybak, apenas 1 agrumento (n). If (n> 170) e = Infinity
Ken,
@ Pointy, mais um serviço de calculadora matemática.
Ken,

Respostas:

110

Você pode pesquisar por (1 ... 100)! no Wolfram | Alpha para pré-calcular a seqüência fatorial.

Os primeiros 100 números são:

1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000, 20922789888000, 355687428096000, 6402373705728000, 121645100408832000, 2432902008176640000, 51090942171709440000, 1124000727777607680000, 25852016738884976640000, 620448401733239439360000, 15511210043330985984000000, 403291461126605635584000000, 10888869450418352160768000000, 304888344611713860501504000000, 8841761993739701954543616000000, 265252859812191058636308480000000, 8222838654177922817725562880000000, 263130836933693530167218012160000000, 8683317618811886495518194401280000000, 295232799039604140847618609643520000000, 10333147966386144929666651337523200000000, 371993326789901217467999448150835200000000, 13763753091226345046315979581580902400000000, 523022617466601111760007224100074291200000000, 20397882081197443358640281739902897356800000000, 815915283247897734345611269596115894272000000000, 33452526613163807108170062053440751665152000000000, 1405006117752879898543142606244511569936384000000000, 60415263063373835637355132068513997507264512000000000, 2658271574788448768043625811014615890319638528000000000, 119622220865480194561963161495657715064383733760000000000, 5502622159812088949850305428800254892961651752960000000000, 258623241511168180642964355153611979969197632389120000000000, 12413915592536072670862289047373375038521486354677760000000000, 608281864034267560872252163321295376887552831379210240000000000, 30414093201713378043612608166064768844377641568960512000000000000, 1551118753287382280224243016469303211063259720016986112000000000000, 80658175170943878571660636856403766975289505440883277824000000000000, 4274883284060025564298013753389399649690343788366813724672000000000000, 230843697339241380472092742683027581083278564571807941132288000000000000, 12696403353658275925965100847566516959580321051449436762275840000000000000, 710998587804863451854045647463724949736497978881168458687447040000000000000, 40526919504877216755680601905432322134980384796226602145184481280000000000000, 2350561331282878571829474910515074683828862318181142924420699914240000000000000, 138683118545689835737939019720389406345902876772687432540821294940160000000000000, 8320987112741390144276341183223364380754172606361245952449277696409600000000000000, 507580213877224798800856812176625227226004528988036003099405939480985600000000000000, 31469973260387937525653122354950764088012280797258232192163168247821107200000000000000, 1982608315404440064116146708361898137544773690227268628106279599612729753600000000000000, 126886932185884164103433389335161480802865516174545192198801894375214704230400000000000000, 8247650592082470666723170306785496252186258551345437492922123134388955774976000000000000000, 544344939077443064003729240247842752644293064388798874532860126869671081148416000000000000000, 36471110918188685288249859096605464427167635314049524593701628500267962436943872000000000000000, 2480035542436830599600990418569171581047399201355367672371710738018221445712183296000000000000000, 171122452428141311372468338881272839092270544893520369393648040923257279754140647424000000000000000, 11978571669969891796072783721689098736458938142546425857555362864628009582789845319680000000000000000, 850478588567862317521167644239926010288584608120796235886430763388588680378079017697280000000000000000, 61234458376886086861524070385274672740778091784697328983823014963978384987221689274204160000000000000000, 4470115461512684340891257138125051110076800700282905015819080092370422104067183317016903680000000000000000, 330788544151938641225953028221253782145683251820934971170611926835411235700971565459250872320000000000000000, 24809140811395398091946477116594033660926243886570122837795894512655842677572867409443815424000000000000000000, 1885494701666050254987932260861146558230394535379329335672487982961844043495537923117729972224000000000000000000, 145183092028285869634070784086308284983740379224208358846781574688061991349156420080065207861248000000000000000000, 11324281178206297831457521158732046228731749579488251990048962825668835325234200766245086213177344000000000000000000, 894618213078297528685144171539831652069808216779571907213868063227837990693501860533361810841010176000000000000000000, 71569457046263802294811533723186532165584657342365752577109445058227039255480148842668944867280814080000000000000000000, 5797126020747367985879734231578109105412357244731625958745865049716390179693892056256184534249745940480000000000000000000, 475364333701284174842138206989404946643813294067993328617160934076743994734899148613007131808479167119360000000000000000000, 39455239697206586511897471180120610571436503407643446275224357528369751562996629334879591940103770870906880000000000000000000, 3314240134565353266999387579130131288000666286242049487118846032383059131291716864129885722968716753156177920000000000000000000, 281710411438055027694947944226061159480056634330574206405101912752560026159795933451040286452340924018275123200000000000000000000, 24227095383672732381765523203441259715284870552429381750838764496720162249742450276789464634901319465571660595200000000000000000000, 2107757298379527717213600518699389595229783738061356212322972511214654115727593174080683423236414793504734471782400000000000000000000, 185482642257398439114796845645546284380220968949399346684421580986889562184028199319100141244804501828416633516851200000000000000000000, 16507955160908461081216919262453619309839666236496541854913520707833171034378509739399912570787600662729080382999756800000000000000000000, 1485715964481761497309522733620825737885569961284688766942216863704985393094065876545992131370884059645617234469978112000000000000000000000, 135200152767840296255166568759495142147586866476906677791741734597153670771559994765685283954750449427751168336768008192000000000000000000000, 12438414054641307255475324325873553077577991715875414356840239582938137710983519518443046123837041347353107486982656753664000000000000000000000, 1156772507081641574759205162306240436214753229576413535186142281213246807121467315215203289516844845303838996289387078090752000000000000000000000, 108736615665674308027365285256786601004186803580182872307497374434045199869417927630229109214583415458560865651202385340530688000000000000000000000, 10329978488239059262599702099394727095397746340117372869212250571234293987594703124871765375385424468563282236864226607350415360000000000000000000000, 991677934870949689209571401541893801158183648651267795444376054838492222809091499987689476037000748982075094738965754305639874560000000000000000000000, 96192759682482119853328425949563698712343813919172976158104477319333745612481875498805879175589072651261284189679678167647067832320000000000000000000000, 9426890448883247745626185743057242473809693764078951663494238777294707070023223798882976159207729119823605850588608460429412647567360000000000000000000000, 933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000, 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

Se você ainda deseja calcular os valores sozinho, pode usar a memoização :

var f = [];
function factorial (n) {
  if (n == 0 || n == 1)
    return 1;
  if (f[n] > 0)
    return f[n];
  return f[n] = factorial(n-1) * n;
}

Edição: 21/08/2014

Solução 2

Achei que seria útil adicionar um exemplo funcional de função fatorial iterativa preguiçosa que usa números grandes para obter o resultado exato com memoização e cache como comparação

var f = [new BigNumber("1"), new BigNumber("1")];
var i = 2;
function factorial(n)
{
  if (typeof f[n] != 'undefined')
    return f[n];
  var result = f[i-1];
  for (; i <= n; i++)
      f[i] = result = result.multiply(i.toString());
  return result;
}
var cache = 100;
// Due to memoization, following line will cache first 100 elements.
factorial(cache);

Presumo que você usaria algum tipo de encerramento para limitar a visibilidade do nome da variável.

Ref : BigNumber Sandbox : JsFiddle

Margus
fonte
Os valores anteriores a 6402373705728000 serão truncados, portanto, se você for usar essa abordagem, certifique-se de converter para exponencial antes de usar a tabela mencionada.
David Scott Kirby
1
@DavidScottKirby Javascript converte automaticamente esses números em sua representação float de 64 bits mais próxima. O benefício real de não ter os números de precisão total no código é o tamanho do arquivo reduzido.
le_m
Sua segunda solução pode ser simplificada para function factorial (n) { for (var i = f.length; i <= n; i++) f.push(f[i - 1].multiply(i.toString())); return f[n]; }ver também minha resposta, que usa a BigIntbiblioteca integrada mais recente em vez de uma biblioteca de terceiros.
Patrick Roberts,
96

Você deve usar um loop.

Aqui estão duas versões comparadas calculando o fatorial de 100 para 10.000 vezes.

Recursiva

function rFact(num)
{
    if (num === 0)
      { return 1; }
    else
      { return num * rFact( num - 1 ); }
}

Iterativo

function sFact(num)
{
    var rval=1;
    for (var i = 2; i <= num; i++)
        rval = rval * i;
    return rval;
}

Ao vivo em: http://jsfiddle.net/xMpTv/

Meus resultados mostram:
- Recursivo ~ 150 milissegundos
- Iterativo ~ 5 milissegundos ..

Gabriele Petrioli
fonte
+1 Ótima resposta! Embora a memoização possa ser razoável quando há várias chamadas para calcular fatoriais para números maiores.
Tadeck
@Tadeck, obrigado. Na verdade, a memoização é muito útil neste caso e é por isso que a resposta de Margus é escolhida como a correta :)
Gabriele Petrioli
Uma versão de recursiva de 1 linha: função fatorial (num) {return (num == 1)? num: num * argumentos.callee (num-1); }
jbyrd
2
@HWTech, você nunca está chamando os métodos. Seu teste compara a velocidade de definição dos dois métodos .. não o tempo que eles levam para executar .. Este é um teste
Gabriele Petrioli
3
Em vez de rval = rval * i;você poderia escreverrval *= i;
Ryan,
29

Ainda acho que a resposta de Margus é a melhor. No entanto, se você quiser calcular os fatoriais de números dentro do intervalo de 0 a 1 (ou seja, a função gama) também, você não pode usar essa abordagem porque a tabela de pesquisa terá que conter valores infinitos.

No entanto, você pode aproximar os valores dos fatoriais e é muito rápido, mais rápido do que chamar a si mesmo recursivamente ou pelo menos fazer um loop (especialmente quando os valores começam a ficar maiores).

Um bom método de aproximação é o de Lanczos

Aqui está uma implementação em JavaScript (transferida de uma calculadora que escrevi meses atrás):

function factorial(op) {
 // Lanczos Approximation of the Gamma Function
 // As described in Numerical Recipes in C (2nd ed. Cambridge University Press, 1992)
 var z = op + 1;
 var p = [1.000000000190015, 76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 1.208650973866179E-3, -5.395239384953E-6];

 var d1 = Math.sqrt(2 * Math.PI) / z;
 var d2 = p[0];

 for (var i = 1; i <= 6; ++i)
  d2 += p[i] / (z + i);

 var d3 = Math.pow((z + 5.5), (z + 0.5));
 var d4 = Math.exp(-(z + 5.5));

 d = d1 * d2 * d3 * d4;

 return d;
}

Agora você pode fazer coisas legais como factorial(0.41)etc, porém a precisão pode estar um pouco errada, afinal, é uma aproximação do resultado.

Waleed Amjad
fonte
abordagem bastante interessante, obrigado.
Ken,
Acabei de me poupar muito tempo, muito obrigado :)
nicolaskruchten
Eu recomendo mudar a parte abaixo do for-loop para var d3d4 = Math.exp((z + 0.5) * Math.log(z + 5.5) - z - 5.5); return d1 * d2 * d3d4;. Isso permite que você calcule fatoriais de até 169! em vez de atualmente apenas 140 !. Isso é muito próximo do fatorial máximo representável usando o Numbertipo de dados, que é 170 !.
final de
18

A tabela de pesquisa é o caminho óbvio, se você estiver trabalhando com números naturais. Para calcular qualquer fatorial em tempo real, você pode acelerá-lo com um cache, salvando os números que você calculou antes. Algo como:

factorial = (function() {
    var cache = {},
        fn = function(n) {
            if (n === 0) {
                return 1;
            } else if (cache[n]) {
                return cache[n];
            }
            return cache[n] = n * fn(n -1);
        };
    return fn;
})();

Você pode pré-calcular alguns valores para acelerar ainda mais.

xPheRe
fonte
3
Eu criei um auto-memoizer para qualquer função com base nesta resposta (também um pouco mais rápido :)), incluindo também um limite no tamanho do cache. stackoverflow.com/a/10031674/36537
Phil H
16

Aqui está minha solução:

function fac(n){
    return(n<2)?1:fac(n-1)*n;
}

É a maneira mais simples (menos caracteres / linhas) que encontrei, apenas uma função com uma linha de código.


Edit:
Se você realmente deseja salvar alguns caracteres, pode usar uma função de seta (21 bytes) :

f=n=>(n<2)?1:f(n-1)*n
António Almeida
fonte
7
Economize ainda mais com f=n=>n?f(n-1)*n:1...
le_m
infelizmente, mesmo que seja bom de ver e curto, essa é a maneira mais lenta de fazer isso.
Zibri
11

Apenas uma linha com ES6

const factorial = n => !(n > 1) ? 1 : factorial(n - 1) * n;

Abdennour TOUMI
fonte
factorial = n => n <= 1 ? 1 : factorial(n - 1) * n
Naramsim
10

função recursiva curta e fácil (você também poderia fazer isso com um loop, mas não acho que isso faria qualquer diferença no desempenho):

function factorial (n){
  if (n==0 || n==1){
    return 1;
  }
  return factorial(n-1)*n;
} 

para um n muito grande, você poderia usar a aproximação de Stirlings - mas isso só fornecerá um valor aproximado.

EDITAR: um comentário sobre por que estou recebendo uma votação negativa para isso teria sido bom ...

EDIT2: esta seria a solução usando um loop (que seria a melhor escolha):

function factorial (n){
  j = 1;
  for(i=1;i<=n;i++){
    j = j*i;
  }
  return j;
}

Acho que a melhor solução seria usar os valores em cache, como Margus mencionou, e usar a aproximação de stirlings para valores maiores (assumindo que você precisa ser muito rápido e não precisa ser tão exato em números tão grandes).

Oezi
fonte
3
Em linguagens sem otimização de chamada final (ou seja, as linguagens mais amplamente utilizadas), é melhor usar uma implementação não recursiva onde seja fácil de fazer, embora haja maneiras de contornar isso: paulbarry.com/articles/2009/08/30 / tail-call-optimization
Daniel Earwicker,
isso definitivamente não é tão rápido, já que nem mesmo usaria o TCO, se fosse implementado. Mas é simples e eu não votaria contra ele. Não é o mais rápido com certeza.
haylem
A otimização da chamada final nem mesmo é possível para esta função, pois a chamada recursiva não está na posição final.
Fred Foo,
3
@Josh, ( não o downvoter ) mais rápido é o loop por uma margem considerável.
Gabriele Petrioli
7

Veja, o memoizer, que pega qualquer função de argumento único e a memoriza. Acontece ser um pouco mais rápido do que a solução de @ xPheRe , incluindo o limite do tamanho do cache e a verificação associada, porque eu uso curto-circuito e assim por diante.

function memoize(func, max) {
    max = max || 5000;
    return (function() {
        var cache = {};
        var remaining = max;
        function fn(n) {
            return (cache[n] || (remaining-- >0 ? (cache[n]=func(n)) : func(n)));
        }
        return fn;
    }());
}

function fact(n) {
    return n<2 ? 1: n*fact(n-1);
}

// construct memoized version
var memfact = memoize(fact,170);

// xPheRe's solution
var factorial = (function() {
    var cache = {},
        fn = function(n) {
            if (n === 0) {
                return 1;
            } else if (cache[n]) {
                return cache[n];
            }
            return cache[n] = n * fn(n -1);
        };
    return fn;
}());

Aproximadamente 25x mais rápido em minha máquina no Chrome do que a versão recursiva e 10% mais rápido do que o xPheRe.

Phil H
fonte
6

Função fatorial mais rápida

Acho que esta versão baseada em loop pode ser a função fatorial mais rápida.

function factorial(n, r = 1) {
  while (n > 0) r *= n--;
  return r;
}

// Default parameters `r = 1`,
//   was introduced in ES6

E aqui está o meu raciocínio:

  • Funções recursivas, mesmo com memoização, têm a sobrecarga de uma chamada de função (basicamente colocando funções na pilha), que tem menos desempenho do que usar um loop
  • Embora os forloops e whileloops tenham desempenho semelhante, um forloop sem uma expressão de inicialização e uma expressão final parece estranho; provavelmente melhor escrever for(; n > 0;)comowhile(n > 0)
  • Apenas dois parâmetros ne rsão usados, então, em teoria, menos parâmetros significa menos tempo gasto na alocação de memória
  • Usa um loop decrementado que verifica se né zero - ouvi teorias de que os computadores são melhores na verificação de números binários (0 e 1) do que em outros inteiros
tfmontague
fonte
5

Me deparei com este post. Inspirado por todas as contribuições aqui, eu vim com minha própria versão, que tem dois recursos que não tinha discutido antes: 1) Uma verificação para garantir que o argumento é um número inteiro não negativo 2) Fazer uma unidade fora do cache e a função para torná-lo um pedaço de código independente. Para me divertir, tentei torná-lo o mais compacto possível. Alguns podem achar isso elegante, outros podem pensar que é terrivelmente obscuro. Enfim, aqui está:

var fact;
(fact = function(n){
    if ((n = parseInt(n)) < 0 || isNaN(n)) throw "Must be non-negative number";
    var cache = fact.cache, i = cache.length - 1;
    while (i < n) cache.push(cache[i++] * i);
    return cache[n];
}).cache = [1];

Você pode pré-preencher o cache ou permitir que ele seja preenchido conforme as chamadas vão passando. Mas o elemento inicial (por fato (0) deve estar presente ou será quebrado.

Aproveitar :)

Roland Bouman
fonte
4

É muito simples usar ES6

const factorial = n => n ? (n * factorial(n-1)) : 1;

Veja um exemplo aqui

Joseluiscc
fonte
4

Aqui está uma solução:

function factorial(number) {
  total = 1
  while (number > 0) {
    total *= number
    number = number - 1
  }
  return total
}
Erika Smith
fonte
4

Usando ES6, você pode alcançá-lo rápido e curto:

const factorial = n => [...Array(n + 1).keys()].slice(1).reduce((acc, cur) => acc * cur, 1)
Amin Jafari
fonte
3

O código para calcular o fatorial depende de seus requisitos.

  1. Você está preocupado com o estouro?
  2. Que gama de entradas você terá?
  3. É mais importante para você minimizar o tamanho ou o tempo?
  4. O que você vai fazer com o fatorial?

Em relação aos pontos 1 e 4, muitas vezes é mais útil ter uma função para avaliar o log do fatorial diretamente do que ter uma função para avaliar o próprio fatorial.

Aqui está uma postagem de blog que discute esses problemas. Aqui está um código C # para calcular o fatorial de log que seria trivial portar para JavaScript. Mas pode não ser o melhor para suas necessidades, dependendo de suas respostas às perguntas acima.

John D. Cook
fonte
A lista numerada provavelmente deve estar nos comentários. Tudo o que resta são dois links, e respostas apenas com links são desencorajadas.
Barett
3

Esta é uma versão baseada em loop compacto

function factorial( _n )
{
    var _p = 1 ;
    while( _n > 0 ) { _p *= _n-- ; }
    return _p ;
}

Ou você pode substituir o objeto Math (versão recursiva):

Math.factorial = function( _x )  { return _x <= 1 ? 1 : _x * Math.factorial( --_x ) ; }

Ou junte as duas abordagens ...

Sandro Rosa
fonte
1
Eu consertei dentro do código acima. Obrigado!
Sandro Rosa
3

Explorando o fato de que Number.MAX_VALUE < 171!podemos simplesmente usar uma tabela de pesquisa completa consiste em apenas 171 elementos compactos de array, ocupando menos de 1,4 kilobytes de memória.

Uma função de pesquisa rápida com complexidade de tempo de execução O (1) e sobrecarga mínima de acesso ao array ficaria assim:

// Lookup table for n! for 0 <= n <= 170:
const factorials = [1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600,6227020800,87178291200,1307674368e3,20922789888e3,355687428096e3,6402373705728e3,121645100408832e3,243290200817664e4,5109094217170944e4,1.1240007277776077e21,2.585201673888498e22,6.204484017332394e23,1.5511210043330986e25,4.0329146112660565e26,1.0888869450418352e28,3.0488834461171387e29,8.841761993739702e30,2.6525285981219107e32,8.222838654177922e33,2.631308369336935e35,8.683317618811886e36,2.9523279903960416e38,1.0333147966386145e40,3.7199332678990125e41,1.3763753091226346e43,5.230226174666011e44,2.0397882081197444e46,8.159152832478977e47,3.345252661316381e49,1.40500611775288e51,6.041526306337383e52,2.658271574788449e54,1.1962222086548019e56,5.502622159812089e57,2.5862324151116818e59,1.2413915592536073e61,6.082818640342675e62,3.0414093201713376e64,1.5511187532873822e66,8.065817517094388e67,4.2748832840600255e69,2.308436973392414e71,1.2696403353658276e73,7.109985878048635e74,4.0526919504877214e76,2.3505613312828785e78,1.3868311854568984e80,8.32098711274139e81,5.075802138772248e83,3.146997326038794e85,1.98260831540444e87,1.2688693218588417e89,8.247650592082472e90,5.443449390774431e92,3.647111091818868e94,2.4800355424368305e96,1.711224524281413e98,1.1978571669969892e100,8.504785885678623e101,6.1234458376886085e103,4.4701154615126844e105,3.307885441519386e107,2.48091408113954e109,1.8854947016660504e111,1.4518309202828587e113,1.1324281178206297e115,8.946182130782976e116,7.156945704626381e118,5.797126020747368e120,4.753643337012842e122,3.945523969720659e124,3.314240134565353e126,2.81710411438055e128,2.4227095383672734e130,2.107757298379528e132,1.8548264225739844e134,1.650795516090846e136,1.4857159644817615e138,1.352001527678403e140,1.2438414054641308e142,1.1567725070816416e144,1.087366156656743e146,1.032997848823906e148,9.916779348709496e149,9.619275968248212e151,9.426890448883248e153,9.332621544394415e155,9.332621544394415e157,9.42594775983836e159,9.614466715035127e161,9.90290071648618e163,1.0299016745145628e166,1.081396758240291e168,1.1462805637347084e170,1.226520203196138e172,1.324641819451829e174,1.4438595832024937e176,1.588245541522743e178,1.7629525510902446e180,1.974506857221074e182,2.2311927486598138e184,2.5435597334721877e186,2.925093693493016e188,3.393108684451898e190,3.969937160808721e192,4.684525849754291e194,5.574585761207606e196,6.689502913449127e198,8.094298525273444e200,9.875044200833601e202,1.214630436702533e205,1.506141741511141e207,1.882677176888926e209,2.372173242880047e211,3.0126600184576594e213,3.856204823625804e215,4.974504222477287e217,6.466855489220474e219,8.47158069087882e221,1.1182486511960043e224,1.4872707060906857e226,1.9929427461615188e228,2.6904727073180504e230,3.659042881952549e232,5.012888748274992e234,6.917786472619489e236,9.615723196941089e238,1.3462012475717526e241,1.898143759076171e243,2.695364137888163e245,3.854370717180073e247,5.5502938327393044e249,8.047926057471992e251,1.1749972043909107e254,1.727245890454639e256,2.5563239178728654e258,3.80892263763057e260,5.713383956445855e262,8.62720977423324e264,1.3113358856834524e267,2.0063439050956823e269,3.0897696138473508e271,4.789142901463394e273,7.471062926282894e275,1.1729568794264145e278,1.853271869493735e280,2.9467022724950384e282,4.7147236359920616e284,7.590705053947219e286,1.2296942187394494e289,2.0044015765453026e291,3.287218585534296e293,5.423910666131589e295,9.003691705778438e297,1.503616514864999e300,2.5260757449731984e302,4.269068009004705e304,7.257415615307999e306];

// Lookup function:
function factorial(n) {
  return factorials[n] || (n > 170 ? Infinity : NaN);
}

// Test cases:
console.log(factorial(NaN));       // NaN
console.log(factorial(-Infinity)); // NaN
console.log(factorial(-1));        // NaN
console.log(factorial(0));         // 1
console.log(factorial(170));       // 7.257415615307999e+306 < Number.MAX_VALUE
console.log(factorial(171));       // Infinity > Number.MAX_VALUE
console.log(factorial(Infinity));  // Infinity

Isso é tão preciso e rápido quanto possível usando o Numbertipo de dados. Computando a tabela de pesquisa em Javascript - como algumas outras respostas sugerem - reduzirá a precisão quando n! > Number.MAX_SAFE_INTEGER.

Compactar a tabela de tempo de execução via gzip reduz seu tamanho no disco de cerca de 3,6 para 1,8 kilobytes.

le_m
fonte
3

Resposta de uma linha:

const factorial = (num, accumulator) => num <= 1 ? accumulator || 1 : factorial(--num, num * (accumulator || num + 1));

factorial(5); // 120
factorial(10); // 3628800
factorial(3); // 6
factorial(7); // 5040
// et cetera

timgfx
fonte
3

Fatorial iterativo com BigIntpara segurança

A solução usa BigInt, um recurso ES 2018 + / 2019.

Este é um exemplo de uso funcional BigInt, porque muitas respostas aqui escapam Numberquase imediatamente do limite seguro de (MDN). Não é o mais rápido, mas é simples e, portanto, mais claro para adaptar outras otimizações (como um cache dos primeiros 100 números).

function factorial(nat) {
   let p = BigInt(1)
   let i = BigInt(nat)

   while (1 < i--) p *= i

   return p
}

Exemplo de uso

// 9.332621544394415e+157
Number(factorial(100))

// "933262154439441526816992388562667004907159682643816214685929638952175999
//  932299156089414639761565182862536979208272237582511852109168640000000000
//  00000000000000"
String(factorial(100))

// 9332621544394415268169923885626670049071596826438162146859296389521759999
// 3229915608941463976156518286253697920827223758251185210916864000000000000
// 000000000000n
factorial(100)
  • O nno final de um literal numérico como 1303nindica que é umBigInt tipo.
  • Lembre-se de que você não deve misturar BigIntcom a Numbermenos que os coage explicitamente, e isso pode causar perda de precisão.
Miau
fonte
3

Usando os recursos do ES6, pode escrever código em UMA linha e sem recursão :

var factorial=(n)=>Array.from({length: n},(v, k) => k+1).reduce((a, b) => a*b, 1)

Abdennour TOUMI
fonte
2

Apenas para completar, aqui está uma versão recursiva que permitiria a otimização da chamada final. Não tenho certeza se as otimizações de chamada final são realizadas em JavaScript.

function rFact(n, acc)
{
    if (n == 0 || n == 1) return acc; 
    else return rFact(n-1, acc*n); 
}

Para chamá-lo:

rFact(x, 1);
Robert Jeppesen
fonte
ES6 suporta TCO, mas afaik este recurso não está ativo por padrão em nenhum mecanismo principal ainda
le_m
2

Esta é uma solução iterativa que usa menos espaço na pilha e salva os valores calculados anteriormente de forma autolimitada:

Math.factorial = function(n){
    if(this.factorials[n]){ // memoized
        return this.factorials[n];
    }
    var total=1;
    for(var i=n; i>0; i--){
        total*=i;
    }
    this.factorials[n] = total; // save
    return total;
};
Math.factorials={}; // store

Observe também que estou adicionando isso ao objeto Math, que é um objeto literal, portanto, não há protótipo. Em vez disso, apenas vinculando-os diretamente à função.

bh-
fonte
Isso realmente não tira vantagem total da memoização para subproblemas - por exemplo, Math.factorial(100); Math.factorial(500);irá calcular a multiplicação 1..100 duas vezes.
Barett
2

Eu acredito que o seguinte é o trecho de código mais sustentável e eficiente dos comentários acima. Você pode usar isso na arquitetura js de seu aplicativo global ... e não se preocupe em escrevê-lo em vários namespaces (já que é uma tarefa que provavelmente não precisa de muito aumento). Incluí 2 nomes de métodos (com base na preferência), mas ambos podem ser usados ​​porque são apenas referências.

Math.factorial = Math.fact = function(n) {
    if (isNaN(n)||n<0) return undefined;
    var f = 1; while (n > 1) {
        f *= n--;
    } return f;
};
Joe Johnson
fonte
Ao iniciar sua multiplicação com, em n * (n-1) * (n-2) * ... * 1vez do contrário, você perde até 4 dígitos na precisão para n >> 20.
le_m
2
// if you don't want to update the Math object, use `var factorial = ...`
Math.factorial = (function() {
    var f = function(n) {
        if (n < 1) {return 1;}  // no real error checking, could add type-check
        return (f[n] > 0) ? f[n] : f[n] = n * f(n -1);
    }
    for (i = 0; i < 101; i++) {f(i);} // precalculate some values
    return f;
}());

factorial(6); // 720, initially cached
factorial[6]; // 720, same thing, slightly faster access, 
              // but fails above current cache limit of 100
factorial(100); // 9.33262154439441e+157, called, but pulled from cache
factorial(142); // 2.6953641378881614e+245, called
factorial[141]; // 1.89814375907617e+243, now cached

Isso faz o cache dos primeiros 100 valores em tempo real e não introduz uma variável externa no escopo do cache, armazenando os valores como propriedades do próprio objeto de função, o que significa que se você sabe factorial(n)que já foi calculado, você pode basta referir-se a ele como factorial[n], que é um pouco mais eficiente. A execução desses primeiros 100 valores levará menos de um milissegundo em navegadores modernos.

Scott Sauyet
fonte
Eu descobri isso depois dos 21! os números não são confiáveis.
AutoSponge
@AutoSponge Isso ocorre porque 21! > Number.MAX_SAFE_INTEGER, portanto, não pode ser representado com segurança como um float de 64 bits.
le_m
2

Aqui está uma implementação que calcula fatoriais positivos e negativos. É rápido e simples.

var factorial = function(n) {
  return n > 1
    ? n * factorial(n - 1)
    : n < 0
        ? n * factorial(n + 1)
        : 1;
}
Ilia Bykow
fonte
Normalmente, n! para n <0 não está definido. Consulte mathoverflow.net/questions/10124/the-factorial-of-1-2-3
le_m
2

Aqui está um que eu mesmo fiz, não use números acima de 170 ou abaixo de 2.

function factorial(x){
 if((!(isNaN(Number(x)))) && (Number(x)<=170) && (Number(x)>=2)){
  x=Number(x);for(i=x-(1);i>=1;--i){
   x*=i;
  }
 }return x;
}
TheBestGuest
fonte
Iniciando sua multiplicação com n * (n-1) * (n-2) * ... * 1 em vez do contrário, você perde até 4 dígitos na precisão para n >> 20. Além disso, cria um efeito indesejado variável global ie executa muitas Numberconversões e dá resultados incorretos para 0! (como você afirmou, mas por quê?).
le_m
2

Aqui está meu código

function factorial(num){
    var result = num;
    for(i=num;i>=2;i--){
        result = result * (i-1);
    }
    return result;
}
cse031sust02
fonte
1
Se (n> 170) e = infinito. E seu código irá gerar um grande número. não haverá transbordamentos?
primo de
Resultado incorreto para factorial(0). Além disso, ao iniciar sua multiplicação com n * (n-1) * (n-2) * ... * 1 em vez do contrário, você perde até 4 dígitos na precisão para n >> 20. @prime: 170! > Number.MAX_VALUEe é melhor representado com Infinity.
final de
2

O loop em cache deve ser mais rápido (pelo menos quando chamado várias vezes)

var factorial = (function() {
  var x =[];

  return function (num) {
    if (x[num] >0) return x[num];
    var rval=1;
    for (var i = 2; i <= num; i++) {
        rval = rval * i;
        x[i] = rval;
    }
    return rval;
  }
})();
Сухой 27
fonte
2
function isNumeric(n) {
    return !isNaN(parseFloat(n)) && isFinite(n)
}

Fornecido por http://javascript.info/tutorial/number-math como uma maneira simples de avaliar se um objeto é um número inteiro adequado para cálculo.

var factorials=[[1,2,6],3];

Um conjunto simples de fatoriais memorizados que requerem cálculos redundantes, podem ser processados ​​com "multiplicação por 1", ou são um dígito que é uma equação simples que não vale a pena processar ao vivo.

var factorial = (function(memo,n) {
    this.memomize = (function(n) {
        var ni=n-1;
        if(factorials[1]<n) {
            factorials[0][ni]=0;
            for(var factorial_index=factorials[1]-1;factorials[1]<n;factorial_index++) {
                factorials[0][factorials[1]]=factorials[0][factorial_index]*(factorials[1]+1);
                factorials[1]++;
            }
        }
    });
    this.factorialize = (function(n) {
        return (n<3)?n:(factorialize(n-1)*n);
    });
    if(isNumeric(n)) {
        if(memo===true) {
            this.memomize(n);
            return factorials[0][n-1];
        }
        return this.factorialize(n);
    }
    return factorials;
});

Depois de revisar a entrada de outros membros (excluindo o conselho do Log, embora eu possa implementá-lo mais tarde), fui em frente e criei um script que é bastante simples. Comecei com um exemplo simples de JavaScript OOP não educado e construí uma pequena classe para lidar com fatoriais. Em seguida, implementei minha versão da Memoização sugerida acima. Também implementei a Fatorialização abreviada, porém fiz um pequeno ajuste de erro; Mudei o "n <2" para "n <3". "n <2" ainda processaria n = 2, o que seria um desperdício, porque você iteraria para um 2 * 1 = 2; isso é um desperdício na minha opinião. Eu alterei para "n <3"; porque se n for 1 ou 2 ele simplesmente retornará n, se for 3 ou mais ele avaliará normalmente. Claro, como as regras se aplicam, coloquei minhas funções em ordem decrescente de execução presumida. Eu adicionei a opção bool (true | false) para permitir a alteração rápida entre a execução memorizada e normal (você nunca sabe quando quer trocar em sua página sem precisar mudar o "estilo") Como eu disse antes do A variável memoized factorials é definida com as 3 posições iniciais, pegando 4 caracteres e minimizando o desperdício de cálculos. Tudo após a terceira iteração, você está lidando com matemática de dois dígitos plus. Eu imagino que se você fosse um defensor o suficiente sobre isso, você executaria em uma tabela fatorial (conforme implementada). pegando 4 caracteres e minimizando cálculos inúteis. Tudo após a terceira iteração, você está lidando com matemática de dois dígitos plus. Eu imagino que se você fosse um defensor o suficiente sobre isso, você executaria em uma tabela fatorial (conforme implementada). pegando 4 caracteres e minimizando cálculos inúteis. Tudo após a terceira iteração, você está lidando com matemática de dois dígitos plus. Eu imagino que se você fosse um defensor o suficiente sobre isso, você executaria em uma tabela fatorial (conforme implementada).

O que eu planejei depois disso? armazenamento local & | sessão para permitir um cache caso a caso de iterações necessárias, essencialmente lidando com o problema da "tabela" falado acima. Isso também economizaria muito espaço no banco de dados e no servidor. No entanto, se você escolher localStorage, estará essencialmente sugando espaço no computador de seus usuários simplesmente para armazenar uma lista de números e fazer a tela PARECER mais rápida; no entanto, durante um longo período de tempo com uma necessidade imensa, isso seria lento. Estou pensando que sessionStorage (limpar após a saída de Tab) seria um caminho muito melhor. Possivelmente combinar isso com um servidor de auto-equilíbrio / cache dependente local? O usuário A precisa de X iterações. O usuário B precisa de Y iterações. X + Y / 2 = Quantidade necessária em cache local. Em seguida, basta detectar e mexer nos benchmarks de tempo de carregamento e tempo de execução ao vivo para cada usuário até que ele se ajuste à otimização do próprio site. Obrigado!

Editar 3:

var f=[1,2,6];
var fc=3;
var factorial = (function(memo) {
    this.memomize = (function(n) {
        var ni=n-1;
        if(fc<n) {
            for(var fi=fc-1;fc<n;fi++) {
                f[fc]=f[fi]*(fc+1);
                fc++;
            }
        }
        return f[ni];
    });

    this.factorialize = (function(n) {
        return (n<3)?n:(factorialize(n-1)*n);
    });

    this.fractal = (function (functio) {
        return function(n) {
            if(isNumeric(n)) {
                return functio(n);
            }
            return NaN;
        }
    });

    if(memo===true) {
        return this.fractal(memomize);
    }
    return this.fractal(factorialize);
});

Esta edição implementa outra sugestão de Stack e me permite chamar a função como fatorial (true) (5), que foi um dos meus objetivos estabelecidos. : 3 Também removi algumas atribuições desnecessárias e abreviei alguns nomes de variáveis ​​não públicas.

Sir Christopher Michael-Don Ro
fonte
Retorna undefinedpara 0 !. ES6 permite substituir isNumericcom Number.isInteger. Linhas como factorials[0][factorials[1]]=factorials[0][factorial_index]*(factorials[1]+1);são totalmente ilegíveis.
le_m
2

Aqui está um usando as funções javascript mais recentes preencher , mapear , reduzir e construtor (e sintaxe de seta grande):

Math.factorial = n => n === 0 ? 1 : Array(n).fill(null).map((e,i)=>i+1).reduce((p,c)=>p*c)

Editar: atualizado para lidar com n === 0

Ashley Coolman
fonte
2
Essa é uma linha de código ilegível muito feia.
jungledev
1
É uma boa ideia. Em vez de percorrer o comprimento duas vezes, por que não converter toda a lógica para a função de redução e usar seu valor inicial para lidar com o caso extremo n === 0? Math.factorial = n => Array.from({ length: n }).reduce((product, _, i) => product * (i + 1), 1)
AlexSashaRegan de
2
function computeFactorialOfN(n) {
  var output=1;
  for(i=1; i<=n; i++){
    output*=i;
  } return output;
}
computeFactorialOfN(5);
Odiljon Djamalov
fonte
2
Bem-vindo ao StackOverflow e obrigado por sua ajuda. Você pode querer tornar sua resposta ainda melhor adicionando alguma explicação.
Elias MP