BinomialCoefficient.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.numbers.combinatorics;

  18. import org.apache.commons.numbers.core.ArithmeticUtils;

  19. /**
  20.  * Representation of the <a href="http://mathworld.wolfram.com/BinomialCoefficient.html">
  21.  * binomial coefficient</a>.
  22.  * It is "{@code n choose k}", the number of {@code k}-element subsets that
  23.  * can be selected from an {@code n}-element set.
  24.  */
  25. public final class BinomialCoefficient {
  26.     /** The maximum m that can be computed without overflow of a long.
  27.      * {@code C(68, 34) > 2^63}. */
  28.     private static final int MAX_M = 33;
  29.     /** The maximum n that can be computed without intermediate overflow for any m.
  30.      * {@code C(61, 30) * 30 < 2^63}. */
  31.     private static final int SMALL_N = 61;
  32.     /** The maximum n that can be computed without overflow of a long for any m.
  33.      * {@code C(66, 33) < 2^63}. */
  34.     private static final int LIMIT_N = 66;

  35.     /** Private constructor. */
  36.     private BinomialCoefficient() {
  37.         // intentionally empty.
  38.     }

  39.     /**
  40.      * Computes the binomial coefficient.
  41.      *
  42.      * <p>The largest value of {@code n} for which <em>all</em> coefficients can
  43.      * fit into a {@code long} is 66. Larger {@code n} may result in an
  44.      * {@link ArithmeticException} depending on the value of {@code k}.
  45.      *
  46.      * <p>Any {@code min(k, n - k) >= 34} cannot fit into a {@code long}
  47.      * and will result in an {@link ArithmeticException}.
  48.      *
  49.      * @param n Size of the set.
  50.      * @param k Size of the subsets to be counted.
  51.      * @return {@code n choose k}.
  52.      * @throws IllegalArgumentException if {@code n < 0}, {@code k < 0} or {@code k > n}.
  53.      * @throws ArithmeticException if the result is too large to be
  54.      * represented by a {@code long}.
  55.      */
  56.     public static long value(int n, int k) {
  57.         final int m = checkBinomial(n, k);

  58.         if (m == 0) {
  59.             return 1;
  60.         }
  61.         if (m == 1) {
  62.             return n;
  63.         }

  64.         // We use the formulae:
  65.         // (n choose m) = n! / (n-m)! / m!
  66.         // (n choose m) = ((n-m+1)*...*n) / (1*...*m)
  67.         // which can be written
  68.         // (n choose m) = (n-1 choose m-1) * n / m
  69.         long result = 1;
  70.         if (n <= SMALL_N) {
  71.             // For n <= 61, the naive implementation cannot overflow.
  72.             int i = n - m + 1;
  73.             for (int j = 1; j <= m; j++) {
  74.                 result = result * i / j;
  75.                 i++;
  76.             }
  77.         } else if (n <= LIMIT_N) {
  78.             // For n > 61 but n <= 66, the result cannot overflow,
  79.             // but we must take care not to overflow intermediate values.
  80.             int i = n - m + 1;
  81.             for (int j = 1; j <= m; j++) {
  82.                 // We know that (result * i) is divisible by j,
  83.                 // but (result * i) may overflow, so we split j:
  84.                 // Filter out the gcd, d, so j/d and i/d are integer.
  85.                 // result is divisible by (j/d) because (j/d)
  86.                 // is relative prime to (i/d) and is a divisor of
  87.                 // result * (i/d).
  88.                 final long d = ArithmeticUtils.gcd(i, j);
  89.                 result = (result / (j / d)) * (i / d);
  90.                 ++i;
  91.             }
  92.         } else {
  93.             if (m > MAX_M) {
  94.                 throw new ArithmeticException(n + " choose " + k);
  95.             }

  96.             // For n > 66, a result overflow might occur, so we check
  97.             // the multiplication, taking care to not overflow
  98.             // unnecessary.
  99.             int i = n - m + 1;
  100.             for (int j = 1; j <= m; j++) {
  101.                 final long d = ArithmeticUtils.gcd(i, j);
  102.                 result = Math.multiplyExact(result / (j / d), i / d);
  103.                 ++i;
  104.             }
  105.         }

  106.         return result;
  107.     }

  108.     /**
  109.      * Check binomial preconditions.
  110.      *
  111.      * <p>For convenience in implementations this returns the smaller of
  112.      * {@code k} or {@code n - k} allowing symmetry to be exploited in
  113.      * computing the binomial coefficient.
  114.      *
  115.      * @param n Size of the set.
  116.      * @param k Size of the subsets to be counted.
  117.      * @return min(k, n - k)
  118.      * @throws IllegalArgumentException if {@code n < 0}.
  119.      * @throws IllegalArgumentException if {@code k > n} or {@code k < 0}.
  120.      */
  121.     static int checkBinomial(int n,
  122.                              int k) {
  123.         // Combine all checks with a single branch:
  124.         // 0 <= n; 0 <= k <= n
  125.         // Note: If n >= 0 && k >= 0 && n - k < 0 then k > n.
  126.         final int m = n - k;
  127.         // Bitwise or will detect a negative sign bit in any of the numbers
  128.         if ((n | k | m) < 0) {
  129.             // Raise the correct exception
  130.             if (n < 0) {
  131.                 throw new CombinatoricsException(CombinatoricsException.NEGATIVE, n);
  132.             }
  133.             throw new CombinatoricsException(CombinatoricsException.OUT_OF_RANGE, k, 0, n);
  134.         }
  135.         return m < k ? m : k;
  136.     }
  137. }