JavaScript中最快的阶乘函数是什么?[关闭]


94

寻找JavaScript 中阶乘函数的真正快速实现。有什么建议吗?


8
参数的可能范围是什么?
Nikita Rybak 2010年

5
您是否考虑过预先计算阶乘并将这些值存储在查找表中?
Waleed Amjad

2
这种功能的用途是什么?换句话说,您打算将其用于什么?
尖尖的

@Nikita Rybak,只有1个结果(n)。如果(n> 170)e =无穷大
肯(Ken)

@ Pointy,这是另一个数学计算器服务。
肯(Ken)

Answers:


110

您可以搜索(1 ... 100)!在Wolfram | Alpha上预先计算阶乘序列。

前100个数字是:

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

如果仍然要自己计算值,则可以使用备忘录

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

编辑:21.08.2014

解决方案2

我认为添加一个懒惰的 迭代 阶乘函数的工作示例会很有用,该函数使用大数来获得带有备忘录的准确结果,并将缓存作为比较

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

我假设您将使用某种闭包来限制变量名的可见性。

参考BigNumber 沙盒JsFiddle


6402373705728000之后的值将被截断,因此,如果要使用此方法,请确保在使用上述表之前将其转换为指数。
David Scott Kirby

1
@DavidScottKirby Javascript自动将这些数字转换为最接近的64位浮点表示形式。在代码中没有完整的精度数字的真正好处是减少了文件大小。
le_m

您的第二个解决方案可以简化为“ function factorial (n) { for (var i = f.length; i <= n; i++) f.push(f[i - 1].multiply(i.toString())); return f[n]; }另请参阅我的答案”,该答案使用了较新的内置BigInt库而不是第三方库。
帕特里克·罗伯茨

96

您应该使用循环。

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

递归的

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毫秒。


+1好答案!尽管在有多个调用来计算较大数字的阶乘时,记忆可能是合理的。
塔德克2012年

@Tadeck,谢谢。事实上,记忆化是在这种情况下非常有用,这就是为什么Margus答案被选为正确的:)
加布里埃尔Petrioli

1行版本的递归:function factorial(num){return(num == 1)?num:num * arguments.callee(num-1); }
jbyrd 2014年

2
@HWTech,您永远不会调用方法。您的测试比较了定义这两种方法的速度..而不是它们执行的时间..这是一个更好的测试(仅尝试15阶乘)
Gabriele Petrioli 2014年

3
而不是rval = rval * i;您可以写rval *= i;
Ryan

29

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

但是,您可以近似估计阶乘的值,它比递归调用自身或至少循环执行它要快得多(特别是当值开始变大时)。

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),但是准确性可能会有所下降,毕竟,这只是结果的近似值。


非常有趣的方法,谢谢。
肯(Ken)

刚刚为我节省了很多时间,非常感谢:)
nicolaskruchten 2011年

我建议将for循环下面的部分更改为var d3d4 = Math.exp((z + 0.5) * Math.log(z + 5.5) - z - 5.5); return d1 * d2 * d3d4;。这使您可以计算多达169个阶乘!而不是目前只有140!。这与使用Number数据类型170 的最大可表示阶乘非常接近。
le_m

18

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

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

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


3
我已经根据这个答案为任何给定的函数创建了一个自动内存(也稍微快一些了:)),还包括了缓存大小的限制。stackoverflow.com/a/10031674/36537
菲尔H

16

这是我的解决方案:

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

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


编辑:
如果您真的想保存一些字符,则可以使用箭头功能 (21字节)

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

7
节省更多f=n=>n?f(n-1)*n:1...
le_m

不幸的是,即使很高兴看到它并且形式简短,这也是最慢的方法。
Zibri

11

ES6只需一行

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


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

10

简短易用的递归函数(您也可以使用循环来实现,但我认为这不会对性能产生任何影响):

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

对于非常大的n,您可以使用斯特林近似法 -但这只会给您一个近似值。

编辑:关于为什么我对此不满意的评论本来不错...

EDIT2:这将是使用循环的解决方案(这将是更好的选择):

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

我认为最好的解决方案是使用缓存的值(如Margus所述),并对较大的值使用Stirlings近似值(假设您必须非常并且不必对这么大的数字那么精确)。


3
在没有尾部调用优化的语言(即,使用最广泛的语言)中,最好使用容易实现的非递归实现,尽管有很多解决方法:paulbarry.com/articles/2009/08/30 / tail-call-optimization
Daniel Earwicker 2010年

的确确实不是那么快,因为如果实施了TCO,它甚至都不会使用TCO。但这很简单,我不会拒绝投票。当然不是最快的。
haylem 2010年

此函数甚至无法进行尾调用优化,因为递归调用不在尾位置。
Fred Foo 2010年

3
@Josh,(不是下降投票者)最快的循环是一个相当大的循环..
Gabriele Petrioli 2010年

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%。


6

最快的阶乘函数

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

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

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

这是我的理由:

  • 递归函数(即使带有备忘录)也具有函数调用(基本上将函数推入堆栈)的开销,这比使用循环的性能差
  • 尽管for循环和while循环具有相似的性能,但是for没有初始化表达式和最终表达式的循环看起来很奇怪。可能更好地写for(; n > 0;)while(n > 0)
  • 仅使用n和的两个参数r,因此从理论上讲,更少的参数意味着更少的分配内存时间
  • 使用递减的循环检查是否n为零-我听说过这样的理论:计算机比检查其他整数更好地检查二进制数(0和1)

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)必须存在,否则它将中断。

请享用 :)





3

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

  1. 您是否担心溢出?
  2. 您将输入什么范围的输入?
  3. 对您来说,最小化尺寸或时间更重要吗?
  4. 你要怎么处理阶乘?

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

这是讨论这些问题的博客文章。这是一些用于计算对数阶乘的C#代码,对于移植到JavaScript而言,这是微不足道的。但这可能不是最适合您的需求,具体取决于您对上述问题的回答。


编号列表可能应该在注释中。剩下的就是两个链接,不鼓励仅链接的答案。
巴瑞特

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

或加入两种方法...


1
我在上面的代码中修复了它。谢谢!
桑德罗·罗莎

3

利用的事实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千字节减少到1.8千字节。


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


3

迭代阶乘与BigInt安全性

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

这是工作示例使用的BigInt,因为这里的许多答案Number几乎都立刻逃脱了(MDN)的安全范围。它不是最快的,但是它很简单,因此可以更轻松地适应其他优化(例如前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在数字文字像年底1303n表示这是一个BigInt类型。
  • 请记住,除非明确强制将它们混合BigInt使用,Number否则可能会导致准确性降低。

3

使用ES6功能,可以在编写代码一行无递归

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


2

仅出于完整性考虑,这里有一个递归版本,允许进行尾部调用优化。我不确定是否在JavaScript中执行了尾部调用优化。

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

调用它:

rFact(x, 1);

ES6支持TCO,但afaik尚未在任何主要引擎中默认启用此功能
le_m

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对象(这是一个对象常量)中,因此没有原型。而是直接将它们直接绑定到函数。


这实际上并没有充分利用子问题的记忆-例如,Math.factorial(100); Math.factorial(500);将两次计算1..100乘法。
巴瑞特

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

通过与开始你的乘法n * (n-1) * (n-2) * ... * 1,而不是倒过来,你失去多达4位精度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

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


我发现21岁以后!数字不可靠。
AutoSponge 2012年

@AutoSponge这是因为21! > Number.MAX_SAFE_INTEGER,因此不能安全地表示为64位浮点数。
le_m


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

通过用n *(n-1)*(n-2)* ... * 1而不是反数开始乘法,您可以将n >> 20的精度放宽至4位。全局变量i,执行太多的Number转换,并给出0的错误结果!(如您所说,但是为什么?)。
le_m

2

这是我的代码

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

1
如果(n> 170)e =无穷大。而且您的代码将产生大量的代码。不会有溢出吗?
2014年

的结果不正确factorial(0)。另外,通过从n *(n-1)*(n-2)* ... * 1开始乘法运算,而不是反过来,您可以将n >> 20的精度放宽至4位。@prime:170! > Number.MAX_VALUE最好用表示Infinity
le_m

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

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;如果n为3或更大,它将正常求值。当然,随着规则的应用,我将函数按假定执行的降序排列。我添加了bool(true | false)选项,以允许在备忘和正常执行之间进行快速更改(您永远不知道何时要在页面上交换而无需更改“样式”),就像我之前所说的那样。记住的阶乘变量设置为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建议,并允许我将该函数称为factorial(true)(5),这是我设定的目标之一。:3我还删除了一些不必要的分配,并简化了一些非公共变量的名称。


返回undefined0!。ES6允许替换isNumericNumber.isInteger。像这样factorials[0][factorials[1]]=factorials[0][factorial_index]*(factorials[1]+1);的行是完全不可读的。
le_m

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


2
那是一堆很难看懂的代码。
丛林dev17年

1
那是个好主意。为什么不将长度遍历两次,为什么不将所有逻辑都转换为reduce函数,并使用其初始值来处理边缘情况n === 0Math.factorial = n => Array.from({ length: n }).reduce((product, _, i) => product * (i + 1), 1)
AlexSashaRegan

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.