106

在 JavaScript 中寻找一个非常快速的阶乘函数实现。有什么建议吗?

4

49 回答 49

122

You can search for (1...100)! on Wolfram|Alpha to pre-calculate the factorial sequence.

The first 100 numbers are:

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

If you still want to calculate the values yourself, you can use memoization:

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

I thought it would be useful to add a working example of lazy iterative factorial function that uses big numbers to get exact result with memoization and cache as comparison

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

I assume you would use some kind of closure to limit variable name visibility.

Ref: BigNumber Sandbox: JsFiddle

于 2010-10-18T12:48:11.203 回答
108

您应该使用循环。

以下是通过计算 100 的阶乘 10.000 次来进行基准测试的两个版本。

递归的

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

迭代

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

住:http: //jsfiddle.net/xMpTv/

我的结果显示:
-递归~ 150 毫秒
-迭代~ 5 毫秒..

于 2010-10-18T12:59:13.147 回答
30

我仍然认为马格斯的回答是最好的。但是,如果您还想计算 0 到 1 范围内的数字的阶乘(即 gamma 函数),则不能使用该方法,因为查找表必须包含无限值。

但是,您可以近似阶乘的值,并且它非常快,至少比递归调用自身或循环它更快(尤其是当值开始变大时)。

一种很好的近似方法是 Lanczos 的方法

这是 JavaScript 中的一个实现(从我几个月前写的一个计算器移植而来):

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

你现在可以做一些很酷的事情,比如factorial(0.41),等等,但是准确度可能会有点偏差,毕竟,它是结果的近似值。

于 2010-10-18T13:00:30.733 回答
19

如果您使用自然数,查找表是显而易见的方法。要实时计算任何阶乘,您可以使用缓存加速它,保存您之前计算的数字。就像是:

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

您可以预先计算一些值以加快速度。

于 2010-10-18T13:34:50.143 回答
18

这是我的解决方案:

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

这是我发现的最简单的方法(更少的字符/行),只有一个代码行的函数。


编辑:
如果你真的想保存一些字符,你可以使用箭头函数 (21 个字节)

f=n=>(n<2)?1:f(n-1)*n
于 2013-10-02T14:14:23.513 回答
16

与 ES6 仅一行代码

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

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


function print(value) {
  document.querySelector('.result').innerHTML = value;
}
.result {
  margin-left: 10px;
}
<input onkeyup="print(factorial(this.value))" type="number"/>

<span class="result">......</span>

于 2016-07-24T20:50:15.567 回答
12

short and easy recursive function (you could do it with a loop, too, but I don't think that would make any difference in performance):

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

for a very large n, you could use the stirlings approximation - but that will only give you an approximate value.

EDIT: a comment on why I'm getting a downvote for this would have been nice...

EDIT2: this would be the soulution using a loop (which would be the better choice):

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

I think the best solution would be to use the cached values, as Margus mentioned and use the stirlings approximation for larger values (assumed you have to be realy fast and don't have to be that exact on such big numbers).

于 2010-10-18T12:47:08.723 回答
9

最快的阶乘函数

我认为这个基于循环的版本可能是最快的阶乘函数。

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

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

这是我的推理:

  • 递归函数,即使有记忆,也有函数调用的开销(基本上将函数推入堆栈),这比使用循环的性能低
  • 虽然for循环和while循环具有相似的性能,for但没有初始化表达式和最终表达式的循环看起来很奇怪;可能最好for(; n > 0;)写成while(n > 0)
  • 只使用了两个参数nr,所以理论上更少的参数意味着更少的时间分配内存
  • 使用递减循环来检查是否n为零 - 我听说计算机在检查二进制数(0 和 1)方面比在检查其他整数方面更好的理论
于 2018-11-03T22:14:59.737 回答
7

看哪,记忆器,它接受任何单参数函数并记忆它。结果比@xPheRe 的解决方案稍微快一点,包括对缓存大小的限制和相关检查,因为我使用短路等。

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

在我的机器上,Chrome 比递归版本快大约 25 倍,比 xPheRe 快 10%。

于 2012-04-05T15:40:03.170 回答
6

使用 ES6 非常简单

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

在此处查看示例

于 2016-11-05T05:40:58.353 回答
6

使用 ES6,您可以快速而简短地实现它:

const factorial = n => [...Array(n + 1).keys()].slice(1).reduce((acc, cur) => acc * cur, 1)
于 2018-02-17T06:03:42.070 回答
5

我偶然发现了这个帖子。受到这里所有贡献的启发,我提出了我自己的版本,它有两个我以前没有讨论过的特性:1)检查以确保参数是非负整数 2)从缓存中制作一个单元,然后使它成为一个自包含的代码的功能。为了好玩,我试着让它尽可能紧凑。有些人可能会觉得这很优雅,有些人可能会认为它非常晦涩难懂。无论如何,这里是:

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

您可以预先填充缓存,也可以在调用经过时允许它被填充。但是初始元素(对于 fact(0) 必须存在,否则它将中断。

享受 :)

于 2012-03-07T21:30:02.497 回答
4

这是一种解决方案:

function factorial(number) {
  total = 1
  while (number > 0) {
    total *= number
    number = number - 1
  }
  return total
}
于 2018-01-23T02:53:08.123 回答
4

利用这一事实Number.MAX_VALUE < 171!,我们可以简单地使用一个完整的查找表,该表仅包含 171 个紧凑的数组元素,占用不到 1.4 KB 的内存。

具有运行时复杂度O(1)最小数组访问开销的快速查找函数将如下所示:

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

这与使用数据类型一样精确和快速Number。在 Javascript 中计算查找表(正如其他一些答案所暗示的那样)会降低n! > Number.MAX_SAFE_INTEGER.

通过 gzip 压缩运行时表可将其在磁盘上的大小从大约 3.6 KB 减少到 1.8 KB。

于 2017-03-09T17:19:02.110 回答
3

计算阶乘的代码取决于您的要求。

  1. 你担心溢出吗?
  2. 您将有什么范围的输入?
  3. 对您来说,最小化尺寸还是时间更重要?
  4. 你打算用阶乘做什么?

关于第 1 点和第 4 点,具有直接评估阶乘对数的函数通常比具有评估阶乘本身的函数更有用。

这是讨论这些问题的博客文章。这是一些用于计算日志阶乘的 C# 代码,这些代码对于移植到 JavaScript 来说是微不足道的。但根据您对上述问题的回答,它可能不是最适合您的需求。

于 2010-10-18T13:12:39.783 回答
3

这是一个紧凑的基于循环的版本

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

或者您可以覆盖 Math 对象(递归版本):

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

或者加入这两种方法......

于 2015-12-26T18:47:34.210 回答
3

迭代阶乘以BigInt确保安全

解决方案使用BigIntES 2018+/2019 功能。

这是使用的工作示例,因为这里的许多答案几乎都立即脱离了(MDN)BigInt的安全边界。Number它不是最快的,但它很简单,因此更易于适应其他优化(如前 100 个数字的缓存)。

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数字文字末尾的“like”表示1303n它是一种BigInt类型。
  • BigInt请记住,除非您明确强制它们,否则您不应混用Number,这样做可能会导致准确性下降。
于 2018-12-06T22:38:34.137 回答
3

一行回复:

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

于 2018-02-09T22:16:48.977 回答
2

这是一个迭代解决方案,它使用更少的堆栈空间并以自记忆的方式保存先前计算的值:

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

另请注意,我将此添加到作为对象文字的 Math 对象中,因此没有原型。而是直接将这些绑定到函数。

于 2012-03-05T19:02:39.927 回答
2

这是我的代码

function factorial(num){
    var result = num;
    for(i=num;i>=2;i--){
        result = result * (i-1);
    }
    return result;
}
于 2014-01-03T16:50:38.437 回答
2

这是我自己做的,不要使用超过 170 或低于 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;
}
于 2013-05-18T18:57:59.460 回答
2

缓存循环应该是最快的(至少在多次调用时)

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;
  }
})();
于 2014-03-20T14:14:48.013 回答
2

我相信以下是上述评论中最可持续和最有效的代码。您可以在您的全局应用程序 js 架构中使用它......而且,不用担心在多个命名空间中编写它(因为它可能不需要太多扩充的任务)。我已经包含了 2 个方法名称(基于偏好),但两者都可以使用,因为它们只是参考。

Math.factorial = Math.fact = function(n) {
    if (isNaN(n)||n<0) return undefined;
    var f = 1; while (n > 1) {
        f *= n--;
    } return f;
};
于 2012-03-19T06:34:41.773 回答
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

这会即时缓存前 100 个值,并且不会将外部变量引入缓存范围,将值存储为函数对象本身的属性,这意味着如果您知道factorial(n)已经计算过,您可以只需将其称为factorial[n],它的效率略高。在现代浏览器中运行这前 100 个值将花费亚毫秒级的时间。

于 2012-03-23T13:43:52.887 回答
2

这是一个计算正因子和负因子的实现。它快速而简单。

var factorial = function(n) {
  return n > 1
    ? n * factorial(n - 1)
    : n < 0
        ? n * factorial(n + 1)
        : 1;
}
于 2012-07-31T03:22:23.600 回答
2

只是为了完整起见,这里有一个允许尾调用优化的递归版本。我不确定是否在 JavaScript 中执行尾调用优化..

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

调用它:

rFact(x, 1);
于 2010-11-19T06:32:33.930 回答
2

这是一种尚未提供的方法。使用BigInt和记忆,我们可以获得准确的结果并跳过已经计算的值的计算:

// using let and const, block scope can be used instead of IIFE for closure
{
  const lut = [1n, 1n];
  
  // returns factorial as BigInt instead of Number
  function factorial (n) {
    for (let i = lut.length; i <= n; i++) {
      lut.push(BigInt(i) * lut[i - 1]);
    }

    return lut[n];
  }
}

console.log('starting');
// first time will require computation
console.log(factorial(10000).toString());
// second time will return result from cache
console.log(factorial(10000).toString());
div.as-console-wrapper { overflow-x: scroll; }

于 2019-08-22T16:01:56.153 回答
2

使用 ES6 特性,可以在一行上编写代码且无需递归

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

var factorial=(n)=>Array.from(
       {length: n}, (v, k) => k+1)   /*Generate Array [1, 2, .., n -1, n]*/
     .reduce(
       (a, b) => a*b, 1 /*Multiply all aarray items; one with the next*/
     ); 
     

var n = prompt('Give us "n", we will calculate "n!" ?');

if (n) {
  alert(`${n}! = ${factorial(n)}`)
}

于 2016-07-24T21:15:29.957 回答
2

根据Wolfram MathWorld的说法:

阶乘n ! 正整数 n定义为

n!=n(n-1)...2·1。

因此,您可以使用以下方法来获得一个数的阶乘:

const factorial = n => +!n || n * factorial(--n);

factorial(4) // 4! = 4 * 3 * 2 * 1 = 24
于 2018-11-01T21:09:33.470 回答
2
function computeFactorialOfN(n) {
  var output=1;
  for(i=1; i<=n; i++){
    output*=i;
  } return output;
}
computeFactorialOfN(5);
于 2017-08-29T10:55:09.833 回答
2
function isNumeric(n) {
    return !isNaN(parseFloat(n)) && isFinite(n)
}

http://javascript.info/tutorial/number-math提供,作为评估对象是否是适合计算的整数的简单方法。

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

一组需要冗余计算的简单记忆阶乘,可以用“乘以 1”来处理,或者是一个不值得实时处理的简单方程。

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

在查看了其他成员的输入后(不包括 Log 建议,尽管我可能稍后会实施),然后我继续编写了一个相当简单的脚本。我从一个简单的未受过教育的 JavaScript OOP 示例开始,并构建了一个小类来处理阶乘。然后,我实现了上面建议的 Memoization 版本。我还实现了速记分解,但是我做了一个小的错误调整;我将“n<2”更改为“n<3”。"n<2" 仍然会处理 n=2 ,这将是一种浪费,因为您将迭代 2*1=2; 我认为这是一种浪费。我把它改成了“n<3”;因为如果 n 是 1 或 2,它将简单地返回 n,如果它是 3 或更大,它将正常评估。当然,当规则适用时,我将我的函数按假定执行的降序排列。我在 bool(true|false) 选项中添加了允许在 memo'ed 和正常执行之间快速更改(您永远不知道何时要在页面上交换而不需要更改“样式”)正如我之前所说memoized factorials 变量设置为 3 个起始位置,占用 4 个字符,并最大限度地减少浪费计算。第三次迭代之后的所有内容都在处理两位数的数学加。我想如果你对它有足够的坚持,你会在阶乘表上运行(如实现的那样)。占用 4 个字符,并最大限度地减少浪费的计算。第三次迭代之后的所有内容都在处理两位数的数学加。我想如果你对它有足够的坚持,你会在阶乘表上运行(如实现的那样)。占用 4 个字符,并最大限度地减少浪费的计算。第三次迭代之后的所有内容都在处理两位数的数学加。我想如果你对它有足够的坚持,你会在阶乘表上运行(如实现的那样)。

这之后我有什么打算?本地&|会话存储,以允许对所需迭代进行逐个缓存,基本上处理上面提到的“表”问题。这也将大量节省数据库和服务器端空间。但是,如果您使用 localStorage,您基本上会占用用户计算机上的空间,只是为了存储数字列表并让他们的屏幕看起来更快,但是在很长一段时间内有巨大的需求,这会很慢。我认为 sessionStorage (在 Tab 离开后清除)将是一条更好的路线。可能将其与自平衡服务器/本地依赖缓存相结合?用户 A 需要 X 次迭代。用户 B 需要 Y 次迭代。X+Y/2=需要本地缓存的数量。然后只需为每个用户检测并修改加载时间和执行时间基准,直到它调整自己以优化网站本身。谢谢!

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

此编辑实现了另一个 Stack 建议,并允许我将该函数称为阶乘(true)(5),这是我设定的目标之一。:3 我还删除了一些不必要的分配,并简写了一些非公共变量名。

于 2016-05-10T23:18:19.350 回答
2

这是一个使用较新的 javascript 函数fillmapreduce构造函数 (以及粗箭头语法)的函数:

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

编辑:更新为处理 n === 0

于 2016-01-04T22:36:03.720 回答
1
 used closure  for this with the helper (getFact) , I think this approach is neat hope this helps  


    factorial of n : using closures*/

    function getFact(num) {

        if (num > 1)
            return num * getFact(num - 1);
        else
            return 1;

    }


    function makeFact(fn) {
        return function(num) {
            return fn(num);
        }


    }

   makeFact(getFact)(5) //120
于 2014-11-08T09:11:42.303 回答
1
var factorial = (function() {
    var cache = [1];
    return function(value) {
        for (var index = cache.length; index <= value; index++) {
            cache[index] = index * cache[index - 1]
        }
        return cache[value];
    }
})();

我发现这在相同的情况下很有用:

function factorialDivision(n, d) {
    var value = 1;
    for (d++ < n) {
        value *= d;
    }
    return value;
}
于 2014-01-14T15:48:53.837 回答
1

这将返回 n 的阶乘

function f(n) {
    var e = n;
    if (e == 1 | e == 0) return 1;
    while (n--) {
        if (n < 1)
            break;
        e *= n;
    }
    return e
}
于 2014-01-13T16:48:04.250 回答
1

由于阶乘只是从给定的数字到 1 的退化乘法,因此循环乘法确实更容易:

Math.factorial = function(n) {

  if (n === 0||n === 1) {

    return 1;

  } else {

    for(var i = n; i > 0; --i) { //always make sure to decrement the value BEFORE it's tacked onto the original as a product
      n *= i;
    }

    return n;

  }

}
于 2014-01-27T05:05:43.273 回答
1
var factorial = function(numToBeFactored)
    {
        if (numToBeFactored == 0)
                return 1;
            var numLength = 0;
            var numBeingFactored = 1;
        /*goes through the loop numToBeFactored times and each time multiplies numBeingFactored by one less than the last loop*/
            for (numLength = 0; numLength < numToBeFactored; numLength++)
            {
                numBeingFactored *= (numToBeFactored - numLength);
            }
            return numBeingFactored;
    };
于 2015-02-09T14:57:08.697 回答
1

一开始它可能非常简单,也许从这个简短的代码中,您可以根据您的需要更好地设置它:

<body>

    <button  onclick="fact()">Open the Prompt</button>
    <h2 id="output"></h2>

    <script>

    function fact(){ 
        var enter=prompt("Enter You Factorial Number Bellow :","");
        var Num_enter=Number(enter);

        for (var i=1,FactNumber=1;i<=Num_enter;i++){

        FactNumber=FactNumber*i;
        }

        if(Num_enter){ 
           document.getElementById("output").textContent="the factorial of "+ Num_enter + " is: "+Num_enter+"!= "+ FactNumber;
        }


     }

   </script>


 </body>
于 2015-06-09T07:35:00.183 回答
1
    function factorial(num){    
        var num=Number(num);
        if (num < 0){
            return "this is not a positive number";
        }
        else{

        for (i=2 , f=1 ; i<=num;i++){

        f=f*i;

        }

    return f;
    }
    }
    // the function assumes that a number < 0 is null and factorial of any word is considerate as factorial of 0 //

    console.log("-5! ="+factorial(-1));
    console.log("something ="+factorial("something"));
    console.log("15! ="+factorial(15));
    console.log("20! ="+factorial(20));
于 2015-06-12T00:21:22.820 回答
1
var factorial = function() {
    var memo = [1];
    var facto = function (n) {
        var result = memo[n];
    if (typeof result !== 'number'){
        result = facto(n-1)*n;
    }
        return result;
    };
    return facto;
}();

要使用 memoization 计算数字的阶乘,请调用factorial(n).

于 2015-09-10T09:39:03.917 回答
1

您可以使用

function factorial(n) {
    return [...Array(n+1).keys()].slice(1).reduce( (a,b) => a * b, 1 );
}
于 2017-04-28T19:48:00.760 回答
1

迭代和递归选项的一条线解决方案;

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

const sf = n => {for (var i = n, c = 1; i > 1; i --) c *= i; return c;}
于 2018-09-10T11:39:37.140 回答
1

虽然上面所有的都很好,但它们对我来说都太长了,所以我自己做了。

function factorial(n) {              //"n" is the number used in the factorial

    var ans = 1,                     //define the base variables
        neg = false,
        n = Math.round(n);

    if (n<0 || !n) neg = true;       //check if the number is negative or if the number doesn't exist

    for (var i=1;i<=n;i++) ans *= i; //the actual repeating code, won't run if the number is less than or equal to 0

    return neg ? NaN : ans;          //return the answer if the original was positive

}

循环的工作方式for将自动不对任何低于 1 的数字执行任何操作。所以基本上“如果 (The Number) 小于或等于0,则返回1.

if (n<0 || !n) neg = true;return neg ? NaN : ans;一起说“如果数字是负数,则返回NaN(非数字)”。它们还会检查数字是否存在,如果数字不存在,将返回NaN(Not a Number)。

笔记

至少在 Chrome v50.0.2661.86(64 位)上,它的最大值为 170。因此,如果您在高于 170(例如 171)的数字上运行此函数,它将返回infinity.

于 2016-04-25T17:01:10.830 回答
1

这是我所知道的制作阶乘函数的最简单方法

function factorial(num) {

    var result = 1;
    for(var i = 2; i<= num; i++) {
        result *= i;
    }
    return result;
}
于 2017-01-20T17:32:12.990 回答
1

好吧,这个问题有足够多的答案,但只是为阶乘和反向阶乘发布一个可读、快速和简短的解决方案。

{
    const cache = [1, 1];
    let i = 2;

    function factorial(n) {
        if (!isFinite(n = parseInt(n)) || n < 0)
            throw new Error('argument for factorial has to be a positive finite integer but was ' + n);

        for (; i <= n; i++)
            cache[i] = cache[i - 1] * i;

        return cache[n];
    }

    function reverseFactorial(n) {
        if (!isFinite(n = parseFloat(n)) || n < 0)
            throw new Error('argument for reverseFactorial has to be a positive finite floatingpoint number but was ' + n);

        let f = 1;

        while (true)
            if (factorial(++f) >= n)
                return f - 1; // lower bound (f! which will fit in the given n, for upper bound just return f)

    }
}

reverseFactorial将返回一个适合给定 nk的最大的。k!

这两个功能都受益于缓存构建factorial

如果你想稍微测试一下:

for (let i = 0; i < 10; i++) {
    let random = Math.random() * 100;
    random = factorial(random) * Math.random() * random;

    const reverse = reverseFactorial(random);
    const resultOfReverse = factorial(reverse);

    function expo(x) {
        return x.toExponential(2);
    }

    console.log('%s fits %d! which is %s (upper bound %d! is %s)', expo(random), reverse, expo(resultOfReverse), reverse + 1, expo(factorial(reverse + 1)));
}
于 2018-06-10T20:34:46.660 回答
0

她是我使用 IIFy 函数和递归的解决方案:

console.log((function factorial(n){return (n>1)?n*factorial(n-1):1;})(10))

这是在一行代码中获得阶乘输出的最佳解决方案。

于 2019-08-22T15:09:29.030 回答
0

迭代:

Math.factorial=n=>{for(var o=n;n>1;)o*=--n;return o};

递归:

Math.factorial=n=>n>1?n--*Math.fac(n):1;

预先计算:

(_=>{let f=[],i=0;for(;i<171;i++)f[i]=(n=>{for(var o=n;n>1;)o*=--n;return o})(i);Math.factorial=n=>{n=Math.round(n);return n<171?f[n]:Infinity}})();

https://code.sololearn.com/Wj4rlA27C9fD。在这里,我可能会发布更多解决方案。

于 2017-11-03T14:12:22.803 回答
-1

老问题,但我发现这种方法非常易读和简单

function factorialize(num) {
  var arr = [];
  var result;
  if ( num === 0 || num === 1 ) {
    return 1;
  } else {
    for (var i = num; i > 0; i--) {
      arr.push(i);
      result = arr.reduce(function(previousVal, nextVal){
                return previousVal * nextVal;
              });
    }
    return result;
  }
}
于 2017-01-15T19:32:40.687 回答
-2
    var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    numbers.reduce(function factorial(previous, current) {
      return previous * current;
      });

   console.log(numbers);
于 2017-01-04T11:58:39.320 回答