Qual è la funzione fattoriale più veloce in JavaScript? [chiuso]


94

Alla ricerca di un'implementazione molto rapida della funzione fattoriale in JavaScript. Qualcuno suggerisce?


8
Qual è la possibile gamma di argomenti?
Nikita Rybak

5
Hai preso in considerazione il calcolo preliminare dei fattoriali e la memorizzazione dei valori in una tabella di ricerca?
Waleed Amjad

2
Qual è l'applicazione di una tale funzione? In altre parole, per cosa lo userai?
Pointy

@Nikita Rybak, solo 1 agrumento (n). If (n> 170) e = Infinity
Ken

@ Pointy, ancora un altro servizio di calcolatrice matematica.
Ken

Risposte:


110

Puoi cercare (1 ... 100)! su Wolfram | Alpha per pre-calcolare la sequenza fattoriale.

I primi 100 numeri sono:

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 desideri comunque calcolare i valori da solo, puoi utilizzare la memorizzazione :

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;
}

Modifica: 21.08.2014

Soluzione 2

Ho pensato che sarebbe stato utile aggiungere un esempio funzionante di funzione fattoriale iterativa pigra che utilizza numeri grandi per ottenere risultati esatti con memoizzazione e cache come confronto

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 che useresti una sorta di chiusura per limitare la visibilità del nome della variabile.

Rif : BigNumber Sandbox : JsFiddle


I valori oltre 6402373705728000 verranno troncati, quindi se utilizzerai questo approccio assicurati di convertirlo in esponenziale prima di utilizzare la tabella di cui sopra.
David Scott Kirby

1
@DavidScottKirby Javascript converte automaticamente questi numeri nella loro rappresentazione float a 64 bit più vicina. Il vero vantaggio di non avere i numeri di precisione completa nel codice è la dimensione del file ridotta.
le_m

La tua seconda soluzione potrebbe essere semplificata per function factorial (n) { for (var i = f.length; i <= n; i++) f.push(f[i - 1].multiply(i.toString())); return f[n]; }vedere anche la mia risposta che utilizza il builtin più recente BigIntpiuttosto che una libreria di terze parti.
Patrick Roberts,

96

Dovresti usare un ciclo.

Qui ci sono due versioni confrontate calcolando il fattoriale di 100 per 10.000 volte.

Ricorsivo

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;
}

Dal vivo su: http://jsfiddle.net/xMpTv/

I miei risultati mostrano:
- Ricorsivo ~ 150 millisecondi
- Iterativo ~ 5 millisecondi ..


+1 Ottima risposta! Sebbene la memorizzazione possa essere ragionevole quando ci sono più chiamate per calcolare fattoriali per numeri più grandi.
Tadeck

@Tadeck, grazie. Effettivamente la memoizzazione è molto utile in questo caso ed è per questo che la risposta di Margus è scelta come quella corretta :)
Gabriele Petrioli

Una versione a 1 riga di ricorsivo: funzione fattoriale (num) {return (num == 1)? num: num * arguments.callee (num-1); }
jbyrd

2
@ HWTech, non chiami mai i metodi. Il tuo test confronta la velocità di definizione dei due metodi .. non il tempo che impiegano per eseguire .. Questo è un test
Gabriele Petrioli

3
Invece di rval = rval * i;scrivere tu potresti scrivererval *= i;
Ryan

29

Penso ancora che la risposta di Margus sia la migliore. Tuttavia, se si desidera calcolare anche i fattoriali dei numeri nell'intervallo da 0 a 1 (cioè la funzione gamma), non è possibile utilizzare tale approccio perché la tabella di ricerca dovrà contenere valori infiniti.

Tuttavia, puoi approssimare i valori dei fattoriali, ed è abbastanza veloce, più veloce che chiamare se stesso ricorsivamente o almeno ripetendolo (specialmente quando i valori iniziano a diventare più grandi).

Un buon metodo di approssimazione è quello di Lanczos

Ecco un'implementazione in JavaScript (trasferita da una calcolatrice che ho scritto mesi fa):

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;
}

Ora puoi fare cose interessanti come factorial(0.41), ecc.Tuttavia la precisione potrebbe essere un po 'fuori, dopotutto, è un'approssimazione del risultato.


approccio piuttosto interessante, grazie.
Ken

Mi ha fatto risparmiare un sacco di tempo, grazie mille :)
nicolaskruchten

Consiglio di cambiare la parte sotto il ciclo for in var d3d4 = Math.exp((z + 0.5) * Math.log(z + 5.5) - z - 5.5); return d1 * d2 * d3d4;. Ciò consente di calcolare fattoriali fino a 169! invece di attualmente solo 140 !. Questo è abbastanza vicino al fattoriale massimo rappresentabile utilizzando il Numbertipo di dati, che è 170 !.
le_m

18

La tabella di ricerca è il modo più ovvio, se stai lavorando con numeri naturali. Per calcolare qualsiasi fattoriale in tempo reale, puoi velocizzarlo con una cache, salvando i numeri che hai calcolato in precedenza. Qualcosa di simile a:

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;
})();

Puoi precalcolare alcuni valori per velocizzarlo ancora di più.


3
Ho creato un memoizer automatico per qualsiasi funzione in base a questa risposta (anche leggermente più veloce :)), includendo anche un limite alla dimensione della cache. stackoverflow.com/a/10031674/36537
Phil H

16

Ecco la mia soluzione:

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

È il modo più semplice (meno caratteri / righe) che ho trovato, solo una funzione con una riga di codice.


Modifica:
se vuoi davvero salvare alcuni caratteri puoi andare con una funzione freccia (21 byte) :

f=n=>(n<2)?1:f(n-1)*n

7
Risparmia ancora di più con f=n=>n?f(n-1)*n:1...
le_m

purtroppo anche se è bello da vedere e di forma corta, questo è il modo più lento per farlo.
Zibri

11

Solo una riga con ES6

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


factorial = n => n <= 1 ? 1 : factorial(n - 1) * n
Naramsim,

10

funzione ricorsiva breve e facile (potresti farlo anche con un ciclo, ma non credo che farebbe differenza in termini di prestazioni):

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

per una n molto grande, potresti usare l' approssimazione di stirlings , ma questo ti darà solo un valore approssimativo.

EDIT: un commento sul motivo per cui sto ricevendo un voto negativo per questo sarebbe stato carino ...

EDIT2: questa sarebbe l'anima usando un loop (che sarebbe la scelta migliore):

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

Penso che la soluzione migliore sarebbe quella di utilizzare i valori memorizzati nella cache, come menzionato da Margus e utilizzare l' approssimazione di stirlings per valori più grandi (supponendo che si debba essere molto veloci e non sia necessario essere così esatti su numeri così grandi).


3
Nelle lingue senza l'ottimizzazione della chiamata di coda (cioè i linguaggi più utilizzati) è meglio usare un'implementazione non ricorsiva dove è facile farlo, sebbene ci siano modi per aggirarla
Daniel Earwicker

in effetti non è decisamente così veloce, poiché non utilizzerebbe nemmeno il TCO, se fosse implementato. Ma è semplice e non lo svuoterei. Non è sicuramente il più veloce.
haylem

L'ottimizzazione della chiamata di coda non è nemmeno possibile per questa funzione, poiché la chiamata ricorsiva non è in posizione di coda.
Fred Foo

3
@ Josh, ( non il downvoter ) il più veloce è il loop con un discreto margine ..
Gabriele Petrioli

7

Ecco, il memoizer, che accetta qualsiasi funzione a singolo argomento e la memorizza. Risulta essere leggermente più veloce della soluzione di @ xPheRe , compreso il limite sulla dimensione della cache e il controllo associato, perché utilizzo il cortocircuito e così via.

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;
}());

Circa 25 volte più veloce sulla mia macchina in Chrome rispetto alla versione ricorsiva e il 10% più veloce di xPheRe.


6

Funzione fattoriale più veloce

Penso che questa versione basata su loop potrebbe essere la funzione fattoriale più veloce.

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

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

Ed ecco il mio ragionamento:

  • Le funzioni ricorsive, anche con la memoizzazione, hanno l'overhead di una chiamata di funzione (fondamentalmente inserendo le funzioni nello stack) che è meno performante rispetto all'utilizzo di un ciclo
  • Mentre forloop e whileloop hanno prestazioni simili, un forciclo senza un'espressione di inizializzazione e un'espressione finale sembra strano; probabilmente meglio scrivere for(; n > 0;)comewhile(n > 0)
  • Vengono utilizzati solo due parametri ne r, quindi in teoria meno parametri significa meno tempo impiegato per allocare la memoria
  • Utilizza un ciclo decrementato che controlla se nè zero - Ho sentito teorie secondo cui i computer sono più bravi a controllare i numeri binari (0 e 1) che a controllare altri numeri interi

5

Mi sono imbattuto in questo post. Ispirato da tutti i contributi qui, ho ideato la mia versione, che ha due caratteristiche che non ho visto discusse prima: 1) Un controllo per assicurarsi che l'argomento sia un intero non negativo 2) Creare un'unità dalla cache e la funzione per renderlo un bit di codice autonomo. Per gioco, ho cercato di renderlo il più compatto possibile. Alcuni potrebbero trovarlo elegante, altri potrebbero pensare che sia terribilmente oscuro. Comunque, eccolo qui:

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];

Puoi precompilare la cache o consentire che venga riempita man mano che le chiamate passano. Ma l'elemento iniziale (per fact (0) deve essere presente o si romperà.

Godere :)


4

È molto semplice usare ES6

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

Vedi un esempio qui


4

Ecco una soluzione:

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

4

Usando ES6 puoi ottenerlo sia veloce che breve:

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

3

Il codice per calcolare il fattoriale dipende dalle tue esigenze.

  1. Sei preoccupato per il trabocco?
  2. Quale gamma di input avrai?
  3. È più importante per te ridurre al minimo le dimensioni o il tempo?
  4. Cosa farai con il fattoriale?

Per quanto riguarda i punti 1 e 4, spesso è più utile avere una funzione per valutare il log del fattoriale piuttosto che avere una funzione per valutare il fattoriale stesso.

Ecco un post sul blog che discute di questi problemi. Di seguito è riportato del codice C # per il calcolo del fattoriale dei log che sarebbe banale da trasferire in JavaScript. Ma potrebbe non essere il migliore per le tue esigenze a seconda delle tue risposte alle domande sopra.


L'elenco numerato probabilmente dovrebbe essere nei commenti. Tutto ciò che resta sono due collegamenti e le risposte di soli collegamenti sono scoraggiate.
Barett

3

Questa è una versione compatta basata su loop

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

Oppure potresti sovrascrivere l'oggetto Math (versione ricorsiva):

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

O unisci entrambi gli approcci ...


1
L'ho risolto all'interno del codice sopra. Grazie!
Sandro Rosa

3

Sfruttando il fatto che Number.MAX_VALUE < 171!, possiamo semplicemente utilizzare una tabella di ricerca completa composta da soli 171 elementi di array compatti che occupano meno di 1,4 kilobyte di memoria.

Una funzione di ricerca rapida con complessità di runtime O (1) e un sovraccarico minimo di accesso all'array sarebbe quindi il seguente:

// 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

Questo è tanto preciso e veloce quanto si ottiene utilizzando il Numbertipo di dati. Il calcolo della tabella di ricerca in Javascript, come suggeriscono altre risposte, ridurrà la precisione quando n! > Number.MAX_SAFE_INTEGER.

La compressione della tabella di runtime tramite gzip riduce la sua dimensione su disco da circa 3,6 a 1,8 kilobyte.


3

Risposta in una riga:

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


3

Fattoriale iterativo con BigIntper sicurezza

La soluzione utilizza BigIntuna funzionalità ES 2018 + / 2019.

Questo è un esempio funzionante BigInt, perché molte risposte qui sfuggono Numberquasi subito al confine sicuro di (MDN). Non è il più veloce ma è semplice e quindi più chiaro per adattare altre ottimizzazioni (come una cache dei primi 100 numeri).

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

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

   return p
}

Utilizzo di esempio

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

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

// 9332621544394415268169923885626670049071596826438162146859296389521759999
// 3229915608941463976156518286253697920827223758251185210916864000000000000
// 000000000000n
factorial(100)
  • Alla nfine di un letterale numerico come 1303nindica che è un BigInttipo.
  • Ricorda che non dovresti mescolarli BigInta Numbermeno che non li costringa esplicitamente, e che ciò potrebbe causare una perdita di precisione.

3

Utilizzando le funzionalità ES6, puoi scrivere codice su UNA riga e senza ricorsione :

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


2

Solo per completezza, ecco una versione ricorsiva che consentirebbe l'ottimizzazione delle chiamate di coda. Tuttavia, non sono sicuro che le ottimizzazioni delle chiamate di coda vengano eseguite in JavaScript ..

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

Per chiamarlo:

rFact(x, 1);

ES6 supporta il TCO, ma questa funzione non è ancora attiva per impostazione predefinita in nessun motore principale
le_m

2

Questa è una soluzione iterativa che utilizza meno spazio nello stack e salva i valori calcolati in precedenza in modo auto-memo:

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

Si noti inoltre che lo sto aggiungendo all'oggetto Math che è un oggetto letterale, quindi non esiste un prototipo. Piuttosto semplicemente legandoli direttamente alla funzione.


Questo non sfrutta appieno la memorizzazione per i sottoproblemi, ad esempio, Math.factorial(100); Math.factorial(500);calcolerà la moltiplicazione 1..100 due volte.
Barett

2

Credo che quanto segue sia il pezzo di codice più sostenibile ed efficiente dai commenti sopra. Puoi usarlo nell'architettura js della tua applicazione globale ... e, non preoccuparti di scriverlo in più spazi dei nomi (poiché è un'attività che probabilmente non necessita di molti aumenti). Ho incluso 2 nomi di metodo (in base alle preferenze) ma entrambi possono essere utilizzati in quanto sono solo riferimenti.

Math.factorial = Math.fact = function(n) {
    if (isNaN(n)||n<0) return undefined;
    var f = 1; while (n > 1) {
        f *= n--;
    } return f;
};

Iniziando la moltiplicazione con n * (n-1) * (n-2) * ... * 1invece del contrario,
perdi

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

Questo esegue la memorizzazione nella cache dei primi 100 valori al volo e non introduce una variabile esterna nell'ambito della cache, memorizzando i valori come proprietà dell'oggetto funzione stesso, il che significa che se sai che factorial(n)è già stato calcolato, puoi semplicemente chiamalo factorial[n], che è leggermente più efficiente. L'esecuzione di questi primi 100 valori richiederà un tempo inferiore al millisecondo nei browser moderni.


L'ho capito dopo le 21! i numeri non sono affidabili.
AutoSponge

@AutoSponge Questo perché 21! > Number.MAX_SAFE_INTEGER, quindi, non può essere rappresentato in modo sicuro come un float a 64 bit.
le_m


2

Eccone uno che ho fatto io, non usare numeri superiori a 170 o inferiori a 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;
}

Iniziando la moltiplicazione con n * (n-1) * (n-2) * ... * 1 invece del contrario, perdi fino a 4 cifre di precisione per n >> 20. Inoltre, crea un indesiderato variabile globale ied esegue troppe Numberconversioni e fornisce risultati errati per 0! (come hai affermato, ma perché?).
le_m

2

Ecco il mio codice

function factorial(num){
    var result = num;
    for(i=num;i>=2;i--){
        result = result * (i-1);
    }
    return result;
}

1
Se (n> 170) e = Infinito. E il tuo codice genererà un numero enorme. non ci saranno overflow?
Prime

Risultato errato per factorial(0). Inoltre, iniziando la moltiplicazione con n * (n-1) * (n-2) * ... * 1 invece del contrario, perdi fino a 4 cifre di precisione per n >> 20. @prime:170! > Number.MAX_VALUE ed è rappresentato al meglio con Infinity.
le_m

2

Il ciclo memorizzato nella cache dovrebbe essere più veloce (almeno se chiamato più volte)

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;
  }
})();

2
function isNumeric(n) {
    return !isNaN(parseFloat(n)) && isFinite(n)
}

Fornito da http://javascript.info/tutorial/number-math come un modo semplice per valutare se un oggetto è un numero intero corretto per il calcolo.

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

Un semplice insieme di fattoriali memorizzati che richiedono calcoli ridondanti, possono essere elaborati con "moltiplicare per 1" o sono una cifra che è una semplice equazione che non vale la pena elaborare in tempo reale.

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;
});

Dopo aver esaminato l'input di altri membri (escluso il consiglio di registro, anche se potrei implementarlo in seguito) sono andato avanti e ho messo insieme uno script che è abbastanza semplice. Ho iniziato con un semplice esempio JavaScript OOP non istruito e ho creato una piccola classe per gestire i fattoriali. Ho quindi implementato la mia versione della Memoization suggerita sopra. Ho anche implementato la fattorializzazione abbreviata, ma ho fatto un piccolo aggiustamento dell'errore; Ho cambiato "n <2" in "n <3". "n <2" elaborerebbe ancora n = 2, il che sarebbe uno spreco, perché itererebbe per un 2 * 1 = 2; questo è uno spreco secondo me. L'ho modificato in "n <3"; perché se n è 1 o 2 restituirà semplicemente n, se è 3 o più verrà valutato normalmente. Ovviamente, poiché le regole si applicano, ho posizionato le mie funzioni in ordine decrescente di esecuzione presunta. Ho aggiunto l'opzione bool (true | false) per consentire una rapida alterazione tra l'esecuzione di memo e la normale (non sai mai quando vuoi scambiare sulla tua pagina senza bisogno di cambiare lo "stile") Come ho detto prima La variabile fattoriale memorizzata è impostata con le 3 posizioni iniziali, prendendo 4 caratteri e riducendo al minimo i calcoli dispendiosi. Tutto oltre la terza iterazione che stai gestendo con la matematica a due cifre più. Immagino che se fossi abbastanza pignolo su di esso correresti su una tabella fattoriale (come implementata). prendendo 4 caratteri e riducendo al minimo i calcoli dispendiosi. Tutto oltre la terza iterazione che stai gestendo con la matematica a due cifre più. Immagino che se fossi abbastanza pignolo su di esso correresti su una tabella fattoriale (come implementata). prendendo 4 caratteri e riducendo al minimo i calcoli dispendiosi. Tutto oltre la terza iterazione che stai gestendo con la matematica a due cifre più. Immagino che se fossi abbastanza pignolo su di esso correresti su una tabella fattoriale (come implementata).

Cosa ho pianificato dopo? archiviazione locale e di sessione per consentire una cache caso per caso delle iterazioni necessarie, gestendo essenzialmente il problema della "tabella" di cui sopra. Ciò consentirebbe anche un enorme risparmio di spazio sul database e sul server. Tuttavia, se si utilizza localStorage, si risucchia essenzialmente spazio sul computer degli utenti semplicemente per memorizzare un elenco di numeri e rendere il loro schermo VELOCE più veloce, tuttavia per un lungo periodo di tempo con un immenso bisogno questo sarebbe lento. Sto pensando che sessionStorage (cancellazione dopo che Tab se ne va) sarebbe un percorso molto migliore. Possibilmente combinarlo con un server autobilanciato / cache dipendente locale? L'utente A necessita di X iterazioni. L'utente B necessita di iterazioni Y. X + Y / 2 = importo necessario memorizzato nella cache locale. Quindi rileva e giocherella con i benchmark sul tempo di caricamento e sul tempo di esecuzione dal vivo per ogni utente fino a quando non si adegua all'ottimizzazione per il sito stesso. Grazie!

Modifica 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);
});

Questa modifica implementa un altro suggerimento di Stack e mi consente di chiamare la funzione come fattoriale (true) (5), che era uno dei miei obiettivi stabiliti. : 3 Ho anche rimosso alcune assegnazioni inutili e ho ridotto alcuni nomi di variabili non pubbliche.


Restituisce undefinedper 0 !. ES6 permette di sostituire isNumericcon Number.isInteger. Righe come factorials[0][factorials[1]]=factorials[0][factorial_index]*(factorials[1]+1);sono totalmente illeggibili.
le_m

2

Eccone uno che utilizza le nuove funzioni javascript fill , map , reduce e constructor (e la sintassi della freccia grassa):

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

Modifica: aggiornato per gestire n === 0


2
Questa è una riga di codice davvero brutta e illeggibile.
jungledev

1
È un'idea geniale. Invece di attraversare la lunghezza due volte, perché non convertire tutta la logica nella funzione di riduzione e utilizzare il suo valore iniziale per gestire il caso limite n === 0? Math.factorial = n => Array.from({ length: n }).reduce((product, _, i) => product * (i + 1), 1)
AlexSashaRegan

2
function computeFactorialOfN(n) {
  var output=1;
  for(i=1; i<=n; i++){
    output*=i;
  } return output;
}
computeFactorialOfN(5);

2
Benvenuto in StackOverflow e grazie per il tuo aiuto. Potresti voler migliorare la tua risposta aggiungendo qualche spiegazione.
Elias MP
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.