Quelle est la fonction factorielle la plus rapide en JavaScript? [fermé]


94

Vous recherchez une implémentation très rapide de la fonction factorielle en JavaScript. Des suggestions?


8
Quelle est la gamme d'arguments possible?
Nikita Rybak

5
Avez-vous envisagé de pré-calculer les factorielles et de stocker les valeurs dans une table de consultation?
Waleed Amjad

2
Quelle est l'application d'une telle fonction? En d'autres termes, dans quel but allez-vous l'utiliser?
Pointy

@Nikita Rybak, seulement 1 agrument (n). Si (n> 170) e = Infinity
Ken

@ Pointy, encore un autre service de calculatrice mathématique.
Ken

Réponses:


110

Vous pouvez rechercher (1 ... 100)! sur Wolfram | Alpha pour pré-calculer la séquence factorielle.

Les 100 premiers nombres sont:

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 vous souhaitez toujours calculer les valeurs vous-même, vous pouvez utiliser la mémorisation :

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

Edit: 21.08.2014

Solution 2

J'ai pensé qu'il serait utile d'ajouter un exemple fonctionnel de fonction factorielle itérative paresseuse qui utilise de grands nombres pour obtenir un résultat exact avec la mémorisation et le cache à titre de comparaison

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

Je suppose que vous utiliseriez une sorte de fermeture pour limiter la visibilité des noms de variables.

Réf : BigNumber Sandbox : JsFiddle


Les valeurs au-delà de 6402373705728000 seront tronquées, donc si vous allez utiliser cette approche, assurez-vous de les convertir en exponentielle avant d'utiliser le tableau susmentionné.
David Scott Kirby

1
@DavidScottKirby Javascript convertit automatiquement ces nombres en leur représentation flottante 64 bits la plus proche. Le véritable avantage de ne pas avoir les nombres de précision complets dans le code est la réduction de la taille du fichier.
le_m

Votre deuxième solution pourrait être simplifiée pour function factorial (n) { for (var i = f.length; i <= n; i++) f.push(f[i - 1].multiply(i.toString())); return f[n]; }voir également ma réponse qui utilise la bibliothèque intégrée plus récente BigIntplutôt qu'une bibliothèque tierce.
Patrick Roberts du

97

Vous devez utiliser une boucle.

Voici deux versions comparées en calculant le factoriel de 100 pour 10 000 fois.

Récursif

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

Itératif

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

En direct sur: http://jsfiddle.net/xMpTv/

Mes résultats montrent:
- Récursif ~ 150 millisecondes
- Itératif ~ 5 millisecondes.


+1 Excellente réponse! Bien que la mémorisation puisse être raisonnable lorsqu'il y a plusieurs appels pour calculer des factorielles pour des nombres plus grands.
Tadeck

@Tadeck, merci. En effet, la mémorisation est très utile dans ce cas et c'est pourquoi la réponse de Margus est choisie comme la bonne :)
Gabriele Petrioli

Une version 1 ligne de recursive: function factorial (num) {return (num == 1)? num: num * arguments.callee (num-1); }
jbyrd

2
@HWTech, vous n'appelez jamais les méthodes. Votre test compare la vitesse de définition des deux méthodes .. pas le temps qu'elles prennent pour s'exécuter .. C'est un meilleur test (en essayant uniquement la factorielle de 15)
Gabriele Petrioli

3
Au lieu de rval = rval * i;vous pourriez écrirerval *= i;
Ryan

29

Je pense toujours que la réponse de Margus est la meilleure. Cependant, si vous souhaitez également calculer les factorielles des nombres compris entre 0 et 1 (c'est-à-dire la fonction gamma), vous ne pouvez pas utiliser cette approche car la table de recherche devra contenir des valeurs infinies.

Cependant, vous pouvez approximer les valeurs des factorielles, et c'est assez rapide, plus rapide que de s'appeler récursivement ou de le boucler au moins (surtout lorsque les valeurs commencent à grossir).

Une bonne méthode d'approximation est celle de Lanczos

Voici une implémentation en JavaScript (portée à partir d'une calculatrice que j'ai écrite il y a des mois):

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

Vous pouvez maintenant faire des trucs sympas comme factorial(0.41), etc., mais la précision peut être un peu mauvaise, après tout, c'est une approximation du résultat.


approche assez intéressante, merci.
Ken

Je viens de me faire gagner beaucoup de temps, merci beaucoup :)
nicolaskruchten

Je recommande de changer la partie sous la boucle for en var d3d4 = Math.exp((z + 0.5) * Math.log(z + 5.5) - z - 5.5); return d1 * d2 * d3d4;. Cela vous permet de calculer des factorielles jusqu'à 169! au lieu de seulement 140 actuellement !. C'est assez proche de la factorielle maximale représentable en utilisant le Numbertype de données, qui est 170 !.
le_m

18

La table de recherche est la solution la plus évidente si vous travaillez avec des nombres naturels. Pour calculer n'importe quel factoriel en temps réel, vous pouvez l'accélérer avec un cache, en enregistrant les nombres que vous avez calculés auparavant. Quelque chose comme:

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

Vous pouvez précalculer certaines valeurs afin de l'accélérer encore plus.


3
J'ai créé un mémorisation automatique pour toute fonction donnée basée sur cette réponse (également légèrement plus rapide :)), en incluant également une limite sur la taille du cache. stackoverflow.com/a/10031674/36537
Phil H

16

Voici ma solution:

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

C'est le moyen le plus simple (moins de caractères / lignes) que j'ai trouvé, seulement une fonction avec une ligne de code.


Edit:
Si vous voulez vraiment enregistrer des caractères, vous pouvez utiliser une fonction de flèche (21 octets) :

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

7
Économisez encore plus avec f=n=>n?f(n-1)*n:1...
le_m

malheureusement même si c'est agréable à voir et court de forme, c'est la manière la plus lente de le faire.
Zibri

11

Juste une ligne avec ES6

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


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

10

fonction récursive courte et facile (vous pouvez aussi le faire avec une boucle, mais je ne pense pas que cela ferait une différence dans les performances):

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

pour un n très grand, vous pouvez utiliser l' approximation de Stirlings - mais cela ne vous donnera qu'une valeur approximative.

EDIT: un commentaire sur les raisons pour lesquelles je reçois un vote négatif pour cela aurait été bien ...

EDIT2: ce serait la soulution utilisant une boucle (ce qui serait le meilleur choix):

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

Je pense que la meilleure solution serait d'utiliser les valeurs mises en cache, comme Margus l'a mentionné et d'utiliser l' approximation de Stirlings pour des valeurs plus grandes (en supposant que vous devez être vraiment rapide et que vous n'avez pas à être aussi exact sur de si grands nombres).


3
Dans les langages sans optimisation des appels de queue (c'est-à-dire les langages les plus largement utilisés), il est préférable d'utiliser une implémentation non récursive où il est facile de le faire, bien qu'il existe des moyens de contourner cela: paulbarry.com/articles/2009/08/30 / optimisation des appels de queue
Daniel Earwicker

ce n'est en effet certainement pas si rapide, car il n'utiliserait même pas le TCO s'il était mis en œuvre. Mais c'est simple et je ne voterais pas contre. Ce n'est certainement pas le plus rapide.
haylem

L'optimisation des appels de queue n'est même pas possible pour cette fonction, car l'appel récursif n'est pas en position de queue.
Fred Foo

3
@Josh, ( pas le downvoter ) le plus rapide est la boucle de ..
Gabriele Petrioli

7

Voici le mémoizer, qui prend n'importe quelle fonction à argument unique et la mémorise. Se révèle être légèrement plus rapide que la solution de @ xPheRe , y compris la limite de la taille du cache et la vérification associée, car j'utilise un court-circuit et ainsi de suite.

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

Environ 25 fois plus rapide sur ma machine dans Chrome que la version récursive et 10% plus rapide que celle de xPheRe.


6

Fonction factorielle la plus rapide

Je pense que cette version basée sur la boucle pourrait être la fonction factorielle la plus rapide.

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

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

Et voici mon raisonnement:

  • Les fonctions récursives, même avec la mémorisation, ont la surcharge d'un appel de fonction (essentiellement pousser des fonctions sur la pile) qui est moins performant que l'utilisation d'une boucle
  • Alors que les forboucles et les whileboucles ont des performances similaires, une forboucle sans expression d'initialisation et expression finale semble étrange; probablement mieux d'écrire for(; n > 0;)commewhile(n > 0)
  • Seuls deux paramètres net rsont utilisés, donc en théorie moins de paramètres signifie moins de temps passé à allouer de la mémoire
  • Utilise une boucle décrémentée qui vérifie si nest zéro - j'ai entendu des théories selon lesquelles les ordinateurs sont meilleurs pour vérifier les nombres binaires (0 et 1) qu'ils ne le sont pour vérifier d'autres entiers

5

Je suis tombé sur ce post. Inspiré par toutes les contributions ici, j'ai proposé ma propre version, qui a deux fonctionnalités que je n'ai jamais vues auparavant: 1) Une vérification pour s'assurer que l'argument est un entier non négatif 2) Faire une unité à partir du cache et la fonction pour en faire un bit de code autonome. Pour le plaisir, j'ai essayé de le rendre aussi compact que possible. Certains peuvent trouver cela élégant, d'autres peuvent le trouver terriblement obscur. Bref, le voici:

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

Vous pouvez soit pré-remplir le cache, soit l'autoriser à se remplir au fur et à mesure des appels. Mais l'élément initial (pour fact (0) doit être présent ou il se cassera.

Prendre plaisir :)


4

C'est très simple d'utiliser ES6

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

Voir un exemple ici


4

Voici une solution:

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

4

En utilisant ES6, vous pouvez y parvenir à la fois rapidement et rapidement:

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

3

Le code pour calculer factoriel dépend de vos besoins.

  1. Êtes-vous préoccupé par le débordement?
  2. Quelle gamme d'entrées disposerez-vous?
  3. Est-il plus important pour vous de minimiser la taille ou le temps?
  4. Qu'allez-vous faire de la factorielle?

Concernant les points 1 et 4, il est souvent plus utile d'avoir une fonction pour évaluer le log directement de la factorielle plutôt que d'avoir une fonction pour évaluer la factorielle elle-même.

Voici un article de blog qui traite de ces problèmes. Voici un code C # pour calculer la factorielle du journal qui serait simple à porter en JavaScript. Mais ce n'est peut-être pas le meilleur pour vos besoins en fonction de vos réponses aux questions ci-dessus.


La liste numérotée devrait probablement figurer dans les commentaires. Il ne reste plus que deux liens et les réponses ne contenant que des liens sont déconseillées.
Barett

3

Ceci est une version compacte basée sur une boucle

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

Ou vous pouvez remplacer l'objet Math (version récursive):

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

Ou rejoignez les deux approches ...


1
Je l'ai corrigé dans le code ci-dessus. Je vous remercie!
Sandro Rosa

3

En exploitant le fait Number.MAX_VALUE < 171!, nous pouvons simplement utiliser une table de recherche complète composée de seulement 171 éléments de tableau compact occupant moins de 1,4 kilo-octets de mémoire.

Une fonction de recherche rapide avec une complexité d'exécution O (1) et une surcharge minimale d'accès au tableau ressemblerait alors à ceci:

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

C'est aussi précis et aussi rapide que possible en utilisant le Numbertype de données. Le calcul de la table de recherche en Javascript - comme le suggèrent d'autres réponses - réduira la précision quand n! > Number.MAX_SAFE_INTEGER.

La compression de la table d'exécution via gzip réduit sa taille sur le disque d'environ 3,6 à 1,8 kilo-octets.


3

Réponse en une ligne:

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

Factorielle itérative avec BigIntpour la sécurité

La solution utilise BigInt, une fonctionnalité ES 2018 + / 2019.

Cet exemple fonctionne BigInt, car de nombreuses réponses ici échappent toutes Numberpresque immédiatement à la limite de sécurité de (MDN). Ce n'est pas le plus rapide mais c'est simple et donc plus clair pour adapter d'autres optimisations (comme un cache des 100 premiers nombres).

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

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

   return p
}

Exemple d'utilisation

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

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

// 9332621544394415268169923885626670049071596826438162146859296389521759999
// 3229915608941463976156518286253697920827223758251185210916864000000000000
// 000000000000n
factorial(100)
  • Le nà la fin d'un littéral numérique comme 1303nindique qu'il s'agit d'un BigInttype.
  • N'oubliez pas que vous ne devez pas vous mélanger BigIntà Numbermoins que vous ne les contraigniez explicitement, et cela pourrait entraîner une perte de précision.

3

En utilisant les fonctionnalités ES6, peut écrire du code sur UNE seule ligne et sans récursivité :

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


2

Juste pour être complet, voici une version récursive qui permettrait une optimisation des appels de queue. Je ne suis pas sûr que les optimisations des appels de fin soient effectuées en JavaScript.

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

Pour l'appeler:

rFact(x, 1);

ES6 prend en charge le TCO, mais afaik cette fonctionnalité n'est pas encore active par défaut dans aucun moteur majeur
le_m

2

Il s'agit d'une solution itérative qui utilise moins d'espace de pile et enregistre les valeurs précédemment calculées de manière auto-mémorisable:

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

Notez également que j'ajoute ceci à l'objet Math qui est un objet littéral donc il n'y a pas de prototype. Plutôt que de les lier directement à la fonction.


Cela ne profite pas vraiment pleinement de la mémorisation pour les sous-problèmes - par exemple, Math.factorial(100); Math.factorial(500);calculera la multiplication 1..100 deux fois.
Barett

2

Je pense que ce qui suit est le morceau de code le plus durable et le plus efficace parmi les commentaires ci-dessus. Vous pouvez l'utiliser dans l'architecture js de votre application globale ... et, ne vous inquiétez pas de l'écrire dans plusieurs espaces de noms (car c'est une tâche qui n'a probablement pas besoin d'être augmentée). J'ai inclus 2 noms de méthodes (en fonction des préférences) mais les deux peuvent être utilisés car ce ne sont que des références.

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

En commençant votre multiplication par n * (n-1) * (n-2) * ... * 1au lieu de l'inverse, vous perdez jusqu'à 4 chiffres de précision pour 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

Cela met en cache les 100 premières valeurs à la volée et n'introduit pas de variable externe dans la portée du cache, en stockant les valeurs en tant que propriétés de l'objet fonction lui-même, ce qui signifie que si vous savez qu'il factorial(n)a déjà été calculé, vous pouvez appelez-le simplement factorial[n], ce qui est légèrement plus efficace. L'exécution de ces 100 premières valeurs prendra moins d'une milliseconde dans les navigateurs modernes.


J'ai compris qu'après 21 ans! les chiffres ne sont pas fiables.
AutoSponge

@AutoSponge C'est parce que 21! > Number.MAX_SAFE_INTEGER, donc ne peut pas être représenté en toute sécurité comme un flottant 64 bits.
le_m


2

En voici un que j'ai créé moi-même, n'utilisez pas de nombres supérieurs à 170 ou inférieurs à 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;
}

En commençant votre multiplication par n * (n-1) * (n-2) * ... * 1 au lieu de l'inverse, vous perdez jusqu'à 4 chiffres en précision pour n >> 20. En outre, crée un variable globale iet effectue beaucoup trop de Numberconversions et donne des résultats incorrects pour 0! (comme vous l'avez dit, mais pourquoi?).
le_m

2

Voici mon code

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

1
Si (n> 170) e = Infini. Et votre code générera un nombre énorme. n'y aura-t-il pas de débordements?
prime

Résultat incorrect pour factorial(0) . De plus, en commençant votre multiplication par n * (n-1) * (n-2) * ... * 1 au lieu de l'inverse, vous perdez jusqu'à 4 chiffres en précision pour n >> 20. @prime: 170! > Number.MAX_VALUEet est mieux représenté avec Infinity.
le_m

2

La boucle mise en cache doit être la plus rapide (au moins lorsqu'elle est appelée plusieurs fois)

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

Fourni par http://javascript.info/tutorial/number-math comme moyen simple d'évaluer si un objet est un entier approprié pour le calcul.

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

Un ensemble simple de factorielles mémorisées qui nécessitent des calculs redondants, peuvent être traités avec "multiplier par 1", ou sont un chiffre qui est une équation simple qui ne vaut pas la peine d'être traitée en direct.

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

Après avoir examiné les commentaires des autres membres (à l'exclusion des conseils du journal, bien que je puisse l'implémenter plus tard), je suis allé de l'avant et j'ai lancé un script assez simple. J'ai commencé avec un simple exemple de POO JavaScript sans instruction et j'ai construit une petite classe pour gérer les factorielles. J'ai ensuite implémenté ma version de la mémorisation suggérée ci-dessus. J'ai également implémenté la factorisation sténographique mais j'ai fait un petit ajustement d'erreur; J'ai changé le "n <2" en "n <3". "n <2" traiterait toujours n = 2, ce qui serait un gaspillage, car vous itéreriez pour un 2 * 1 = 2; c'est un gaspillage à mon avis. Je l'ai changé en "n <3"; car si n est 1 ou 2, il retournera simplement n, s'il est 3 ou plus, il s'évaluera normalement. Bien sûr, comme les règles s'appliquent, j'ai placé mes fonctions dans l'ordre décroissant d'exécution supposée. J'ai ajouté l'option bool (true | false) pour permettre une modification rapide entre l'exécution mémorisée et l'exécution normale (vous ne savez jamais quand vous voulez permuter sur votre page sans avoir besoin de changer le "style") Comme je l'ai dit avant le La variable factorielle mémorisée est définie avec les 3 positions de départ, prenant 4 caractères et minimisant les calculs inutiles. Tout après la troisième itération, vous gérez les mathématiques à deux chiffres plus. Je suppose que si vous étiez assez collant à ce sujet, vous courriez sur une table factorielle (telle qu'implémentée). prendre 4 caractères et minimiser les calculs inutiles. Tout après la troisième itération, vous gérez les mathématiques à deux chiffres plus. Je suppose que si vous étiez assez collant à ce sujet, vous courriez sur une table factorielle (telle qu'implémentée). prendre 4 caractères et minimiser les calculs inutiles. Tout après la troisième itération, vous gérez les mathématiques à deux chiffres plus. Je suppose que si vous étiez assez collant à ce sujet, vous courriez sur une table factorielle (telle qu'implémentée).

Qu'ai-je prévu après cela? stockage local et de session pour permettre un cache au cas par cas des itérations nécessaires, traitant essentiellement le problème de «table» mentionné ci-dessus. Cela permettrait également d'économiser massivement la base de données et l'espace côté serveur. Cependant, si vous optez pour localStorage, vous aspireriez essentiellement de l'espace sur l'ordinateur de vos utilisateurs simplement pour stocker une liste de numéros et rendre leur écran plus rapide, mais sur une longue période avec un immense besoin, cela serait lent. Je pense que sessionStorage (effacement après le départ de Tab) serait un bien meilleur itinéraire. Peut-être combiner cela avec un serveur auto-équilibré / un cache local dépendant? L'utilisateur A a besoin de X itérations. L'utilisateur B a besoin d'itérations Y. X + Y / 2 = Montant nécessaire mis en cache localement. Ensuite, il suffit de détecter et de jouer avec les benchmarks de temps de chargement et d'exécution en direct pour chaque utilisateur jusqu'à ce qu'il s'ajuste à l'optimisation du site lui-même. Merci!

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

Cette modification implémente une autre suggestion de Stack et me permet d'appeler la fonction factorielle (true) (5), ce qui était l'un de mes objectifs. : 3 J'ai également supprimé certaines affectations inutiles et raccourci certains noms de variables non publics.


Renvoie undefinedpour 0 !. ES6 permet de remplacer isNumericpar Number.isInteger. Les lignes comme factorials[0][factorials[1]]=factorials[0][factorial_index]*(factorials[1]+1);sont totalement illisibles.
le_m

2

En voici une qui utilise les nouvelles fonctions javascript fill , map , reduction et constructor (et la syntaxe de la grosse flèche):

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

Edit: mis à jour pour gérer n === 0


2
C'est une ligne de code vraiment laide et illisible.
jungledev

1
C'est une bonne idée. Plutôt que de parcourir la longueur deux fois, pourquoi ne pas convertir toute la logique en fonction de réduction et utiliser sa valeur initiale pour gérer le cas de bord n === 0? Math.factorial = n => Array.from({ length: n }).reduce((product, _, i) => product * (i + 1), 1)
AlexSashaRegan le

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

2
Bienvenue dans StackOverflow et merci pour votre aide. Vous voudrez peut-être améliorer encore votre réponse en ajoutant quelques explications.
Elias MP
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.