¿Cuál es la función factorial más rápida en JavaScript? [cerrado]


94

Buscando una implementación realmente rápida de la función factorial en JavaScript. ¿Alguna sugerencia?


8
¿Cuál es el posible rango de argumentos?
Nikita Rybak

5
¿Ha considerado precalcular factoriales y almacenar los valores en una tabla de búsqueda?
Waleed Amjad

2
¿Cuál es la aplicación de tal función? En otras palabras, ¿para qué lo vas a usar?
Puntiagudo

@Nikita Rybak, solo 1 comentario (n). Si (n> 170) e = Infinito
Ken

@ Pointy, otro servicio de calculadora matemática.
Ken

Respuestas:


110

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

Editar: 21.08.2014

Solucion 2

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.

Ref : BigNumber Sandbox : JsFiddle


Los valores posteriores a 6402373705728000 se truncarán, por lo que si va a utilizar este enfoque, asegúrese de convertir a exponencial antes de utilizar la tabla mencionada anteriormente.
David Scott Kirby

1
@DavidScottKirby Javascript convierte automáticamente estos números a su representación flotante de 64 bits más cercana. El beneficio real de no tener los números de precisión completa en el código es el tamaño del archivo reducido.
le_m

Su segunda solución podría simplificarse para 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 BigIntbiblioteca incorporada más reciente en lugar de una biblioteca de terceros.
Patrick Roberts

97

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


+1 ¡Gran respuesta! Aunque la memorización puede ser razonable cuando hay varias llamadas para calcular factoriales para números más grandes.
Tadeck

@Tadeck, gracias. De hecho, la memorización es muy útil en este caso y es por eso que la respuesta de Margus se elige como la correcta :)
Gabriele Petrioli

Una versión de 1 línea de recursive: function factorial (num) {return (num == 1)? num: num * argumentos.callee (num-1); }
jbyrd

2
@HWTech, nunca llamas a los métodos. Tu prueba compara la velocidad de definir los dos métodos ... no el tiempo que tardan en ejecutarse ... Esta es una prueba
Gabriele Petrioli

3
En lugar de rval = rval * i;usted podría escribirrval *= i;
Ryan

29

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.


enfoque bastante interesante, gracias.
Ken

Me
acabo de

Recomiendo cambiar la parte debajo del bucle for a 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 Numbertipo de datos, que es 170 !.
le_m

18

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.


3
He creado un auto-memoizer para cualquier función en base a esta respuesta (también un poco más rápido :)), que también incluye un límite en el tamaño de la caché. stackoverflow.com/a/10031674/36537
Phil H

16

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

7
Ahorre aún más con f=n=>n?f(n-1)*n:1...
le_m

desafortunadamente, incluso si es agradable de ver y de forma corta, esta es la forma más lenta de hacerlo.
Zibri

11

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
Naramsim

10

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).


3
En los lenguajes sin optimización de llamadas de cola (es decir, los lenguajes más utilizados) es mejor utilizar una implementación no recursiva donde sea fácil hacerlo, aunque hay formas de evitarlo
Daniel Earwicker

eso definitivamente no es tan rápido, ya que ni siquiera usaría el TCO, si se implementara. Pero es simple y no lo rechazaría. Seguro que no es el más rápido.
Haylem

La optimización de la llamada de cola ni siquiera es posible para esta función, ya que la llamada recursiva no está en la posición de cola.
Fred Foo

3
@Josh, ( no el votante negativo ) el más rápido es el bucle por un margen considerable ..
Gabriele Petrioli

7

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.


6

Función factorial más rápida

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:

  • Las funciones recursivas, incluso con la memorización, tienen la sobrecarga de una llamada de función (básicamente, empujar funciones a la pila) que es menos eficaz que usar un bucle
  • Si bien los forbucles y los whilebucles tienen un rendimiento similar, un forbucle 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)
  • Solo se utilizan dos parámetros ny r, por lo que, en teoría, menos parámetros significa menos tiempo dedicado a la asignación de memoria
  • Utiliza un bucle decrementado que verifica si nes cero: he escuchado teorías de que las computadoras son mejores para verificar números binarios (0 y 1) que para verificar otros enteros

5

Me 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 :)


4

Es muy simple usar ES6

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

Vea un ejemplo aquí


4

Aquí hay una solución:

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

4

Usando ES6 puede lograrlo rápido y corto:

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

3

El código para calcular factorial depende de sus requisitos.

  1. ¿Le preocupa el desbordamiento?
  2. ¿Qué rango de insumos tendrá?
  3. ¿Es más importante para usted minimizar el tamaño o el tiempo?
  4. ¿Qué vas a hacer con el factorial?

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.


La lista numerada probablemente debería estar en los comentarios. Todo lo que queda son dos enlaces, y no se recomiendan las respuestas de solo enlace.
Barett

3

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


1
Lo arreglé dentro del código anterior. ¡Gracias!
Sandro Rosa

3

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 Numbertipo 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.


3

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


3

Factorial iterativo con BigIntseguridad

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

Ejemplo de uso

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

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

// 9332621544394415268169923885626670049071596826438162146859296389521759999
// 3229915608941463976156518286253697920827223758251185210916864000000000000
// 000000000000n
factorial(100)
  • El nal final de un literal numérico como 1303nindica que es un BigInttipo.
  • Recuerde que no debe mezclarse BigIntcon a Numbermenos que los obligue explícitamente, y que hacerlo podría causar una pérdida de precisión.

3

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)


2

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

ES6 admite TCO, pero afaik esta función no está activa de forma predeterminada en ningún motor importante todavía
le_m

2

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.


Esto realmente no aprovecha al máximo la memorización para los subproblemas; por ejemplo, Math.factorial(100); Math.factorial(500);calculará la multiplicación del 1 ... 100 dos veces.
Barett

2

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

Al comenzar su multiplicación con en n * (n-1) * (n-2) * ... * 1lugar de al revés, pierde hasta 4 dígitos en precisión para n >> 20.
le_m

2
// if you don't want to update the Math object, use `var factorial = ...`
Math.factorial = (function() {
    var f = function(n) {
        if (n < 1) {return 1;}  // no real error checking, could add type-check
        return (f[n] > 0) ? f[n] : f[n] = n * f(n -1);
    }
    for (i = 0; i < 101; i++) {f(i);} // precalculate some values
    return f;
}());

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

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.


¡Me di cuenta de eso después de los 21! los números no son confiables.
AutoSponge

@AutoSponge Eso es porque 21! > Number.MAX_SAFE_INTEGER, por lo tanto, no se puede representar con seguridad como un flotante de 64 bits.
le_m


2

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

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. Además, crea un variable global iy realiza demasiadas Numberconversiones y da resultados incorrectos para 0! (como dijiste, pero ¿por qué?).
le_m

2

Aqui esta mi codigo

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

1
Si (n> 170) e = Infinito. Y su código generará una gran cantidad. ¿No habrá desbordes?
Prime el

Resultado incorrecto para 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_VALUEy se representa mejor con Infinity.
le_m

2

El bucle en caché debe ser más rápido (al menos cuando se llama varias veces)

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

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.


¡Devoluciones undefinedpor 0 !. ES6 permite reemplazar isNumericcon Number.isInteger. Las líneas como factorials[0][factorials[1]]=factorials[0][factorial_index]*(factorials[1]+1);son totalmente ilegibles.
le_m

2

Aquí hay uno que usa funciones de JavaScript más nuevas , llenar , asignar , reducir y construir (y sintaxis de flecha gruesa):

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

Editar: actualizado para manejar n === 0


2
Esa es una línea de código ilegible seriamente fea.
jungledev

1
Esa es una buena idea. En lugar de atravesar la longitud dos veces, ¿por qué no convertir toda la lógica a la función de reducción y usar su valor inicial para manejar el caso de borde 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
Bienvenido a StackOverflow y gracias por su ayuda. Es posible que desee mejorar aún más su respuesta agregando alguna explicación.
Elias MP
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.