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 /*
19 * This is not the original file distributed by the Apache Software Foundation
20 * It has been modified by the Hipparchus project
21 */
22 package org.hipparchus.distribution.discrete;
23
24 import org.hipparchus.exception.LocalizedCoreFormats;
25 import org.hipparchus.exception.MathIllegalArgumentException;
26 import org.hipparchus.special.Beta;
27 import org.hipparchus.util.CombinatoricsUtils;
28 import org.hipparchus.util.FastMath;
29 import org.hipparchus.util.MathUtils;
30
31 /**
32 * Implementation of the Pascal distribution.
33 * <p>
34 * The Pascal distribution is a special case of the Negative Binomial distribution
35 * where the number of successes parameter is an integer.
36 * <p>
37 * There are various ways to express the probability mass and distribution
38 * functions for the Pascal distribution. The present implementation represents
39 * the distribution of the number of failures before {@code r} successes occur.
40 * This is the convention adopted in e.g.
41 * <a href="http://mathworld.wolfram.com/NegativeBinomialDistribution.html">MathWorld</a>,
42 * but <em>not</em> in
43 * <a href="http://en.wikipedia.org/wiki/Negative_binomial_distribution">Wikipedia</a>.
44 * <p>
45 * For a random variable {@code X} whose values are distributed according to this
46 * distribution, the probability mass function is given by<br>
47 * {@code P(X = k) = C(k + r - 1, r - 1) * p^r * (1 - p)^k,}<br>
48 * where {@code r} is the number of successes, {@code p} is the probability of
49 * success, and {@code X} is the total number of failures. {@code C(n, k)} is
50 * the binomial coefficient ({@code n} choose {@code k}). The mean and variance
51 * of {@code X} are<br>
52 * {@code E(X) = (1 - p) * r / p, var(X) = (1 - p) * r / p^2.}<br>
53 * Finally, the cumulative distribution function is given by<br>
54 * {@code P(X <= k) = I(p, r, k + 1)},
55 * where I is the regularized incomplete Beta function.
56 *
57 * @see <a href="http://en.wikipedia.org/wiki/Negative_binomial_distribution">
58 * Negative binomial distribution (Wikipedia)</a>
59 * @see <a href="http://mathworld.wolfram.com/NegativeBinomialDistribution.html">
60 * Negative binomial distribution (MathWorld)</a>
61 */
62 public class PascalDistribution extends AbstractIntegerDistribution {
63 /** Serializable version identifier. */
64 private static final long serialVersionUID = 20160320L;
65 /** The number of successes. */
66 private final int numberOfSuccesses;
67 /** The probability of success. */
68 private final double probabilityOfSuccess;
69 /** The value of {@code log(p)}, where {@code p} is the probability of success,
70 * stored for faster computation. */
71 private final double logProbabilityOfSuccess;
72 /** The value of {@code log(1-p)}, where {@code p} is the probability of success,
73 * stored for faster computation. */
74 private final double log1mProbabilityOfSuccess;
75
76 /**
77 * Create a Pascal distribution with the given number of successes and
78 * probability of success.
79 *
80 * @param r Number of successes.
81 * @param p Probability of success.
82 * @throws MathIllegalArgumentException if the number of successes is not positive
83 * @throws MathIllegalArgumentException if the probability of success is not in the
84 * range {@code [0, 1]}.
85 */
86 public PascalDistribution(int r, double p)
87 throws MathIllegalArgumentException {
88 if (r <= 0) {
89 throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_OF_SUCCESSES,
90 r);
91 }
92
93 MathUtils.checkRangeInclusive(p, 0, 1);
94
95 numberOfSuccesses = r;
96 probabilityOfSuccess = p;
97 logProbabilityOfSuccess = FastMath.log(p);
98 log1mProbabilityOfSuccess = FastMath.log1p(-p);
99 }
100
101 /**
102 * Access the number of successes for this distribution.
103 *
104 * @return the number of successes.
105 */
106 public int getNumberOfSuccesses() {
107 return numberOfSuccesses;
108 }
109
110 /**
111 * Access the probability of success for this distribution.
112 *
113 * @return the probability of success.
114 */
115 public double getProbabilityOfSuccess() {
116 return probabilityOfSuccess;
117 }
118
119 /** {@inheritDoc} */
120 @Override
121 public double probability(int x) {
122 double ret;
123 if (x < 0) {
124 ret = 0.0;
125 } else {
126 ret = CombinatoricsUtils.binomialCoefficientDouble(x +
127 numberOfSuccesses - 1, numberOfSuccesses - 1) *
128 FastMath.pow(probabilityOfSuccess, numberOfSuccesses) *
129 FastMath.pow(1.0 - probabilityOfSuccess, x);
130 }
131 return ret;
132 }
133
134 /** {@inheritDoc} */
135 @Override
136 public double logProbability(int x) {
137 double ret;
138 if (x < 0) {
139 ret = Double.NEGATIVE_INFINITY;
140 } else {
141 ret = CombinatoricsUtils.binomialCoefficientLog(x +
142 numberOfSuccesses - 1, numberOfSuccesses - 1) +
143 logProbabilityOfSuccess * numberOfSuccesses +
144 log1mProbabilityOfSuccess * x;
145 }
146 return ret;
147 }
148
149 /** {@inheritDoc} */
150 @Override
151 public double cumulativeProbability(int x) {
152 double ret;
153 if (x < 0) {
154 ret = 0.0;
155 } else {
156 ret = Beta.regularizedBeta(probabilityOfSuccess,
157 numberOfSuccesses, x + 1.0);
158 }
159 return ret;
160 }
161
162 /**
163 * {@inheritDoc}
164 *
165 * For number of successes {@code r} and probability of success {@code p},
166 * the mean is {@code r * (1 - p) / p}.
167 */
168 @Override
169 public double getNumericalMean() {
170 final double p = getProbabilityOfSuccess();
171 final double r = getNumberOfSuccesses();
172 return (r * (1 - p)) / p;
173 }
174
175 /**
176 * {@inheritDoc}
177 *
178 * For number of successes {@code r} and probability of success {@code p},
179 * the variance is {@code r * (1 - p) / p^2}.
180 */
181 @Override
182 public double getNumericalVariance() {
183 final double p = getProbabilityOfSuccess();
184 final double r = getNumberOfSuccesses();
185 return r * (1 - p) / (p * p);
186 }
187
188 /**
189 * {@inheritDoc}
190 *
191 * The lower bound of the support is always 0 no matter the parameters.
192 *
193 * @return lower bound of the support (always 0)
194 */
195 @Override
196 public int getSupportLowerBound() {
197 return 0;
198 }
199
200 /**
201 * {@inheritDoc}
202 *
203 * The upper bound of the support is always positive infinity no matter the
204 * parameters. Positive infinity is symbolized by {@code Integer.MAX_VALUE}.
205 *
206 * @return upper bound of the support (always {@code Integer.MAX_VALUE}
207 * for positive infinity)
208 */
209 @Override
210 public int getSupportUpperBound() {
211 return Integer.MAX_VALUE;
212 }
213
214 /**
215 * {@inheritDoc}
216 *
217 * The support of this distribution is connected.
218 *
219 * @return {@code true}
220 */
221 @Override
222 public boolean isSupportConnected() {
223 return true;
224 }
225 }