I wrote a C++ function to calculate the binomial coefficient, trying to avoid overflows as much as possible.
/*!
* @brief Calculates the binomial coefficient indexed by n and k
*
* This implementation is based on the algorithm described here: http://blog.plover.com/math/choose.html
* and the relevant follow-up: http://blog.plover.com/math/choose-2.html
*
* @param n The number of elements
* @param k The number of elements in each subset
* @return The number of k-element subsets in a n-element set
*
* @throw std::overflow_error If the computation caused an overflow
*/
uint64_t binomial_coefficient(uint64_t n, uint64_t k)
{
if (k > n)
return 0;
if (n - k < k)
k = n - k;
uint64_t r = 1;
for (uint64_t d = 1; d <= k; d++)
{
uint64_t mult = n;
bool divided = true;
if (mult % d == 0)
mult /= d;
else if (r % d == 0)
r /= d;
else
divided = false;
const uint64_t r_mult = r * mult;
if (r_mult / mult != r)
throw std::overflow_error("Overflow");
r = r_mult;
if (!divided)
r /= d;
n--;
}
return r;
}
I have implemented the algorithm described here with a slight modification (try to divide n
or r
by d
before multiplying n
by r
) as described in the article follow-up.
I also added an overflow check, which I believe should be reliable, as unsigned arithmetic overflow should not yield undefined behaviour.
Do you see any blatant or subtle bug in my implementation, and/or any room for improvement (besides lookup tables for common values and alike, which I am not willing to pursue at the moment)?