After hitting a similar problem, I decided to compile the best solutions if have seen and run a simple test on each for a couple different values of n and k. I started with 10 or so functions and weeded out the ones that were just plain wrong or stopped working at specific values. Of all of the solutions, the answer above by user448810 is the cleanest and easiest to implement, I quite like it. Below will be code including each test i ran, the number of times i ean each function for each test, each functions code, the functions output and the time it took to get that output. I only did 20000 runs, there were still fluctuations in the time if i re ran the tests, but you should get a general sense of how well each worked.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//EXPECTED VALUES
//x choose x = 1
//9 choose 4 =126
//52 choose 5 = 2598960;
//64 choose 33 = 1.777090076065542336E18;
//# of runs for each test: 20000
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//https://stackoverflow.com/a/12983878/4285191
public static double combination(long n, long k) {
double sum=0;
for(long i=0;i<k;i++) {
sum+=Math.Log10(n-i);
sum-=Math.Log10(i+1);
}
return Math.Pow(10, sum);
}
/*
10 choose 10
0.9999999999999992
Elapsed=00:00:00.0000015
9 choose 4
126.00000000000001
Elapsed=00:00:00.0000009
52 choose 5
2598959.9999999944
Elapsed=00:00:00.0000013
64 choose 33
1.7770900760655124E+18
Elapsed=00:00:00.0000058
*/
//........................................................
//https://stackoverflow.com/a/19125294/4285191
public static double BinomCoefficient(long n, long k) {
if (k > n) return 0;
if (n == k) return 1; // only one way to chose when n == k
if (k > n-k) k = n-k; // Everything is symmetric around n-k, so it is quicker to iterate over a smaller k than a larger one.
double c = 1;
for (long i = 1; i <= k; i++) {
c *= n--;
c /= i;
}
return c;
}
/*
10 choose 10
1
Elapsed=00:00:00
9 choose 4
126
Elapsed=00:00:00.0000001
52 choose 5
2598960
Elapsed=00:00:00.0000001
64 choose 33
1.7770900760655432E+18
Elapsed=00:00:00.0000006
*/
//........................................................
//https://stackoverflow.com/a/15302448/4285191
public static double choose(long n, long k) {
if (k == 0) return 1;
return (n * choose(n-1, k-1)) / k;
}
/*
10 choose 10
1
Elapsed=00:00:00.0000002
9 choose 4
126
Elapsed=00:00:00.0000003
52 choose 5
2598960
Elapsed=00:00:00.0000004
64 choose 33
1.777090076065543E+18
Elapsed=00:00:00.0000008
*/
//........................................................
//My own version which is just a mix of the two best above.
public static double binomialCoeff(int n, int k) {
if (k > n) return 0;
if (k > n-k) k = n-k; // Everything is symmetric around n-k, so it is quicker to iterate over a smaller k than a larger one.
double recusion(long n, long k) {
if (k == 0) return 1; // only one way to chose when n == k
return (n * recusion(n-1, k-1)) / k;
}
return recusion(n,k);
}
/*
10 choose 10
1
Elapsed=00:00:00
9 choose 4
126
Elapsed=00:00:00.0000001
52 choose 5
2598960
Elapsed=00:00:00.0000002
64 choose 33
1.777090076065543E+18
Elapsed=00:00:00.0000007
*/
//........................................................
//https://en.wikipedia.org/wiki/Binomial_coefficient
public static double binomial(long n, long k) {
// take advantage of symmetry
if (k > n-k) k = n-k;
long c = 1;
for (long i = 1; i <= k; i++, n--) {
// return 0 on potential overflow
if (c/i >= long.MaxValue/n) return 0;
// split c * n / i into (c / i * i) + (c % i * n / i)
c = (c / i * n) + (c % i * n / i);
}
return c;
}
/*
10 choose 10
1
Elapsed=00:00:00.0000006
9 choose 4
126
Elapsed=00:00:00.0000002
52 choose 5
2598960
Elapsed=00:00:00.0000003
64 choose 33
1.7770900760655424E+18
Elapsed=00:00:00.0000029
*/