BinomialCoefficientDouble.java
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
- package org.apache.commons.numbers.combinatorics;
- /**
- * Representation of the <a href="http://mathworld.wolfram.com/BinomialCoefficient.html">
- * binomial coefficient</a>, as a {@code double}.
- * It is "{@code n choose k}", the number of {@code k}-element subsets that
- * can be selected from an {@code n}-element set.
- */
- public final class BinomialCoefficientDouble {
- /** The maximum factorial that can be represented as a double. */
- private static final int MAX_FACTORIAL = 170;
- /** The maximum n that can be computed without overflow of a long for any m.
- * {@code C(66, 33) < 2^63}. */
- private static final int LIMIT_N_LONG = 66;
- /** The maximum m that can be computed without overflow of a double.
- * C(1030, 515) ~ 2.85e308. */
- private static final int MAX_M = 514;
- /** The maximum n that can be computed without intermediate overflow for any m.
- * C(1020, 510) * 510 ~ 1.43e308. */
- private static final int SMALL_N = 1020;
- /** The maximum m that can be computed without intermediate overflow for any n.
- * C(2147483647, 37) * 37 ~ 5.13e303. */
- private static final int SMALL_M = 37;
- /** Private constructor. */
- private BinomialCoefficientDouble() {
- // intentionally empty.
- }
- /**
- * Computes the binomial coefficient.
- *
- * <p>The largest value of {@code n} for which <em>all</em> coefficients can
- * fit into a {@code double} is 1029. Larger {@code n} may result in
- * infinity depending on the value of {@code k}.
- *
- * <p>Any {@code min(k, n - k) >= 515} cannot fit into a {@code double}
- * and will result in infinity.
- *
- * @param n Size of the set.
- * @param k Size of the subsets to be counted.
- * @return {@code n choose k}.
- * @throws IllegalArgumentException if {@code n < 0}, {@code k < 0} or {@code k > n}.
- */
- public static double value(int n, int k) {
- if (n <= LIMIT_N_LONG) {
- // Delegate to the exact long result
- return BinomialCoefficient.value(n, k);
- }
- final int m = BinomialCoefficient.checkBinomial(n, k);
- if (m == 0) {
- return 1;
- }
- if (m == 1) {
- return n;
- }
- double result;
- if (n <= MAX_FACTORIAL) {
- // Small factorials are tabulated exactly
- // n! / m! / (n-m)!
- result = Factorial.uncheckedFactorial(n) /
- Factorial.uncheckedFactorial(m) /
- Factorial.uncheckedFactorial(n - m);
- } else {
- // Compute recursively using:
- // (n choose m) = (n-1 choose m-1) * n / m
- if (n <= SMALL_N || m <= SMALL_M) {
- // No overflow possible
- result = 1;
- for (int i = 1; i <= m; i++) {
- result *= n - m + i;
- result /= i;
- }
- } else {
- if (m > MAX_M) {
- return Double.POSITIVE_INFINITY;
- }
- // Compute the initial part without overflow checks
- result = 1;
- for (int i = 1; i <= SMALL_M; i++) {
- result *= n - m + i;
- result /= i;
- }
- // Careful of overflow
- for (int i = SMALL_M + 1; i <= m; i++) {
- final double next = result * (n - m + i);
- if (next > Double.MAX_VALUE) {
- // Reverse order of terms
- result /= i;
- result *= n - m + i;
- if (result > Double.MAX_VALUE) {
- // Definite overflow
- return Double.POSITIVE_INFINITY;
- }
- } else {
- result = next / i;
- }
- }
- }
- }
- return Math.floor(result + 0.5);
- }
- }