如何计算大数的二项式系数

Jay*_*Jay 4 c# math binomial-coefficients

我需要n!/(n-r)!r!用C#来计算.对于小数字,使用阶乘函数很容易计算,但是当数字变得像100那样大时,它就不起作用了.有没有其他方法可以计算更大数字的组合?

Eri*_*ert 17

首先,我注意到你正在尝试计算二项式系数,所以我们称之为.

以下是一些进行计算的方法.如果你使用BigInteger,你不必担心溢出:

方法一:使用阶乘:

static BigInteger Factorial(BigInteger n)
{
    BigInteger f = 1;
    for (BigInteger i = 2; i <= n; ++i)
        f = f * i;
    return f;
}

static BigInteger BinomialCoefficient(BigInteger n, BigInteger k)
{
    return Factorial(n) / (Factorial(n-k) * Factorial(k));
}
Run Code Online (Sandbox Code Playgroud)

方法二:使用递归:

static BigInteger BinomialCoefficient(BigInteger n, BigInteger k)
{
    if (n == 0) return 1;
    if (k == 0) return 0;
    return BinomialCoefficient(n-1, k-1) + BinomialCoefficient(n-1, k)
}
Run Code Online (Sandbox Code Playgroud)

但是,除非您记住结果,否则此方法并不快.

方法三:更加巧妙地减少乘法次数,并尽早划分.这使数字变小:

static BigInteger BinomialCoefficient(BigInteger n, BigInteger k)
{
    // (n C k) and (n C (n-k)) are the same, so pick the smaller as k:
    if (k > n - k) k = n - k;
    BigInteger result = 1;
    for (BigInteger i = 1; i <= k; ++i)
    {
        result *= n - k + i;
        result /= i;
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

因此,例如,如果你是计算(6 C 3),而不是计算(6 x 5 x 4 x 3 x 2 x 1)/((3 x 2 x 1)x(3 x 2 x 1)),你计算(((4/1)*5)/ 2)*6)/ 3,尽可能保持数字小.

  • @CodeInChaos:当你这么想时,很明显:假设我们有`(((n)/ 1)*(n + 1))/ 2`.显然,n或n + 1可被2整除.假设我们有`(((((n)/ 1)*(n + 1))/ 2)*(n + 2)/ 3` - 我们已经处理了2.显然是n,n之一+1或n + 2可被3整除.依此类推.每次,我们除以*必须*作为当前或前一项中的因子出现的值. (3认同)

Jer*_*ens 5

按照埃里克(Eric)所说,尽早分配对您有很大帮助,您可以通过从高分到低分来改善这一点。这样,只要最终结果适合Long,就可以计算任何结果。这是我使用的代码(对Java表示歉意,但转换应该很容易):

public static long binomialCoefficient(int n, int k) {
   // take the lowest possible k to reduce computing using: n over k = n over (n-k)
   k = java.lang.Math.min( k, n - k );

   // holds the high number: fi. (1000 over 990) holds 991..1000
   long highnumber[] = new long[k];
   for (int i = 0; i < k; i++)
      highnumber[i] = n - i; // the high number first order is important
   // holds the dividers: fi. (1000 over 990) holds 2..10
   int dividers[] = new int[k - 1];
   for (int i = 0; i < k - 1; i++)
      dividers[i] = k - i;

   // for every divider there always exists a highnumber that can be divided by 
   // this, the number of highnumbers being a sequence that equals the number of 
   // dividers. Thus, the only trick needed is to divide in reverse order, so 
   // divide the highest divider first trying it on the highest highnumber first. 
   // That way you do not need to do any tricks with primes.
   for (int divider: dividers) 
      for (int i = 0; i < k; i++)
         if (highnumber[i] % divider == 0) {
            highnumber[i] /= divider;
            break;
         }

   // multiply remainder of highnumbers
   long result = 1;
   for (long high : highnumber)
      result *= high;
   return result;
}
Run Code Online (Sandbox Code Playgroud)