如何在JavaScript中编写一个可以计算十进制数阶乘(或伽马函数)的函数?例如,我应该如何计算2.33!
?
如何在JavaScript中编写一个可以计算十进制数阶乘(或伽马函数)的函数?例如,我应该如何计算2.33!
?
var g = 7;
var C = [0.99999999999980993, 676.5203681218851, -1259.1392167224028,771.32342877765313, -176.61502916214059, 12.507343278686905, -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7];
function gamma(z) {
if (z < 0.5) return Math.PI / (Math.sin(Math.PI * z) * gamma(1 - z));
else {
z -= 1;
var x = C[0];
for (var i = 1; i < g + 2; i++)
x += C[i] / (z + i);
var t = z + g + 0.5;
return Math.sqrt(2 * Math.PI) * Math.pow(t, (z + 0.5)) * Math.exp(-t) * x;
}
}
当然,它不支持虚数,因为js不支持。
这可以直接在JavaScript中用一行代码实现:
function gamma(z) {
return Math.sqrt(2 * Math.PI / z) * Math.pow((1 / Math.E) * (z + 1 / (12 * z - 1 / (10 * z))), z);
}
您可以在jsFiddle上看到此操作的实例。
对于z>8,此方法可精确到8位,但对于较小的z,仅能准确保留几位数字。它的准确度不如Lanczos逼近法,但更简单,也稍微快一些。
请注意伽马函数和阶乘函数略有不同。阶乘函数可以用伽马函数来定义:
function factorial(n) {
return gamma(n + 1);
}
其中g是任意选择的常数,控制着逼近的精度。g越大,逼近的精度就越高。Ag(z)的定义如下:
最困难的部分是找到Ag(z),因为pn也是使用一个依赖于g的复杂公式定义的。
以下代码我不能太拿credit,因为我只是在写维基百科页面上Python程序的移植。
function gamma(n) { // accurate to about 15 decimal places
//some magic constants
var g = 7, // g represents the precision desired, p is the values of p[i] to plug into Lanczos' formula
p = [0.99999999999980993, 676.5203681218851, -1259.1392167224028, 771.32342877765313, -176.61502916214059, 12.507343278686905, -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7];
if(n < 0.5) {
return Math.PI / Math.sin(n * Math.PI) / gamma(1 - n);
}
else {
n--;
var x = p[0];
for(var i = 1; i < g + 2; i++) {
x += p[i] / (n + i);
}
var t = n + g + 0.5;
return Math.sqrt(2 * Math.PI) * Math.pow(t, (n + 0.5)) * Math.exp(-t) * x;
}
}
当然,根据伽玛函数的定义:
function factorial(n) {
return gamma(n + 1);
}
你可以在 jsFiddle 上 查看实际效果。
为了完善@apelsinapa的答案,修正整数计算(当输入整数时,我们没有得到整数解)。
@apelsinapa的好方法:
var g = 7;
var C = [0.99999999999980993, 676.5203681218851, -1259.1392167224028,771.32342877765313, -176.61502916214059, 12.507343278686905, -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7];
function gamma(z) {
if (z < 0.5) return Math.PI / (Math.sin(Math.PI * z) * gamma(1 - z));
else {
z -= 1;
var x = C[0];
for (var i = 1; i < g + 2; i++)
x += C[i] / (z + i);
var t = z + g + 0.5;
return Math.sqrt(2 * Math.PI) * Math.pow(t, (z + 0.5)) * Math.exp(-t) * x;
}
}
如果要得到一个整数的正确答案:
function factorialOfNumber(number) {
if (number % 1 != 0 || number<0){
return gamma(number + 1);
}
else {
if(number == 0) {
return 1;
}
for(var i = number; --i; ) {
number *= i;
}
return number;
}
}
这是我几年前写的一个版本...有点乱但经过测试 :)
var
M_GAMMA = [76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5],
M_GAMMAS = 6;
function gammaFn(x) // Modified to JavaScript from "Numerical Recipies in C" by P. Mainwaring
{
var i = 0, n = ++x, tmp = x + 5.5, ser = 1.000000000190015;
for (tmp -= (x + 0.5) * Math.log(tmp); i < M_GAMMAS; ++i) ser += M_GAMMA [i] / ++n;
return Math.log(2.5066282746310005 * ser / x) - tmp;
}
function fact(x) { return x > 1 ? Math.exp(gammaFn(x)) : 1 }
function combin(n, k) { return (Math.exp(gammaFn(n) - gammaFn(n - k) - gammaFn(k)) + 0.5) | 0 } // Ms Excel COMBIN() n! / k!(n - k)!
n = 49; k = 6; alert(fact(n) + ' ' + fact(k) + ' ' + combin(n, k)); // Lottery odds! (13,983,816)
Gamma函数和GammaLn函数如下:
function gammaLn(x) { return gammaFn(--x) }
function gamma(x) { return Math.exp(gammaLn(x)) }
:-)
如果你只是想要计算实数阶乘的函数,那么你只需要使用Lanczos逼近中的这段代码:
function = factorial(z) {
var g = 7;
var C = [0.99999999999980993, 676.5203681218851, -1259.1392167224028, 771.32342877765313, -176.61502916214059, 12.507343278686905, -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7];
var x = C[0];
for (var i = 1; i < g + 2; i++)
x += C[i] / (z + i);
var t = z + g + 0.5;
return Math.sqrt(2 * Math.PI) * Math.pow(t, (z + 0.5)) * Math.exp(-t) * x;
}
对于负数和小数的加法运算都非常有效。