PascalDistribution.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.  *      https://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. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.distribution.discrete;

  22. import org.hipparchus.exception.LocalizedCoreFormats;
  23. import org.hipparchus.exception.MathIllegalArgumentException;
  24. import org.hipparchus.special.Beta;
  25. import org.hipparchus.util.CombinatoricsUtils;
  26. import org.hipparchus.util.FastMath;
  27. import org.hipparchus.util.MathUtils;

  28. /**
  29.  * Implementation of the Pascal distribution.
  30.  * <p>
  31.  * The Pascal distribution is a special case of the Negative Binomial distribution
  32.  * where the number of successes parameter is an integer.
  33.  * <p>
  34.  * There are various ways to express the probability mass and distribution
  35.  * functions for the Pascal distribution. The present implementation represents
  36.  * the distribution of the number of failures before {@code r} successes occur.
  37.  * This is the convention adopted in e.g.
  38.  * <a href="http://mathworld.wolfram.com/NegativeBinomialDistribution.html">MathWorld</a>,
  39.  * but <em>not</em> in
  40.  * <a href="http://en.wikipedia.org/wiki/Negative_binomial_distribution">Wikipedia</a>.
  41.  * <p>
  42.  * For a random variable {@code X} whose values are distributed according to this
  43.  * distribution, the probability mass function is given by<br>
  44.  * {@code P(X = k) = C(k + r - 1, r - 1) * p^r * (1 - p)^k,}<br>
  45.  * where {@code r} is the number of successes, {@code p} is the probability of
  46.  * success, and {@code X} is the total number of failures. {@code C(n, k)} is
  47.  * the binomial coefficient ({@code n} choose {@code k}). The mean and variance
  48.  * of {@code X} are<br>
  49.  * {@code E(X) = (1 - p) * r / p, var(X) = (1 - p) * r / p^2.}<br>
  50.  * Finally, the cumulative distribution function is given by<br>
  51.  * {@code P(X <= k) = I(p, r, k + 1)},
  52.  * where I is the regularized incomplete Beta function.
  53.  *
  54.  * @see <a href="http://en.wikipedia.org/wiki/Negative_binomial_distribution">
  55.  * Negative binomial distribution (Wikipedia)</a>
  56.  * @see <a href="http://mathworld.wolfram.com/NegativeBinomialDistribution.html">
  57.  * Negative binomial distribution (MathWorld)</a>
  58.  */
  59. public class PascalDistribution extends AbstractIntegerDistribution {
  60.     /** Serializable version identifier. */
  61.     private static final long serialVersionUID = 20160320L;
  62.     /** The number of successes. */
  63.     private final int numberOfSuccesses;
  64.     /** The probability of success. */
  65.     private final double probabilityOfSuccess;
  66.     /** The value of {@code log(p)}, where {@code p} is the probability of success,
  67.      * stored for faster computation. */
  68.     private final double logProbabilityOfSuccess;
  69.     /** The value of {@code log(1-p)}, where {@code p} is the probability of success,
  70.      * stored for faster computation. */
  71.     private final double log1mProbabilityOfSuccess;

  72.     /**
  73.      * Create a Pascal distribution with the given number of successes and
  74.      * probability of success.
  75.      *
  76.      * @param r Number of successes.
  77.      * @param p Probability of success.
  78.      * @throws MathIllegalArgumentException if the number of successes is not positive
  79.      * @throws MathIllegalArgumentException if the probability of success is not in the
  80.      * range {@code [0, 1]}.
  81.      */
  82.     public PascalDistribution(int r, double p)
  83.         throws MathIllegalArgumentException {
  84.         if (r <= 0) {
  85.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_OF_SUCCESSES,
  86.                                                    r);
  87.         }

  88.         MathUtils.checkRangeInclusive(p, 0, 1);

  89.         numberOfSuccesses = r;
  90.         probabilityOfSuccess = p;
  91.         logProbabilityOfSuccess = FastMath.log(p);
  92.         log1mProbabilityOfSuccess = FastMath.log1p(-p);
  93.     }

  94.     /**
  95.      * Access the number of successes for this distribution.
  96.      *
  97.      * @return the number of successes.
  98.      */
  99.     public int getNumberOfSuccesses() {
  100.         return numberOfSuccesses;
  101.     }

  102.     /**
  103.      * Access the probability of success for this distribution.
  104.      *
  105.      * @return the probability of success.
  106.      */
  107.     public double getProbabilityOfSuccess() {
  108.         return probabilityOfSuccess;
  109.     }

  110.     /** {@inheritDoc} */
  111.     @Override
  112.     public double probability(int x) {
  113.         double ret;
  114.         if (x < 0) {
  115.             ret = 0.0;
  116.         } else {
  117.             ret = CombinatoricsUtils.binomialCoefficientDouble(x +
  118.                   numberOfSuccesses - 1, numberOfSuccesses - 1) *
  119.                   FastMath.pow(probabilityOfSuccess, numberOfSuccesses) *
  120.                   FastMath.pow(1.0 - probabilityOfSuccess, x);
  121.         }
  122.         return ret;
  123.     }

  124.     /** {@inheritDoc} */
  125.     @Override
  126.     public double logProbability(int x) {
  127.         double ret;
  128.         if (x < 0) {
  129.             ret = Double.NEGATIVE_INFINITY;
  130.         } else {
  131.             ret = CombinatoricsUtils.binomialCoefficientLog(x +
  132.                   numberOfSuccesses - 1, numberOfSuccesses - 1) +
  133.                   logProbabilityOfSuccess * numberOfSuccesses +
  134.                   log1mProbabilityOfSuccess * x;
  135.         }
  136.         return ret;
  137.     }

  138.     /** {@inheritDoc} */
  139.     @Override
  140.     public double cumulativeProbability(int x) {
  141.         double ret;
  142.         if (x < 0) {
  143.             ret = 0.0;
  144.         } else {
  145.             ret = Beta.regularizedBeta(probabilityOfSuccess,
  146.                     numberOfSuccesses, x + 1.0);
  147.         }
  148.         return ret;
  149.     }

  150.     /**
  151.      * {@inheritDoc}
  152.      *
  153.      * For number of successes {@code r} and probability of success {@code p},
  154.      * the mean is {@code r * (1 - p) / p}.
  155.      */
  156.     @Override
  157.     public double getNumericalMean() {
  158.         final double p = getProbabilityOfSuccess();
  159.         final double r = getNumberOfSuccesses();
  160.         return (r * (1 - p)) / p;
  161.     }

  162.     /**
  163.      * {@inheritDoc}
  164.      *
  165.      * For number of successes {@code r} and probability of success {@code p},
  166.      * the variance is {@code r * (1 - p) / p^2}.
  167.      */
  168.     @Override
  169.     public double getNumericalVariance() {
  170.         final double p = getProbabilityOfSuccess();
  171.         final double r = getNumberOfSuccesses();
  172.         return r * (1 - p) / (p * p);
  173.     }

  174.     /**
  175.      * {@inheritDoc}
  176.      *
  177.      * The lower bound of the support is always 0 no matter the parameters.
  178.      *
  179.      * @return lower bound of the support (always 0)
  180.      */
  181.     @Override
  182.     public int getSupportLowerBound() {
  183.         return 0;
  184.     }

  185.     /**
  186.      * {@inheritDoc}
  187.      *
  188.      * The upper bound of the support is always positive infinity no matter the
  189.      * parameters. Positive infinity is symbolized by {@code Integer.MAX_VALUE}.
  190.      *
  191.      * @return upper bound of the support (always {@code Integer.MAX_VALUE}
  192.      * for positive infinity)
  193.      */
  194.     @Override
  195.     public int getSupportUpperBound() {
  196.         return Integer.MAX_VALUE;
  197.     }

  198.     /**
  199.      * {@inheritDoc}
  200.      *
  201.      * The support of this distribution is connected.
  202.      *
  203.      * @return {@code true}
  204.      */
  205.     @Override
  206.     public boolean isSupportConnected() {
  207.         return true;
  208.     }
  209. }