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.  *      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.math3.distribution;

  18. import org.apache.commons.math3.exception.NotStrictlyPositiveException;
  19. import org.apache.commons.math3.exception.OutOfRangeException;
  20. import org.apache.commons.math3.exception.util.LocalizedFormats;
  21. import org.apache.commons.math3.random.RandomGenerator;
  22. import org.apache.commons.math3.random.Well19937c;
  23. import org.apache.commons.math3.special.Beta;
  24. import org.apache.commons.math3.util.CombinatoricsUtils;
  25. import org.apache.commons.math3.util.FastMath;

  26. /**
  27.  * <p>
  28.  * Implementation of the Pascal distribution. The Pascal distribution is a
  29.  * special case of the Negative Binomial distribution where the number of
  30.  * successes parameter is an integer.
  31.  * </p>
  32.  * <p>
  33.  * There are various ways to express the probability mass and distribution
  34.  * functions for the Pascal distribution. The present implementation represents
  35.  * the distribution of the number of failures before {@code r} successes occur.
  36.  * This is the convention adopted in e.g.
  37.  * <a href="http://mathworld.wolfram.com/NegativeBinomialDistribution.html">MathWorld</a>,
  38.  * but <em>not</em> in
  39.  * <a href="http://en.wikipedia.org/wiki/Negative_binomial_distribution">Wikipedia</a>.
  40.  * </p>
  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.  * </p>
  54.  *
  55.  * @see <a href="http://en.wikipedia.org/wiki/Negative_binomial_distribution">
  56.  * Negative binomial distribution (Wikipedia)</a>
  57.  * @see <a href="http://mathworld.wolfram.com/NegativeBinomialDistribution.html">
  58.  * Negative binomial distribution (MathWorld)</a>
  59.  * @since 1.2 (changed to concrete class in 3.0)
  60.  */
  61. public class PascalDistribution extends AbstractIntegerDistribution {
  62.     /** Serializable version identifier. */
  63.     private static final long serialVersionUID = 6751309484392813623L;
  64.     /** The number of successes. */
  65.     private final int numberOfSuccesses;
  66.     /** The probability of success. */
  67.     private final double probabilityOfSuccess;
  68.     /** The value of {@code log(p)}, where {@code p} is the probability of success,
  69.      * stored for faster computation. */
  70.     private final double logProbabilityOfSuccess;
  71.     /** The value of {@code log(1-p)}, where {@code p} is the probability of success,
  72.      * stored for faster computation. */
  73.     private final double log1mProbabilityOfSuccess;

  74.     /**
  75.      * Create a Pascal distribution with the given number of successes and
  76.      * probability of success.
  77.      * <p>
  78.      * <b>Note:</b> this constructor will implicitly create an instance of
  79.      * {@link Well19937c} as random generator to be used for sampling only (see
  80.      * {@link #sample()} and {@link #sample(int)}). In case no sampling is
  81.      * needed for the created distribution, it is advised to pass {@code null}
  82.      * as random generator via the appropriate constructors to avoid the
  83.      * additional initialisation overhead.
  84.      *
  85.      * @param r Number of successes.
  86.      * @param p Probability of success.
  87.      * @throws NotStrictlyPositiveException if the number of successes is not positive
  88.      * @throws OutOfRangeException if the probability of success is not in the
  89.      * range {@code [0, 1]}.
  90.      */
  91.     public PascalDistribution(int r, double p)
  92.         throws NotStrictlyPositiveException, OutOfRangeException {
  93.         this(new Well19937c(), r, p);
  94.     }

  95.     /**
  96.      * Create a Pascal distribution with the given number of successes and
  97.      * probability of success.
  98.      *
  99.      * @param rng Random number generator.
  100.      * @param r Number of successes.
  101.      * @param p Probability of success.
  102.      * @throws NotStrictlyPositiveException if the number of successes is not positive
  103.      * @throws OutOfRangeException if the probability of success is not in the
  104.      * range {@code [0, 1]}.
  105.      * @since 3.1
  106.      */
  107.     public PascalDistribution(RandomGenerator rng,
  108.                               int r,
  109.                               double p)
  110.         throws NotStrictlyPositiveException, OutOfRangeException {
  111.         super(rng);

  112.         if (r <= 0) {
  113.             throw new NotStrictlyPositiveException(LocalizedFormats.NUMBER_OF_SUCCESSES,
  114.                                                    r);
  115.         }
  116.         if (p < 0 || p > 1) {
  117.             throw new OutOfRangeException(p, 0, 1);
  118.         }

  119.         numberOfSuccesses = r;
  120.         probabilityOfSuccess = p;
  121.         logProbabilityOfSuccess = FastMath.log(p);
  122.         log1mProbabilityOfSuccess = FastMath.log1p(-p);
  123.     }

  124.     /**
  125.      * Access the number of successes for this distribution.
  126.      *
  127.      * @return the number of successes.
  128.      */
  129.     public int getNumberOfSuccesses() {
  130.         return numberOfSuccesses;
  131.     }

  132.     /**
  133.      * Access the probability of success for this distribution.
  134.      *
  135.      * @return the probability of success.
  136.      */
  137.     public double getProbabilityOfSuccess() {
  138.         return probabilityOfSuccess;
  139.     }

  140.     /** {@inheritDoc} */
  141.     public double probability(int x) {
  142.         double ret;
  143.         if (x < 0) {
  144.             ret = 0.0;
  145.         } else {
  146.             ret = CombinatoricsUtils.binomialCoefficientDouble(x +
  147.                   numberOfSuccesses - 1, numberOfSuccesses - 1) *
  148.                   FastMath.pow(probabilityOfSuccess, numberOfSuccesses) *
  149.                   FastMath.pow(1.0 - probabilityOfSuccess, x);
  150.         }
  151.         return ret;
  152.     }

  153.     /** {@inheritDoc} */
  154.     @Override
  155.     public double logProbability(int x) {
  156.         double ret;
  157.         if (x < 0) {
  158.             ret = Double.NEGATIVE_INFINITY;
  159.         } else {
  160.             ret = CombinatoricsUtils.binomialCoefficientLog(x +
  161.                   numberOfSuccesses - 1, numberOfSuccesses - 1) +
  162.                   logProbabilityOfSuccess * numberOfSuccesses +
  163.                   log1mProbabilityOfSuccess * x;
  164.         }
  165.         return ret;
  166.     }

  167.     /** {@inheritDoc} */
  168.     public double cumulativeProbability(int x) {
  169.         double ret;
  170.         if (x < 0) {
  171.             ret = 0.0;
  172.         } else {
  173.             ret = Beta.regularizedBeta(probabilityOfSuccess,
  174.                     numberOfSuccesses, x + 1.0);
  175.         }
  176.         return ret;
  177.     }

  178.     /**
  179.      * {@inheritDoc}
  180.      *
  181.      * For number of successes {@code r} and probability of success {@code p},
  182.      * the mean is {@code r * (1 - p) / p}.
  183.      */
  184.     public double getNumericalMean() {
  185.         final double p = getProbabilityOfSuccess();
  186.         final double r = getNumberOfSuccesses();
  187.         return (r * (1 - p)) / p;
  188.     }

  189.     /**
  190.      * {@inheritDoc}
  191.      *
  192.      * For number of successes {@code r} and probability of success {@code p},
  193.      * the variance is {@code r * (1 - p) / p^2}.
  194.      */
  195.     public double getNumericalVariance() {
  196.         final double p = getProbabilityOfSuccess();
  197.         final double r = getNumberOfSuccesses();
  198.         return r * (1 - p) / (p * p);
  199.     }

  200.     /**
  201.      * {@inheritDoc}
  202.      *
  203.      * The lower bound of the support is always 0 no matter the parameters.
  204.      *
  205.      * @return lower bound of the support (always 0)
  206.      */
  207.     public int getSupportLowerBound() {
  208.         return 0;
  209.     }

  210.     /**
  211.      * {@inheritDoc}
  212.      *
  213.      * The upper bound of the support is always positive infinity no matter the
  214.      * parameters. Positive infinity is symbolized by {@code Integer.MAX_VALUE}.
  215.      *
  216.      * @return upper bound of the support (always {@code Integer.MAX_VALUE}
  217.      * for positive infinity)
  218.      */
  219.     public int getSupportUpperBound() {
  220.         return Integer.MAX_VALUE;
  221.     }

  222.     /**
  223.      * {@inheritDoc}
  224.      *
  225.      * The support of this distribution is connected.
  226.      *
  227.      * @return {@code true}
  228.      */
  229.     public boolean isSupportConnected() {
  230.         return true;
  231.     }
  232. }