I need to calculate n!/(n-r)!r!
in C#. It's easy to calculate using the factorial function for small numbers but when the number gets bigger like 100, it doesn't work. Is there any other way with which we can calculate combinations for larger numbers?
First off, I note that you are attempting to calculate the binomial coefficient, so let's call it that.
Here are a number of ways to do the calculation. If you use BigInteger you do not have to worry about overflow:
Method one: use factorial:
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));
}
Method two: use recursion:
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)
}
This method however is not fast unless you memoize the result.
Method Three: be more clever about minimizing the number of multiplications, and dividing early. This keeps the numbers small:
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;
}
So for example if you were computing (6 C 3), instead of computing (6 x 5 x 4 x 3 x 2 x 1) / ( (3 x 2 x 1) x (3 x 2 x 1)), you compute (((4 / 1) * 5) / 2) * 6) / 3, which keeps the numbers small if possible.
Following what Eric said, dividing early helps a great deal, you can improve that by dividing from high to low. This way you can calculate any result as long as the endresult fits in a Long. Here's the code I use (apologize for the Java, but converting should be easy) :
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With