【问题标题】:How to make a function that computes the factorial for numbers with decimals?如何制作一个计算小数的阶乘的函数?
【发布时间】:2013-03-16 20:10:48
【问题描述】:

如何在 JavaScript 中创建一个计算十进制数的阶乘(或 gamma function)的函数?比如2.33!怎么计算?

【问题讨论】:

  • 你试过什么?至少有一个可能的重复:stackoverflow.com/questions/3959211/…
  • function fact(n) { var x = 1 for (var i = 2; i
  • 我发现之前和一个答案,但它并不完全正确
  • 你是对的@Mich,投票赞成。
  • 带有常量的数组似乎不正确

标签: javascript math factorial gamma-function


【解决方案1】:

我可能已经找到了一个现有的解决方案... 这是 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 不支持)

【讨论】:

    【解决方案2】:

    作为此处其他答案的替代方案,这里有一个更简单的伽玛函数近似值,由 Gergő Nemes 在 2007 年提出。 (请参阅Stirling's approximation 上的维基百科页面)。

    这可以直接在 JavaScript 中用一行代码实现:

    function gamma(z) {
      return Math.sqrt(2 * Math.PI / z) * Math.pow((1 / Math.E) * (z + 1 / (12 * z - 1 / (10 * z))), z);
    }
    

    你可以see this in action on jsFiddle.

    这对于 z > 8 精确到 8 位,但对于较小的 z 仍然精确到少数数字。它不如 Lanczos 近似准确,但它更简单,而且还 slightly faster

    请注意,伽玛函数和阶乘函数略有不同。阶乘函数可以用伽马函数来定义:

    function factorial(n) {
      return gamma(n + 1);
    }
    

    【讨论】:

    【解决方案3】:

    这不是一个小问题。 gamma function 没有简单的封闭式公式。也就是说,有一些数值近似值应该适合您的需求。

    以下答案将使用一种称为Lanczos approximation 的技术。公式如下:

    其中 g 是一个任意选择的常数,用于控制近似值的准确性。对于较大的 g,近似值会更准确。 Ag(z) 定义如下:

    最难的部分是找到 Ag(z),因为 pn is also defined with a complicated formula dependent on g

    我不能对下面的代码有太多的功劳,因为我只是在维基百科页面上编写 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);
    }
    

    你可以see this in action on jsFiddle

    【讨论】:

      【解决方案4】:

      只是为了完成@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;
          }
      }     
      

      【讨论】:

        【解决方案5】:

        这是我几年前写的一个版本……有点乱但经过测试:)

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

        :-)

        【讨论】:

          【解决方案6】:

          如果您只是在寻找计算实数阶乘的函数,那么您只需要来自 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;
          }
          

          适用于除小数之外的负数。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2023-03-22
            • 2015-04-19
            • 2019-05-22
            • 2015-01-08
            • 1970-01-01
            • 2014-03-24
            • 1970-01-01
            相关资源
            最近更新 更多