Alla ricerca di un'implementazione molto rapida della funzione fattoriale in JavaScript. Qualcuno suggerisce?
Alla ricerca di un'implementazione molto rapida della funzione fattoriale in JavaScript. Qualcuno suggerisce?
Risposte:
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;
}
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.
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 BigInt
piuttosto che una libreria di terze parti.
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 ..
rval = rval * i;
scrivere tu potresti scrivererval *= i;
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.
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 Number
tipo di dati, che è 170 !.
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ù.
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
f=n=>n?f(n-1)*n:1
...
Solo una riga con ES6
const factorial = n => !(n > 1) ? 1 : factorial(n - 1) * n;
factorial = n => n <= 1 ? 1 : factorial(n - 1) * n
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).
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.
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:
for
loop e while
loop hanno prestazioni simili, un for
ciclo senza un'espressione di inizializzazione e un'espressione finale sembra strano; probabilmente meglio scrivere for(; n > 0;)
comewhile(n > 0)
n
e r
, quindi in teoria meno parametri significa meno tempo impiegato per allocare la memorian
è zero - Ho sentito teorie secondo cui i computer sono più bravi a controllare i numeri binari (0 e 1) che a controllare altri numeri interiMi 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 :)
Ecco una soluzione:
function factorial(number) {
total = 1
while (number > 0) {
total *= number
number = number - 1
}
return total
}
Usando ES6 puoi ottenerlo sia veloce che breve:
const factorial = n => [...Array(n + 1).keys()].slice(1).reduce((acc, cur) => acc * cur, 1)
Il codice per calcolare il fattoriale dipende dalle tue esigenze.
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.
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 ...
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 Number
tipo 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.
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
BigInt
per sicurezzaLa soluzione utilizza
BigInt
una funzionalità ES 2018 + / 2019.
Questo è un esempio funzionante BigInt
, perché molte risposte qui sfuggono Number
quasi 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
}
// 9.332621544394415e+157
Number(factorial(100))
// "933262154439441526816992388562667004907159682643816214685929638952175999
// 932299156089414639761565182862536979208272237582511852109168640000000000
// 00000000000000"
String(factorial(100))
// 9332621544394415268169923885626670049071596826438162146859296389521759999
// 3229915608941463976156518286253697920827223758251185210916864000000000000
// 000000000000n
factorial(100)
n
fine di un letterale numerico come 1303n
indica che è un BigInt
tipo.BigInt
a Number
meno che non li costringa esplicitamente, e che ciò potrebbe causare una perdita di precisione.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)
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);
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.
Math.factorial(100); Math.factorial(500);
calcolerà la moltiplicazione 1..100 due volte.
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;
};
n * (n-1) * (n-2) * ... * 1
invece del contrario,
// 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.
21! > Number.MAX_SAFE_INTEGER
, quindi, non può essere rappresentato in modo sicuro come un float a 64 bit.
Ecco un'implementazione che calcola fattoriali sia positivi che negativi. È semplice e veloce.
var factorial = function(n) {
return n > 1
? n * factorial(n - 1)
: n < 0
? n * factorial(n + 1)
: 1;
}
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;
}
i
ed esegue troppe Number
conversioni e fornisce risultati errati per 0! (come hai affermato, ma perché?).
Ecco il mio codice
function factorial(num){
var result = num;
for(i=num;i>=2;i--){
result = result * (i-1);
}
return result;
}
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
.
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.
undefined
per 0 !. ES6 permette di sostituire isNumeric
con Number.isInteger
. Righe come factorials[0][factorials[1]]=factorials[0][factorial_index]*(factorials[1]+1);
sono totalmente illeggibili.
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
n === 0
? Math.factorial = n => Array.from({ length: n }).reduce((product, _, i) => product * (i + 1), 1)
function computeFactorialOfN(n) {
var output=1;
for(i=1; i<=n; i++){
output*=i;
} return output;
}
computeFactorialOfN(5);