View Raw SPL
/*****************************************************************************
* *
* BINCOEFF.SPL Copyright (C) 2014 DSP Development Corporation *
* All Rights Reserved *
* *
* Author: Randy Race *
* *
* Synopsis: Calculates the binomial coefficient *
* *
* Revisions: 22 Jul 2014 RRR Creation *
* *
*****************************************************************************/
#if @HELP_BINCOEFF
BINCOEFF
Purpose: Calculates the generalized binomial coefficient.
Syntax: BINCOEFF(n, k)
n - A scalar or series.
k - A scalar or series.
Returns: A scalar or series. For positive integer values, the number
of combinations of n items taken in unordered groups of k.
Example:
bincoeff(5, 3)
returns 10
Example:
bincoeff(5.3, 3)
returns 12.5345
Example:
c = bincoeff(1..5, 3)
c == {0, 0, 1, 4, 10}
Example:
c = bincoeff(3..7, 1..5)
c == {3, 6, 10, 15, 21}
Example:
c = bincoeff(3..0.5..7, 1..0.5..5)
c == {3, 4.375, 6, 7.875, 10, 12.375, 15, 17.875, 21}
Remarks:
BINCOEFF returns the generalized binomial coefficient. The
inputs can be any series or scalar values.
For integer values where 0 <= k <= n:
n n!
bincoeff(n, k) = ( ) = -----------
k k! (n - k)!
The result is accurate to 15 digits. A warning is
displayed if the computation results in an overflow.
See NCHOOSEK to return the actual combinations of series
elements for a given input series.
See Also:
Factorial
Gamma
Nchoosek
Pascal
#endif
/* binomial coefficient, n choose k */
bincoeff(n, k)
{
local c, overflow;
if (argc < 2)
{
if (argc < 1)
{
n = 1;
}
k = 1;
}
/* core computation */
(c, overflow) = bincoeff_core(n, k);
if (outargc > 1)
{
return(c, any(overflow));
}
else
{
if (any(overflow))
{
message("Warning", "bincoeff - possible overflow, result only accurate to 15 digits", 8);
}
return(c);
}
}
/* binomial coeffcient core routine */
ITERATE bincoeff_core(n, k)
{
local c, ni, ki, idx, nlz, ngz, ibin, intn, intk, overflow = 0;
/* sort out sizes */
if (isarray(n) && not(isarray(k)))
{
k = ones(length(n), 1, deltax(n)) * k;
}
else if (isarray(k) && not(isarray(n)))
{
n = ones(length(k), 1, deltax(k)) * n;
}
if (length(n) != length(k))
{
error("bincoeff - inputs must be of conforming size");
}
/* initialize to zeros of same length */
c = n * 0;
/* integer values */
intn = floor(n) == real(n);
intk = floor(k) == real(k);
/* n < 0 */
nlz = n < 0;
if (any(nlz))
{
/* n < 0, non-integer n */
idx = find(nlz && not(intn));
ni = n[idx];
ki = k[idx];
c[idx] = bincoeff_bico(ni, ki);
/* n < 0, integer n, integer k, k >= 0 */
idx = find(nlz && intn && intk && k >= 0);
ni = n[idx];
ki = k[idx];
c[idx] = (-1)^(ki) * bincoeff_bico(-ni + ki - 1, ki);
/* n < 0, k <= n, integer n, integer k */
idx = find(nlz && intn && intk && k <= n);
ni = n[idx];
ki = k[idx];
c[idx] = (-1)^(ni - ki) * bincoeff_bico(-ki - 1, ni - ki);
/* n < 0, integer n, non-integer k */
idx = find(nlz && intn && not(intk));
c[idx] = inf;
}
ngz = n >= 0;
if (any(ngz))
{
/* use integer binomial here */
ibin = intn && intk && (k >= 0) && (k <= n);
/* n > 0, k > 0 && k < n && real integer */
idx = find(ngz && ibin);
ni = n[idx];
ki = k[idx];
/* positive integer binomial coefficient */
c[idx] = binomial(ni, ki);
/* n > 0 and k anything */
idx = find(ngz && not(ibin));
ni = n[idx];
ki = k[idx];
c[idx] = bincoeff_bico(ni, ki);
}
/* nan */
idx = find(isnan(n) || isnan(k));
c[idx] = nan;
/* fast check if all values real */
if (isreal(n) && isreal(k))
{
c = real(c);
}
else
{
/* some values might be real */
idx = find(imag(n) == 0 && imag(k) == 0);
c[idx] = real(c);
}
/* handle integer results */
idx = find(intn && intk);
c[idx] = bincoeff_round(c[idx]);
/* check for overflow */
if (any(c[idx] > 1e15))
{
overflow = 1;
}
return(c, overflow);
}
/* binomial coefficient using gammaln */
bincoeff_bico(n, k)
{
return(exp(gammaln(n + 1) - gammaln(k + 1) - gammaln(n - k + 1)));
}
/* rounding that handles large numbers */
bincoeff_round(x)
{
return(floor(x) == x ? x : floor(x + 0.5));
}