Vous recherchez une implémentation très rapide de la fonction factorielle en JavaScript. Des suggestions?
94
Vous recherchez une implémentation très rapide de la fonction factorielle en JavaScript. Des suggestions?
Réponses:
Vous pouvez rechercher (1 ... 100)! sur Wolfram | Alpha pour pré-calculer la séquence factorielle.
Les 100 premiers nombres sont:
Si vous souhaitez toujours calculer les valeurs vous-même, vous pouvez utiliser la mémorisation :
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
Je suppose que vous utiliseriez une sorte de fermeture pour limiter la visibilité des noms de variables.
Réf : BigNumber Sandbox : JsFiddle
la source
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écenteBigInt
plutôt qu'une bibliothèque tierce.Vous devez utiliser une boucle.
Voici deux versions comparées en calculant le factoriel de 100 pour 10 000 fois.
Récursif
Itératif
En direct sur: http://jsfiddle.net/xMpTv/
Mes résultats montrent:
- Récursif ~ 150 millisecondes
- Itératif ~ 5 millisecondes.
la source
rval = rval * i;
vous pourriez écrirerval *= i;
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):
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.la source
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 leNumber
type de données, qui est 170 !.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:
Vous pouvez précalculer certaines valeurs afin de l'accélérer encore plus.
la source
Voici ma solution:
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) :
la source
f=n=>n?f(n-1)*n:1
...Juste une ligne avec ES6
Afficher l'extrait de code
la source
factorial = n => n <= 1 ? 1 : factorial(n - 1) * n
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):
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):
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).
la source
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.
Environ 25 fois plus rapide sur ma machine dans Chrome que la version récursive et 10% plus rapide que celle de xPheRe.
la source
Fonction factorielle la plus rapide
Je pense que cette version basée sur la boucle pourrait être la fonction factorielle la plus rapide.
Et voici mon raisonnement:
for
boucles et leswhile
boucles ont des performances similaires, unefor
boucle sans expression d'initialisation et expression finale semble étrange; probablement mieux d'écrirefor(; n > 0;)
commewhile(n > 0)
n
etr
sont utilisés, donc en théorie moins de paramètres signifie moins de temps passé à allouer de la mémoiren
est 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 entiersla source
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:
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 :)
la source
C'est très simple d'utiliser ES6
const factorial = n => n ? (n * factorial(n-1)) : 1;
Voir un exemple ici
la source
Voici une solution:
la source
En utilisant ES6, vous pouvez y parvenir à la fois rapidement et rapidement:
la source
Le code pour calculer factoriel dépend de vos besoins.
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 source
Ceci est une version compacte basée sur une boucle
Ou vous pouvez remplacer l'objet Math (version récursive):
Ou rejoignez les deux approches ...
la source
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:
C'est aussi précis et aussi rapide que possible en utilisant le
Number
type 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 quandn! > 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.
la source
Réponse en une ligne:
la source
Factorielle itérative avec
BigInt
pour la sécuritéCet exemple fonctionne
BigInt
, car de nombreuses réponses ici échappent toutesNumber
presque 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).Exemple d'utilisation
n
à la fin d'un littéral numérique comme1303n
indique qu'il s'agit d'unBigInt
type.BigInt
àNumber
moins que vous ne les contraigniez explicitement, et cela pourrait entraîner une perte de précision.la source
En utilisant les fonctionnalités ES6, peut écrire du code sur UNE seule ligne et sans récursivité :
Afficher l'extrait de code
la source
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.
Pour l'appeler:
la source
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:
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.
la source
Math.factorial(100); Math.factorial(500);
calculera la multiplication 1..100 deux fois.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.
la source
n * (n-1) * (n-2) * ... * 1
au lieu de l'inverse, vous perdez jusqu'à 4 chiffres de précision pour n >> 20.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 simplementfactorial[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.la source
21! > Number.MAX_SAFE_INTEGER
, donc ne peut pas être représenté en toute sécurité comme un flottant 64 bits.Voici une implémentation qui calcule les factorielles positives et négatives. C'est simple et rapide.
la source
En voici un que j'ai créé moi-même, n'utilisez pas de nombres supérieurs à 170 ou inférieurs à 2.
la source
i
et effectue beaucoup trop deNumber
conversions et donne des résultats incorrects pour 0! (comme vous l'avez dit, mais pourquoi?).Voici mon code
la source
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_VALUE
et est mieux représenté avecInfinity
.La boucle mise en cache doit être la plus rapide (au moins lorsqu'elle est appelée plusieurs fois)
la source
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:
la source
undefined
pour 0 !. ES6 permet de remplacerisNumeric
parNumber.isInteger
. Les lignes commefactorials[0][factorials[1]]=factorials[0][factorial_index]*(factorials[1]+1);
sont totalement illisibles.En voici une qui utilise les nouvelles fonctions javascript fill , map , reduction et constructor (et la syntaxe de la grosse flèche):
Edit: mis à jour pour gérer n === 0
la source
n === 0
?Math.factorial = n => Array.from({ length: n }).reduce((product, _, i) => product * (i + 1), 1)
la source