Buscando una implementación realmente rápida de la función factorial en JavaScript. ¿Alguna sugerencia?
Buscando una implementación realmente rápida de la función factorial en JavaScript. ¿Alguna sugerencia?
Respuestas:
¡Puede buscar (1 ... 100)! en Wolfram | Alpha para calcular previamente la secuencia factorial.
Los primeros 100 números son:
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
Si aún desea calcular los valores usted mismo, puede utilizar la memorización :
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;
}
Pensé que sería útil agregar un ejemplo funcional de función factorial iterativa perezosa que usa números grandes para obtener un resultado exacto con memorización y caché como comparación
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);
Supongo que usaría algún tipo de cierre para limitar la visibilidad del nombre de la variable.
function factorial (n) { for (var i = f.length; i <= n; i++) f.push(f[i - 1].multiply(i.toString())); return f[n]; }
ver también mi respuesta, que utiliza la BigInt
biblioteca incorporada más reciente en lugar de una biblioteca de terceros.
Deberías usar un bucle.
Aquí hay dos versiones comparadas al calcular el factorial de 100 por 10.000 veces.
Recursivo
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;
}
En vivo en: http://jsfiddle.net/xMpTv/
Mis resultados muestran:
- Recursivo ~ 150 milisegundos
- Iterativo ~ 5 milisegundos ..
rval = rval * i;
usted podría escribirrval *= i;
Sigo pensando que la respuesta de Margus es la mejor. Sin embargo, si también desea calcular los factoriales de números dentro del rango de 0 a 1 (es decir, la función gamma), entonces no puede usar ese enfoque porque la tabla de búsqueda tendrá que contener valores infinitos.
Sin embargo, puede aproximar los valores de los factoriales, y es bastante rápido, más rápido que llamarse a sí mismo de forma recursiva o hacer un bucle al menos (especialmente cuando los valores comienzan a aumentar).
Un buen método de aproximación es el de Lanczos.
Aquí hay una implementación en JavaScript (portado desde una calculadora que escribí hace meses):
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;
}
Ahora puede hacer cosas interesantes como factorial(0.41)
, etc., sin embargo, la precisión puede estar un poco fuera de lugar, después de todo, es una aproximación del resultado.
var d3d4 = Math.exp((z + 0.5) * Math.log(z + 5.5) - z - 5.5); return d1 * d2 * d3d4;
. ¡Esto le permite calcular factoriales hasta 169! en lugar de actualmente solo 140 !. Esto está bastante cerca del factorial máximo representable usando el Number
tipo de datos, que es 170 !.
La tabla de búsqueda es la forma obvia de hacerlo, si está trabajando con números naturales. Para calcular cualquier factorial en tiempo real, puede acelerarlo con un caché, guardando los números que ha calculado 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;
})();
Puede calcular previamente algunos valores para acelerarlo aún más.
Aquí está mi solución:
function fac(n){
return(n<2)?1:fac(n-1)*n;
}
Es la forma más sencilla (menos caracteres / líneas) que he encontrado, solo una función con una línea de código.
Editar:
si realmente desea guardar algunos caracteres, puede ir con una función de flecha (21 bytes) :
f=n=>(n<2)?1:f(n-1)*n
f=n=>n?f(n-1)*n:1
...
Solo una línea con ES6
const factorial = n => !(n > 1) ? 1 : factorial(n - 1) * n;
factorial = n => n <= 1 ? 1 : factorial(n - 1) * n
función recursiva corta y fácil (también podría hacerlo con un bucle, pero no creo que eso haga ninguna diferencia en el rendimiento):
function factorial (n){
if (n==0 || n==1){
return 1;
}
return factorial(n-1)*n;
}
para una n muy grande, podría usar la aproximación de Stirlings , pero eso solo le dará un valor aproximado.
EDITAR: un comentario sobre por qué recibo un voto negativo por esto hubiera sido bueno ...
EDIT2: esta sería la solución usando un bucle (que sería la mejor opción):
function factorial (n){
j = 1;
for(i=1;i<=n;i++){
j = j*i;
}
return j;
}
Creo que la mejor solución sería usar los valores en caché, como mencionó Margus, y usar la aproximación de Stirlings para valores más grandes (asumiendo que tienes que ser muy rápido y no tienes que ser tan exacto en números tan grandes).
He aquí, el memorizador, que toma cualquier función de un solo argumento y la memoriza. Resulta ser marginalmente más rápido que la solución de @ xPheRe , incluido el límite en el tamaño de la caché y la verificación asociada, porque uso cortocircuitos, etc.
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 25 veces más rápido en mi máquina en Chrome que la versión recursiva, y 10% más rápido que xPheRe.
Creo que esta versión basada en bucles podría ser la función factorial más rápida.
function factorial(n, r = 1) {
while (n > 0) r *= n--;
return r;
}
// Default parameters `r = 1`,
// was introduced in ES6
Y aquí está mi razonamiento:
for
bucles y los while
bucles tienen un rendimiento similar, un for
bucle sin una expresión de inicialización y una expresión final parece extraño; probablemente sea mejor escribir for(; n > 0;)
comowhile(n > 0)
n
y r
, por lo que, en teoría, menos parámetros significa menos tiempo dedicado a la asignación de memorian
es cero: he escuchado teorías de que las computadoras son mejores para verificar números binarios (0 y 1) que para verificar otros enterosMe encontré con esta publicación. Inspirado por todas las contribuciones aquí, se me ocurrió mi propia versión, que tiene dos características que no había visto antes discutidas: 1) Una verificación para asegurar que el argumento sea un número entero no negativo 2) Hacer una unidad del caché y la función para convertirlo en un bit de código autónomo. Para divertirme, traté de hacerlo lo más compacto posible. Algunos pueden encontrar eso elegante, otros pueden pensar que es terriblemente oscuro. De todos modos, aquí 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];
Puede llenar previamente el caché o permitir que se llene a medida que pasan las llamadas. Pero el elemento inicial (de hecho (0) debe estar presente o se romperá.
Disfruta :)
Aquí hay una solución:
function factorial(number) {
total = 1
while (number > 0) {
total *= number
number = number - 1
}
return total
}
Usando ES6 puede lograrlo rápido y corto:
const factorial = n => [...Array(n + 1).keys()].slice(1).reduce((acc, cur) => acc * cur, 1)
El código para calcular factorial depende de sus requisitos.
Con respecto a los puntos 1 y 4, a menudo es más útil tener una función para evaluar el logaritmo del factorial directamente que tener una función para evaluar el factorial en sí.
Aquí hay una publicación de blog que analiza estos problemas. Aquí hay algo de código C # para calcular el factorial de registro que sería trivial para migrar a JavaScript. Pero puede que no sea lo mejor para sus necesidades dependiendo de sus respuestas a las preguntas anteriores.
Esta es una versión compacta basada en bucle
function factorial( _n )
{
var _p = 1 ;
while( _n > 0 ) { _p *= _n-- ; }
return _p ;
}
O puede anular el objeto Math (versión recursiva):
Math.factorial = function( _x ) { return _x <= 1 ? 1 : _x * Math.factorial( --_x ) ; }
O únete a ambos enfoques ...
Aprovechando el hecho de que Number.MAX_VALUE < 171!
, simplemente podemos usar una tabla de búsqueda completa que consta de solo 171 elementos de matriz compacta que ocupan menos de 1.4 kilobytes de memoria.
Una función de búsqueda rápida con complejidad en tiempo de ejecución O (1) y una sobrecarga mínima de acceso a la matriz se vería de la siguiente manera:
// 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
Esto es tan preciso y rápido como se obtiene utilizando el Number
tipo de datos. Calcular la tabla de búsqueda en Javascript, como sugieren otras respuestas, reducirá la precisión cuando n! > Number.MAX_SAFE_INTEGER
.
Comprimir la tabla de tiempo de ejecución a través de gzip reduce su tamaño en el disco de aproximadamente 3.6 a 1.8 kilobytes.
Respuesta de una línea:
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
seguridadUsos de la solución
BigInt
, una característica de ES 2018 + / 2019.
Este es un ejemplo de uso BigInt
, porque muchas respuestas aquí escapan del límite seguro de Number
(MDN) casi de inmediato. No es el más rápido, pero es simple y, por lo tanto, más claro para adaptar otras optimizaciones (como un caché de los primeros 100 números).
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
al final de un literal numérico como 1303n
indica que es un BigInt
tipo.BigInt
con a Number
menos que los obligue explícitamente, y que hacerlo podría causar una pérdida de precisión.Usando las funciones de ES6, puede escribir código en UNA línea y sin recursividad :
var factorial=(n)=>Array.from({length: n},(v, k) => k+1).reduce((a, b) => a*b, 1)
Solo para completar, aquí hay una versión recursiva que permitiría la optimización de llamadas de cola. Sin embargo, no estoy seguro de si las optimizaciones de llamadas de cola se realizan en JavaScript ...
function rFact(n, acc)
{
if (n == 0 || n == 1) return acc;
else return rFact(n-1, acc*n);
}
Para llamarlo:
rFact(x, 1);
Esta es una solución iterativa que usa menos espacio de pila y guarda los valores calculados previamente de una manera auto-memorizada:
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
También tenga en cuenta que estoy agregando esto al objeto Math, que es un objeto literal, por lo que no hay prototipo. En lugar de eso, simplemente vincularlos a la función directamente.
Math.factorial(100); Math.factorial(500);
calculará la multiplicación del 1 ... 100 dos veces.
Creo que el siguiente es el fragmento de código más sostenible y eficiente de los comentarios anteriores. Puede usar esto en la arquitectura js de su aplicación global ... y no se preocupe por escribirlo en múltiples espacios de nombres (ya que es una tarea que probablemente no necesite mucho aumento). He incluido 2 nombres de métodos (según la preferencia) pero ambos se pueden usar ya que son solo referencias.
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
lugar de al revés, pierde hasta 4 dígitos en precisión para n >> 20.
// 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
Esto hace el almacenamiento en caché de los primeros 100 valores sobre la marcha y no introduce una variable externa en el alcance de la caché, almacenando los valores como propiedades del objeto de función en sí, lo que significa que si sabe que factorial(n)
ya se ha calculado, puede simplemente refiérase a él como factorial[n]
, que es un poco más eficiente. Ejecutar estos primeros 100 valores tomará menos de milisegundos en los navegadores modernos.
21! > Number.MAX_SAFE_INTEGER
, por lo tanto, no se puede representar con seguridad como un flotante de 64 bits.
Aquí hay una implementación que calcula factoriales positivos y negativos. Es rápido y sencillo.
var factorial = function(n) {
return n > 1
? n * factorial(n - 1)
: n < 0
? n * factorial(n + 1)
: 1;
}
Aquí hay uno que hice yo mismo, no use números mayores de 170 o menores 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;
}
i
y realiza demasiadas Number
conversiones y da resultados incorrectos para 0! (como dijiste, pero ¿por qué?).
Aqui esta mi codigo
function factorial(num){
var result = num;
for(i=num;i>=2;i--){
result = result * (i-1);
}
return result;
}
factorial(0)
. Además, al comenzar su multiplicación con n * (n-1) * (n-2) * ... * 1 en lugar de al revés, pierde hasta 4 dígitos en precisión para n >> 20. @prime: 170! > Number.MAX_VALUE
y se representa mejor con Infinity
.
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n)
}
Proporcionado por http://javascript.info/tutorial/number-math como una forma sencilla de evaluar si un objeto es un número entero adecuado para el cálculo.
var factorials=[[1,2,6],3];
Un conjunto simple de factoriales memorizados que requieren cálculos redundantes, se pueden procesar con "multiplicar por 1", o son un dígito que es una ecuación simple que no vale la pena procesar en 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;
});
Después de revisar la información de otros miembros (excluyendo el consejo de Log, aunque puedo implementarlo más adelante), seguí adelante y preparé un guión que es bastante simple. Comencé con un ejemplo simple de programación orientada a objetos de JavaScript sin educación y construí una pequeña clase para manejar factoriales. Luego implementé mi versión de la Memoización que se sugirió anteriormente. También implementé la abreviatura Factorialización, sin embargo hice un pequeño ajuste de error; Cambié "n <2" a "n <3". "n <2" seguiría procesando n = 2, lo que sería un desperdicio, porque iteraría para un 2 * 1 = 2; esto es un desperdicio en mi opinión. Lo modifiqué a "n <3"; porque si n es 1 o 2 simplemente devolverá n, si es 3 o más se evaluará normalmente. Por supuesto, a medida que se aplican las reglas, coloqué mis funciones en orden descendente de ejecución asumida. Agregué la opción bool (verdadero | falso) para permitir la alteración rápida entre la ejecución normal y la memorizada (nunca se sabe cuándo desea cambiar de página sin necesidad de cambiar el "estilo") Como dije antes de La variable factorial memorizada se establece con las 3 posiciones iniciales, tomando 4 caracteres y minimizando los cálculos innecesarios. Todo lo que pasa después de la tercera iteración está manejando matemáticas de dos dígitos más. Me imagino que si fueras lo suficientemente riguroso al respecto, ejecutarías en una tabla factorial (como se implementó). tomando 4 caracteres y minimizando los cálculos innecesarios. Todo lo que pasa después de la tercera iteración está manejando matemáticas de dos dígitos más. Me imagino que si fueras lo suficientemente riguroso al respecto, ejecutarías en una tabla factorial (como se implementó). tomando 4 caracteres y minimizando los cálculos innecesarios. Todo lo que pasa después de la tercera iteración está manejando matemáticas de dos dígitos más. Me imagino que si fueras lo suficientemente riguroso al respecto, ejecutarías en una tabla factorial (como se implementó).
¿Qué he planeado después de esto? almacenamiento local & | sesión para permitir un caché caso por caso de las iteraciones necesarias, esencialmente manejando el problema de la "tabla" mencionado anteriormente. Esto también ahorraría enormemente el espacio del lado del servidor y la base de datos. Sin embargo, si opta por localStorage, esencialmente estaría absorbiendo espacio en la computadora de sus usuarios simplemente para almacenar una lista de números y hacer que su pantalla se vea más rápido, sin embargo, durante un largo período de tiempo con una inmensa necesidad, esto sería lento. Estoy pensando que sessionStorage (limpiar después de que Tab se vaya) sería una ruta mucho mejor. ¿Posiblemente combinar esto con un servidor autoequilibrado / caché dependiente local? El usuario A necesita X iteraciones. El usuario B necesita Y iteraciones. X + Y / 2 = Cantidad necesaria almacenada en caché local. Luego, simplemente detecte y juegue con los puntos de referencia de tiempo de carga y tiempo de ejecución en vivo para cada usuario hasta que se ajuste a la optimización del sitio en sí. ¡Gracias!
Edición 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 edición implementa otra sugerencia de Stack y me permite llamar a la función como factorial (verdadero) (5), que era uno de mis objetivos. : 3 También eliminé algunas asignaciones innecesarias y reduje algunos nombres de variables no públicas.
undefined
por 0 !. ES6 permite reemplazar isNumeric
con Number.isInteger
. Las líneas como factorials[0][factorials[1]]=factorials[0][factorial_index]*(factorials[1]+1);
son totalmente ilegibles.
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);