@stdlib/math-base-special-binomcoef
Version:
Compute the binomial coefficient.
157 lines (145 loc) • 3.56 kB
JavaScript
/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
;
// MODULES //
var MAX_SAFE_INTEGER = require( '@stdlib/constants-float64-max-safe-integer' );
var PINF = require( '@stdlib/constants-float64-pinf' );
var isInteger = require( '@stdlib/math-base-assert-is-integer' );
var isnan = require( '@stdlib/math-base-assert-is-nan' );
var isOdd = require( '@stdlib/math-base-assert-is-odd' );
var floor = require( '@stdlib/math-base-special-floor' );
var gcd = require( '@stdlib/math-base-special-gcd' );
// MAIN //
/**
* Computes the binomial coefficient of two integers.
*
* @param {integer} n - input value
* @param {integer} k - second input value
* @returns {integer} function value
*
* @example
* var v = binomcoef( 8, 2 );
* // returns 28
*
* @example
* var v = binomcoef( 0, 0 );
* // returns 1
*
* @example
* var v = binomcoef( -4, 2 );
* // returns 10
*
* @example
* var v = binomcoef( NaN, 3 );
* // returns NaN
*
* @example
* var v = binomcoef( 5, NaN );
* // returns NaN
*
* @example
* var v = binomcoef( NaN, NaN );
* // returns NaN
*/
function binomcoef( n, k ) {
var res;
var sgn;
var b;
var c;
var d;
var g;
var s;
if ( isnan( n ) || isnan( k ) ) {
return NaN;
}
if ( !isInteger( n ) || !isInteger( k ) ) {
return NaN;
}
if ( k < 0 ) {
return 0;
}
sgn = 1;
if ( n < 0 ) {
n = -n + k - 1;
if ( isOdd( k ) ) {
sgn *= -1;
}
}
if ( k > n ) {
return 0;
}
if ( k === 0 || k === n ) {
return sgn;
}
if ( k === 1 || k === n - 1 ) {
return sgn * n;
}
// Minimize the number of computed terms by leveraging symmetry:
if ( n - k < k ) {
k = n - k;
}
s = floor( MAX_SAFE_INTEGER / n );
// Use a standard algorithm for computing the binomial coefficient (e.g., see Knuth's "The Art of Computer Programming, 3rd Edition, Volume 2: Seminumerical Algorithms")...
res = 1;
for ( d = 1; d <= k; d++ ) {
// Check for potential overflow...
if ( res > s ) {
break;
}
res *= n;
res /= d;
n -= 1;
}
// If we did not early exit from the previous loop, the answer is exact, and we can simply return...
if ( d > k ) {
return sgn * res;
}
/*
* Let `N` equal the provided `n`.
*
* We want to calculate C(N,k), and, at this point, we have calculated
*
* res = C(N,n) = C(N,N-n) = C(N,d-1)
*
* where `N-n = d-1` and, hence, `n = N - d + 1`.
*
* Given the following identity,
*
* C(N,k) = C(N,d-1) * C(N-d+1,k-d+1) / C(k,k-d+1)
* = C(N,d-1) * C(n,k-d+1) / C(k,k-d+1)
*
* we can leverage recursion to perform argument reduction.
*/
b = binomcoef( n, k-d+1 );
if ( b === PINF ) {
return sgn * b;
}
c = binomcoef( k, k-d+1 );
/*
* At this point, the result should be `res*b/c`.
*
* To help guard against overflow and precision loss, we calculate the greatest common divisor (gcd). In this case, we pick `b`, as `b` should be less than `res` in most (if not all) cases.
*/
g = gcd( b, c );
b /= g;
c /= g;
res /= c;
return sgn * res * b;
}
// EXPORTS //
module.exports = binomcoef;