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
我认为添加一个使用 big 的 lazy迭代阶乘函数 的工作示例会很有用数字得到准确结果与记忆和缓存作为比较
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);
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 lazyiterativefactorial 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);
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;
}
I still think Margus's answer is the best one. However if you want to calculate the factorials of numbers within the range 0 to 1 (ie the gamma function) as well, then you cannot use that approach because the lookup table will have to contain infinite values.
However, you can approximate the values of the factorials, and it's pretty fast, faster than recursively calling itself or looping it at least (especially when values start to get bigger).
A good approximation method is Lanczos's one
Here is an implementation in JavaScript (ported from a calculator I wrote months ago):
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;
}
You can now do cool stuff like factorial(0.41), etc however accuracy might be a little off, after all, it is an approximation of the result.
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;
})();
您可以预先计算一些值以加快速度。
Lookup table is the obvious way to go, if you're working with natural numbers. To calculate any factorial in real-time, you can speed it with a cache, saving the numbers you've calculated before. Something like:
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;
})();
You can precalculate some values in order to speed it even more.
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).
function factorial(n, r = 1) {
while (n > 0) r *= n--;
return r;
}
// Default parameters `r = 1`,
// were introduced in ES6
这是我的推理:
递归函数,即使具有记忆功能,也具有函数调用的开销(基本上将函数推入堆栈),这比使用循环
While for 循环和 的性能要低while 循环具有类似的性能,没有初始化表达式和最终表达式的 for 循环看起来很奇怪;可能更好地将 for(; n > 0;) 写为 while(n > 0)
只有两个参数 n 和 使用 r,因此理论上较少的参数意味着分配内存所花费的时间更少
使用递减循环来检查 n 是否为零 - 我听说计算机更擅长检查二进制数(0 和 1) 比检查其他整数时更重要
Fastest factorial function
I think that this loop-based version might be the fastest factorial function.
function factorial(n, r = 1) {
while (n > 0) r *= n--;
return r;
}
// Default parameters `r = 1`,
// were introduced in ES6
And here is my reasoning:
Recursive functions, even with memoization, have the overhead of a function call (basically pushing functions onto the stack) which is less performant than using a loop
While for loops and while loops have similar performance, a for loop without an initialization-expression and final-expression looks odd; probably better to write for(; n > 0;) as while(n > 0)
Only two parameters n and r are used, so in theory less parameters means less time spent allocating memory
Uses a decremented loop which checks if n is zero - I've heard theories that computers are better at checking binary numbers (0 and 1) than they are at checking other integers
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%。
Behold, the memoizer, which takes any single-argument function and memoizes it. Turns out to be marginally faster than @xPheRe's solution, including the limit on the size of the cache and associated checking, because I use shortcircuiting and so on.
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;
}());
Approximately 25x faster on my machine in Chrome than the recursive version, and 10% faster than xPheRe's.
Exploiting the fact that Number.MAX_VALUE < 171!, we can simply use a complete lookup table consisting of just 171 compact array elements taking up less than 1.4 kilobytes of memory.
A fast lookup function with runtime complexity O(1) and minimal array access overhead would then look as follows:
// 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
This is as precise and as fast as it gets using the Number datatype. Computing the lookup table in Javascript - as some other answers suggest - will reduce precision when n! > Number.MAX_SAFE_INTEGER.
Compressing the runtime table via gzip reduces its size on disk from about 3.6 to 1.8 kilobytes.
I came across this post. Inspired by all contributions here I came up with my own version, which has two features that I haven't seen discussed before: 1) A check to ensure the argument is a non-negative integer 2) Making a unit out of the cache and the function to make it one self contained bit of code. For fun, I tried to make it as compact as possible. Some may find that elegant, others may think it terribly obscure. Anyway, here it is:
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];
You can either pre fill the cache, or allow it to be filled as the calls go by. But the initial element (for fact(0) must be present or it will break.
The code to calculate factorial depends on your requirements.
Are you concerned about overflow?
What range of inputs will you have?
Is it more important for you to minimize size or time?
What are you going to do with the factorial?
Regarding points 1 and 4, it is often more useful to have a function to evaluate the log of the factorial directly rather than to have a function to evaluate factorial itself.
Here's a blog post that discusses these issues. Here is some C# code for computing log factorial that would be trivial to port to JavaScript. But it may not be best for your needs depending on your answers to the questions above.
This is working example uses BigInt, because many answers here all escape the safe boundary of Number (MDN) almost right away. It's not the fastest but it's simple and thus clearer for adapting other optimizations (like a cache of the first 100 numbers).
function factorial(n) {
let p = BigInt(1)
for (let i = BigInt(n); i > 0; i--) p *= i
return p
}
function rFact(n, acc)
{
if (n == 0 || n == 1) return acc;
else return rFact(n-1, acc*n);
}
调用它:
rFact(x, 1);
Just for completeness, here is a recursive version that would allow tail call optimization. I'm not sure if tail call optimizations are performed in JavaScript though..
function rFact(n, acc)
{
if (n == 0 || n == 1) return acc;
else return rFact(n-1, acc*n);
}
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 对象中,该对象是对象文字,因此没有原型。而只是将它们直接绑定到函数。
This is an iterative solution that uses less stack space and save previously computed values in a self-memoizing way:
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
Also note that I am adding this to the Math object which is an object literal so there is no prototype. Rather just binding these to the function directly.
Math.factorial = Math.fact = function(n) {
if (isNaN(n)||n<0) return undefined;
var f = 1; while (n > 1) {
f *= n--;
} return f;
};
I believe the following is the most sustainable and efficient piece of code from the comments above. You can use this in your global application js architecture... and, not worry about writing it in multiple namespaces (since its a task which probably doesn't need much augmenting). I've included 2 method names (based on preference) but both can be used as they're just references.
Math.factorial = Math.fact = function(n) {
if (isNaN(n)||n<0) return undefined;
var f = 1; while (n > 1) {
f *= n--;
} return f;
};
// 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
// 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
This does the caching of the first 100 values on the fly, and does not introduce an external variable into scope for the cache, storing the values as properties of the function object itself, which means that if you know factorial(n) has already been calculated, you can simply refer to it as factorial[n], which is slightly more efficient. Running these first 100 values will take sub-millisecond time in modern browsers.
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;
}
})();
Cached loop should be fastest (at least when called multiple times)
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;
}
})();
这之后我有什么计划呢? 本地和会话存储允许根据具体情况缓存所需的迭代,本质上是处理上面提到的“表”问题。这也将大量节省数据库和服务器端空间。但是,如果您使用 localStorage,您实际上会占用用户计算机上的空间,只是为了存储数字列表并使他们的屏幕看起来更快,但是在很长一段时间内,如果有巨大的需求,这会很慢。我认为 sessionStorage (选项卡离开后清除)将是一个更好的路线。可能将其与自平衡服务器/本地依赖缓存结合起来? 用户 A 需要 X 次迭代。 用户 B 需要 Y 次迭代。 X+Y/2=本地缓存所需的数量。 然后,只需检测并调整每个用户的实时加载时间和执行时间基准,直到它自行调整以优化网站本身。 谢谢!
A simple set of Memoized factorials that require redundant calculations, may be processed with "multiply by 1", or are one digit that is a simple equation not worth processing live.
After reviewing the input from other members (excluding the Log advice, although I may implement that later) I went ahead and threw together a script that is fairly simple. I started with a simple uneducated JavaScript OOP example and built a little class to handle factorials. I then implemented my version of the Memoization that was suggested above. I also implemented the shorthand Factorialization however I made a small error adjustment; I changed the "n<2" to "n<3". "n<2" would still process n=2 which would be a waste, because you would iterate for a 2*1=2; this is a waste in my opinion. I altered it to "n<3"; because if n is 1 or 2 it will simply return n, if it is 3 or more it will evaluate normally. Of course as rules apply, I placed my functions in descending order of assumed execution. I added in the bool(true|false) option to allow quick altering between memo'ed and normal execution (You just never know when you want to swap around on your page without needing to change the "style") As I said before the memoized factorials variable is set with the 3 starting positions, taking 4 characters, and minimizing wasteful calculations. Everything past the third iteration you are handling double digit math plus. I figure if you where a stickler enough about it you would run on a factorial table (as implemented).
What have I planned after this? local&|session storage to allow for a case by case cache of needed iterations, essentially handling the "table" issue spoken above. This would also massively save database and server side space. However, if you go with localStorage you would essentially be sucking up space on your users computer simply to store a list of numbers and make their screen LOOK faster, however over a long period of time with an immense need this would be slow. I am thinking sessionStorage (clearing after Tab leaves) would be a much better route. Possibly combine this with a self balancing server/local dependent cache? User A needs X iterations. User B need Y iterations. X+Y/2=Amount needed locally cached. Then just detect and fiddle with load-time and execute-time benchmarks live for every user until it adjusts itself to optimization for the site itself. Thanks!
This edit implements another Stack suggestion and allows me to call the function as factorial(true)(5), which was one of my goals setting out. :3 I also removed some needless assigning, and shorthanded some non-public variable names.
发布评论
评论(30)
您可以搜索 (1...100)!在 Wolfram|Alpha 上预先计算阶乘序列。
前 100 个数字是:
如果您仍想自己计算这些值,可以使用 memoization:
编辑:21.08.2014
解决方案 2
我认为添加一个使用 big 的 lazy 迭代 阶乘函数 的工作示例会很有用数字得到准确结果与记忆和缓存作为比较
我假设你会使用某种闭包来限制变量名称可见性。
参考:BigNumber
沙箱:JsFiddle
You can search for (1...100)! on Wolfram|Alpha to pre-calculate the factorial sequence.
The first 100 numbers are:
If you still want to calculate the values yourself, you can use memoization:
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
I assume you would use some kind of closure to limit variable name visibility.
Ref: BigNumber
Sandbox: JsFiddle
你应该使用循环。
以下是通过计算 100 的阶乘 10.000 次进行基准测试的两个版本。
递归
迭代
直播地址:http://jsfiddle.net/xMpTv /
我的结果显示:
- 递归 ~ 150 毫秒
- 迭代 ~ 5 毫秒..
You should use a loop.
Here are two versions benchmarked by calculating the factorial of 100 for 10.000 times.
Recursive
Iterative
Live at : http://jsfiddle.net/xMpTv/
My results show:
- Recursive ~ 150 milliseconds
- Iterative ~ 5 milliseconds..
我仍然认为马格斯的答案是最好的。但是,如果您还想计算 0 到 1 范围内的数字的阶乘(即伽玛函数),则不能使用该方法,因为查找表必须包含无限值。
但是,您可以近似阶乘的值,而且速度相当快,至少比递归调用自身或循环它要快(尤其是当值开始变大时)。
Lanczos 的一个很好的近似方法是
JavaScript 中的一个实现(从我几个月前编写的计算器移植而来):
您现在可以做一些很酷的事情,例如
factorial(0.41)
等,但是准确性可能会有点关闭,毕竟这是结果的近似值。I still think Margus's answer is the best one. However if you want to calculate the factorials of numbers within the range 0 to 1 (ie the gamma function) as well, then you cannot use that approach because the lookup table will have to contain infinite values.
However, you can approximate the values of the factorials, and it's pretty fast, faster than recursively calling itself or looping it at least (especially when values start to get bigger).
A good approximation method is Lanczos's one
Here is an implementation in JavaScript (ported from a calculator I wrote months ago):
You can now do cool stuff like
factorial(0.41)
, etc however accuracy might be a little off, after all, it is an approximation of the result.这是我的解决方案:
这是我发现的最简单的方法(更少的字符/行),只有一个具有一行代码行的函数。
编辑:
如果您确实想保存一些字符,可以使用 箭头函数 (21字节):
Here is my solution:
It's the simplest way (less characters / lines) I've found, only a function with one code line.
Edit:
If you really want to save some chars you can go with an Arrow Function (21 bytes):
只需一行 ES6
Just One line with ES6
如果您使用自然数,查找表是显而易见的方法。
要实时计算任何阶乘,您可以使用缓存来加速计算,保存您之前计算过的数字。例如:
您可以预先计算一些值以加快速度。
Lookup table is the obvious way to go, if you're working with natural numbers.
To calculate any factorial in real-time, you can speed it with a cache, saving the numbers you've calculated before. Something like:
You can precalculate some values in order to speed it even more.
简短而简单的递归函数(您也可以使用循环来完成,但我认为这不会对性能产生任何影响):
对于非常大的 n,您可以使用 斯特林近似 - 但这只会给你一个近似值。
编辑:对为什么我对此投反对票的评论会很好......
编辑2:这将是使用循环的解决方案(这将是更好的选择):
我认为最好的解决方案是使用缓存的值,正如 Margus 提到的那样,并使用 对于较大的值,采用斯特林近似(假设您必须非常快,并且不必在如此大的数字上那么精确)。
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):
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):
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).
最快的阶乘函数
我认为这个基于循环的版本可能是最快的阶乘函数。
这是我的推理:
for
循环和的性能要低while
循环具有类似的性能,没有初始化表达式和最终表达式的for
循环看起来很奇怪;可能更好地将for(; n > 0;)
写为while(n > 0)
n
和使用 r
,因此理论上较少的参数意味着分配内存所花费的时间更少n
是否为零 - 我听说计算机更擅长检查二进制数(0 和 1) 比检查其他整数时更重要Fastest factorial function
I think that this loop-based version might be the fastest factorial function.
And here is my reasoning:
for
loops andwhile
loops have similar performance, afor
loop without an initialization-expression and final-expression looks odd; probably better to writefor(; n > 0;)
aswhile(n > 0)
n
andr
are used, so in theory less parameters means less time spent allocating memoryn
is zero - I've heard theories that computers are better at checking binary numbers (0 and 1) than they are at checking other integers看哪,记忆器,它接受任何单参数函数并记忆它。事实证明比 @xPheRe 的 解决方案 稍快,包括缓存大小和相关检查的限制,因为我使用短路等。
在我的 Chrome 机器上,速度比递归版本快大约 25 倍,比 xPheRe 快 10%。
Behold, the memoizer, which takes any single-argument function and memoizes it. Turns out to be marginally faster than @xPheRe's solution, including the limit on the size of the cache and associated checking, because I use shortcircuiting and so on.
Approximately 25x faster on my machine in Chrome than the recursive version, and 10% faster than xPheRe's.
使用 ES6 非常简单
const Factorial = n => ? (n * Factorial(n-1)) : 1;
请参阅此处的示例
It is very simple using ES6
const factorial = n => n ? (n * factorial(n-1)) : 1;
See an example here
利用 Number.MAX_VALUE
Number.MAX_VALUE
171!
,我们可以简单地使用一个完整的查找表,该表仅由 171 个紧凑数组元素组成,占用内存不到 1.4 KB。运行时复杂度O(1)和最小数组访问开销的快速查找函数将如下所示:
这与使用
Number
数据类型一样精确和快速。在 Javascript 中计算查找表 - 正如其他一些答案所建议的 - 当n! 时会降低精度。 >数量。MAX_SAFE_INTEGER
。通过 gzip 压缩运行时表可将其在磁盘上的大小从大约 3.6 KB 减少到 1.8 KB。
Exploiting the fact that
Number.MAX_VALUE < 171!
, we can simply use a complete lookup table consisting of just 171 compact array elements taking up less than 1.4 kilobytes of memory.A fast lookup function with runtime complexity O(1) and minimal array access overhead would then look as follows:
This is as precise and as fast as it gets using the
Number
datatype. Computing the lookup table in Javascript - as some other answers suggest - will reduce precision whenn! > Number.MAX_SAFE_INTEGER
.Compressing the runtime table via gzip reduces its size on disk from about 3.6 to 1.8 kilobytes.
使用 ES6 你可以快速而简短地实现它:
Using ES6 you can achieve it both fast and short:
我偶然发现了这个帖子。受到这里所有贡献的启发,我想出了自己的版本,它有两个我以前没有讨论过的功能:
1) 检查以确保参数是非负整数
2) 从缓存中创建一个单元并使其成为一段自包含的代码。
为了好玩,我尝试使其尽可能紧凑。有些人可能会觉得它很优雅,而另一些人可能会认为它非常晦涩难懂。不管怎样,就是这样:
您可以预先填充缓存,或者允许它在调用过程中填充。但是初始元素(对于fact(0) 必须存在,否则它将破坏。
享受吧:)
I came across this post. Inspired by all contributions here I came up with my own version, which has two features that I haven't seen discussed before:
1) A check to ensure the argument is a non-negative integer
2) Making a unit out of the cache and the function to make it one self contained bit of code.
For fun, I tried to make it as compact as possible. Some may find that elegant, others may think it terribly obscure. Anyway, here it is:
You can either pre fill the cache, or allow it to be filled as the calls go by. But the initial element (for fact(0) must be present or it will break.
Enjoy :)
这是一种解决方案:
Here is one solution:
计算阶乘的代码取决于您的要求。
关于第 1 点和第 4 点,使用一个函数来直接计算阶乘的 log 通常比使用一个函数来计算阶乘本身更有用。
这是一篇博客文章,讨论了这些问题。这里有一些用于计算对数阶乘的 C# 代码,移植到 JavaScript 很简单。但根据您对上述问题的回答,它可能并不适合您的需求。
The code to calculate factorial depends on your requirements.
Regarding points 1 and 4, it is often more useful to have a function to evaluate the log of the factorial directly rather than to have a function to evaluate factorial itself.
Here's a blog post that discusses these issues. Here is some C# code for computing log factorial that would be trivial to port to JavaScript. But it may not be best for your needs depending on your answers to the questions above.
这是一个紧凑的基于循环的版本
或者您可以覆盖数学对象(递归版本):
或者加入两种方法......
This is a compact loop-based version
Or you might override Math object (recursive version):
Or join both approaches ...
一行回答:
One line answer:
使用
BigInt
进行迭代阶乘以确保安全这是使用
BigInt
的工作示例,因为这里的许多答案都逃脱了Number
(MDN) 几乎马上就可以了。它不是最快的,但它很简单,因此可以更清晰地适应其他优化(例如前 100 个数字的缓存)。用法示例
1303n
之类的数字文字末尾的n
表示它是BigInt
类型。BigInt
与Number
混合使用,除非您明确强制它们,否则可能会导致准确性损失。Iterative factorial with
BigInt
for safetyThis is working example uses
BigInt
, because many answers here all escape the safe boundary ofNumber
(MDN) almost right away. It's not the fastest but it's simple and thus clearer for adapting other optimizations (like a cache of the first 100 numbers).Example Usage
n
at the end of a numeric literal like1303n
indicates it's aBigInt
type.BigInt
withNumber
unless you explicitly coerce them, and that doing so could cause a loss in accuracy.只是为了完整性,这里是一个递归版本,允许
尾调用优化。我不确定尾部调用优化是否在 JavaScript 中执行。
调用它:
Just for completeness, here is a recursive version that would allow
tail call optimization. I'm not sure if tail call optimizations are performed in JavaScript though..
To call it:
这是一个迭代解决方案,使用更少的堆栈空间并以自记忆方式保存先前计算的值:
另请注意,我将其添加到 Math 对象中,该对象是对象文字,因此没有原型。而只是将它们直接绑定到函数。
This is an iterative solution that uses less stack space and save previously computed values in a self-memoizing way:
Also note that I am adding this to the Math object which is an object literal so there is no prototype. Rather just binding these to the function directly.
我相信以下是上述评论中最可持续和最有效的代码。您可以在全局应用程序 js 架构中使用它......并且不用担心将其编写在多个命名空间中(因为它是一项可能不需要太多增强的任务)。我已经包含了 2 个方法名称(根据偏好),但这两个都可以使用,因为它们只是参考。
I believe the following is the most sustainable and efficient piece of code from the comments above. You can use this in your global application js architecture... and, not worry about writing it in multiple namespaces (since its a task which probably doesn't need much augmenting). I've included 2 method names (based on preference) but both can be used as they're just references.
这会动态缓存前 100 个值,并且不会将外部变量引入缓存范围,而是将这些值存储为函数对象本身的属性,这意味着如果您知道 Factorial(n) 已经计算出来了,你可以简单地把它称为
factorial[n]
,这样效率稍微高一些。在现代浏览器中,运行前 100 个值将花费亚毫秒的时间。This does the caching of the first 100 values on the fly, and does not introduce an external variable into scope for the cache, storing the values as properties of the function object itself, which means that if you know
factorial(n)
has already been calculated, you can simply refer to it asfactorial[n]
, which is slightly more efficient. Running these first 100 values will take sub-millisecond time in modern browsers.这是计算正阶乘和负阶乘的实现。
它既快速又简单。
Here is an implementation which calculates both positive and negative factorials.
It's fast and simple.
这是我自己制作的,不要使用超过 170 或低于 2 的数字。
Here's one I made myself, don't use numbers over 170 or under 2.
这是我的代码
Here is my code
缓存循环应该是最快的(至少在多次调用时)
Cached loop should be fastest (at least when called multiple times)
在审查了其他成员的输入(不包括日志建议,尽管我可能稍后会实现)后,我继续编写了一个相当简单的脚本。我从一个简单的 JavaScript OOP 示例开始,构建了一个小类来处理阶乘。然后我实现了上面建议的我的记忆化版本。我还实现了简写阶乘,但是我做了一个小的错误调整;我将“n<2”更改为“n<3”。 "n<2" 仍然会处理 n=2 这将是一种浪费,因为您将迭代 2*1=2;我认为这是一种浪费。我把它改成了“n<3”;因为如果 n 是 1 或 2,它将简单地返回 n,如果它是 3 或更大,它将正常评估。当然,根据规则的适用,我将函数按照假定执行的降序排列。我添加了 bool(true|false) 选项,以允许在备忘录执行和正常执行之间快速更改(您永远不知道何时想要在页面上交换而不需要更改“样式”)
正如我之前所说,记忆阶乘变量设置为 3 个起始位置,占用 4 个字符,并最大限度地减少浪费的计算。第三次迭代之后的所有内容都在处理两位数数学加。我想如果你对此有足够的坚持,你会在阶乘表上运行(已实现)。
这之后我有什么计划呢?
本地和会话存储允许根据具体情况缓存所需的迭代,本质上是处理上面提到的“表”问题。这也将大量节省数据库和服务器端空间。但是,如果您使用 localStorage,您实际上会占用用户计算机上的空间,只是为了存储数字列表并使他们的屏幕看起来更快,但是在很长一段时间内,如果有巨大的需求,这会很慢。我认为 sessionStorage (选项卡离开后清除)将是一个更好的路线。可能将其与自平衡服务器/本地依赖缓存结合起来?
用户 A 需要 X 次迭代。
用户 B 需要 Y 次迭代。
X+Y/2=本地缓存所需的数量。
然后,只需检测并调整每个用户的实时加载时间和执行时间基准,直到它自行调整以优化网站本身。
谢谢!
编辑3:
After reviewing the input from other members (excluding the Log advice, although I may implement that later) I went ahead and threw together a script that is fairly simple. I started with a simple uneducated JavaScript OOP example and built a little class to handle factorials. I then implemented my version of the Memoization that was suggested above. I also implemented the shorthand Factorialization however I made a small error adjustment; I changed the "n<2" to "n<3". "n<2" would still process n=2 which would be a waste, because you would iterate for a 2*1=2; this is a waste in my opinion. I altered it to "n<3"; because if n is 1 or 2 it will simply return n, if it is 3 or more it will evaluate normally. Of course as rules apply, I placed my functions in descending order of assumed execution. I added in the bool(true|false) option to allow quick altering between memo'ed and normal execution (You just never know when you want to swap around on your page without needing to change the "style")
As I said before the memoized factorials variable is set with the 3 starting positions, taking 4 characters, and minimizing wasteful calculations. Everything past the third iteration you are handling double digit math plus. I figure if you where a stickler enough about it you would run on a factorial table (as implemented).
What have I planned after this?
local&|session storage to allow for a case by case cache of needed iterations, essentially handling the "table" issue spoken above. This would also massively save database and server side space. However, if you go with localStorage you would essentially be sucking up space on your users computer simply to store a list of numbers and make their screen LOOK faster, however over a long period of time with an immense need this would be slow. I am thinking sessionStorage (clearing after Tab leaves) would be a much better route. Possibly combine this with a self balancing server/local dependent cache?
User A needs X iterations.
User B need Y iterations.
X+Y/2=Amount needed locally cached.
Then just detect and fiddle with load-time and execute-time benchmarks live for every user until it adjusts itself to optimization for the site itself.
Thanks!
Edit 3:
这是使用较新的 javascript 函数的一个 fill, 地图, < a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map" rel="nofollow noreferrer">减少 和 构造函数(和粗箭头语法):
编辑:更新为处理n === 0
Here is one using newer javascript functions fill, map, reduce and constructor (and fat arrow syntax):
Edit: updated to handle n === 0
根据Wolfram MathWorld:
因此,可以使用下面的方法来获取一个数的阶乘:
According to Wolfram MathWorld:
Therefore, you can use the following method to obtain the factorial of a number: