LogBinomialCoefficient.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.gamma.LogBeta;

  19. /**
  20.  * Natural logarithm 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 LogBinomialCoefficient {
  26.     /** The maximum n that can be computed without overflow of a long for any m.
  27.      * {@code C(66, 33) < 2^63}. */
  28.     private static final int LIMIT_N_LONG = 66;
  29.     /** The maximum n that can be computed without overflow of a double for an m.
  30.      * C(1029, 514) ~ 1.43e308. */
  31.     private static final int LIMIT_N_DOUBLE = 1029;
  32.     /** The maximum m that can be computed without overflow of a double for any n.
  33.      * C(2147483647, 37) ~ 1.39e302. */
  34.     private static final int LIMIT_M_DOUBLE = 37;

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

  39.     /**
  40.      * Computes the logarithm of the binomial coefficient.
  41.      *
  42.      * <p>This returns a finite result for any valid {@code n choose k}.
  43.      *
  44.      * @param n Size of the set.
  45.      * @param k Size of the subsets to be counted.
  46.      * @return {@code log(n choose k)}.
  47.      * @throws IllegalArgumentException if {@code n < 0}, {@code k < 0} or {@code k > n}.
  48.      */
  49.     public static double value(int n, int k) {
  50.         final int m = BinomialCoefficient.checkBinomial(n, k);

  51.         if (m == 0) {
  52.             return 0;
  53.         }
  54.         if (m == 1) {
  55.             return Math.log(n);
  56.         }

  57.         if (n <= LIMIT_N_LONG) {
  58.             // Delegate to the exact long result
  59.             return Math.log(BinomialCoefficient.value(n, k));
  60.         }
  61.         if (n <= LIMIT_N_DOUBLE || m <= LIMIT_M_DOUBLE) {
  62.             // Delegate to the double result
  63.             return Math.log(BinomialCoefficientDouble.value(n, k));
  64.         }

  65.         //    n!                gamma(n+1)               gamma(k+1) * gamma(n-k+1)
  66.         // ---------   = ------------------------- = 1 / -------------------------
  67.         // k! (n-k)!     gamma(k+1) * gamma(n-k+1)              gamma(n+1)
  68.         //
  69.         //
  70.         //             = 1 / (k * beta(k, n-k+1))
  71.         //
  72.         // where: beta(a, b) = gamma(a) * gamma(b) / gamma(a+b)

  73.         // Delegate to LogBeta
  74.         return -Math.log(m) - LogBeta.value(m, n - m + 1);
  75.     }
  76. }