I need a way of calculating combinations without running out of memory. Here\'s what i have so far.
public static long combination(long n, long k) // nCk
{
Looking at your code, it is no wonder that you will run out of memory quite fast. Your method divideFactorials
calls the method factorial and uses as argument the difference "numerator-denominator". That difference is very likely going to be very large according to your code and you will be stuck in a very long loop in your factorial method.
If it is really just about finding nCk (which I assume because your comment in your code), just use:
public static long GetnCk(long n, long k)
{
long bufferNum = 1;
long bufferDenom = 1;
for(long i = n; i > Math.Abs(n-k); i--)
{
bufferNum *= i;
}
for(long i = k; i => 1; i--)
{
bufferDenom *= i;
}
return (long)(bufferNom/bufferDenom);
}
Of course using this method you will run out of range very fast, because a long does not actually support very long numbers, so n and k have to be smaller than 20.
Supposing that you actually work with very large numbers you could use doubles instead of longs, as the powers become more and more significant.
Edit: If you use large numbers you could also use Stirling's Formula:
As n becomes large ln(n!) -> n*ln(n) - n.
Putting this into code:
public static double GetnCk(long n, long k)
{
double buffern = n*Math.Log(n) - n;
double bufferk = k*Math.Log(k) - k;
double bufferkn = Math.Abs(n-k)*Math.Log(Math.Abs(n-k)) - Math.Abs(n-k);
return Math.Exp(buffern)/(Math.Exp(bufferk)*Math.Exp(bufferkn));
}
I only propose this answer, as you said language independent, the C# code is just used to demonstrate it. Since you need to use large numbers for n and k for this to work, i propose this as a general way for finding the binomial coefficient for large combinations.
For cases were n and k are both smaller than around 200-300, you should use the answer Victor Mukherjee proposed, as it is exact.
Edit2: Edited my first code.
Here is a solution which is very similar to Bob Byran, but checks two more preconditions to speed up the code.
/// <summary>
/// Calculates the binomial coefficient (nCk) (N items, choose k)
/// </summary>
/// <param name="n">the number items</param>
/// <param name="k">the number to choose</param>
/// <returns>the binomial coefficient</returns>
public static long 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.
long c = 1;
for (long i = 1; i <= k; i++)
{
c *= n--;
c /= i;
}
return c;
}
Just for the sake of completion: the standard C
math library has implementations of both Γ and lnΓ (called tgamma
and lgamma
), where
Γ(n) = (n-1)!
The library computation is certainly faster and more accurate than summing logarithms. For a lot more information, see Wikipedia and Mathworld.
public static long 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 (long)Math.pow(10, sum);
}
One of the best methods for calculating the binomial coefficient I have seen suggested is by Mark Dominus. It is much less likely to overflow with larger values for N and K than some other methods.
public static long GetBinCoeff(long N, long K)
{
// This function gets the total number of unique combinations based upon N and K.
// N is the total number of items.
// K is the size of the group.
// Total number of unique combinations = N! / ( K! (N - K)! ).
// This function is less efficient, but is more likely to not overflow when N and K are large.
// Taken from: http://blog.plover.com/math/choose.html
//
long r = 1;
long d;
if (K > N) return 0;
for (d = 1; d <= K; d++)
{
r *= N--;
r /= d;
}
return r;
}