10

JavaScript で 10 進数の階乗 (またはガンマ関数)を計算する関数を作成するにはどうすればよいですか? たとえば、どのように計算できます2.33!か?

4

6 に答える 6

11

既存の解決策を見つけたかもしれません... これは Lanczos メソッドの実装です。スウェーデンのウィキペディア ( http://sv.wikipedia.org/wiki/Gammafunktionen ) で見つけました。これは Python で書かれており、小数点以下 15 桁まで正しいと言われています。私はそれをjsに移植し、いくつかのランダムな値を( http://www.efunda.com/math/gamma/findgamma.cfm)に対してクロスチェックしました。

http://jsfiddle.net/Fzy9C/

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は虚数をサポートしていないため、虚数をサポートしていません)

于 2013-03-16T21:13:14.960 に答える
5

ここでの他の回答に代わるものとして、Gergő Nemes によって 2007 年に提案されたガンマ関数のはるかに単純な近似があります。(スターリングの近似に関するウィキペディアのページを参照してください)。

ここに画像の説明を入力

これは、JavaScript で 1 行で直接実装できます。

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);
}
于 2013-03-16T21:45:46.440 に答える
4

これは些細な問題ではありません。ガンマ関数の単純な閉じた式はありません。とはいえ、ニーズに合った数値近似がいくつかあります。

次の回答は、 Lanczos approximationと呼ばれる手法を使用しています。式は次のとおりです。

ここに画像の説明を入力

ここで、g は任意に選択された定数で、近似の精度を制御します。g が大きいほど、近似はより正確になります。g (z) は次のように定義されます。

ここに画像の説明を入力

p nも g に依存する複雑な式で定義されるため、最も難しい部分は A g (z)を見つけることです。

私はウィキペディアのページに 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 で実際に確認できます。

于 2013-03-16T21:23:05.307 に答える
2

整数の計算を修正するために@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;
    }
}     
于 2015-09-11T21:07:27.403 に答える
0

これは私が数年前に書いたバージョンです...少し面倒ですが、テスト済みです:)

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

:-)

于 2015-01-03T22:24:09.253 に答える