001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.math.distribution;
018
019 import java.io.Serializable;
020
021 import org.apache.commons.math.exception.OutOfRangeException;
022 import org.apache.commons.math.exception.NotPositiveException;
023 import org.apache.commons.math.exception.util.LocalizedFormats;
024 import org.apache.commons.math.special.Beta;
025 import org.apache.commons.math.util.FastMath;
026
027 /**
028 * The default implementation of {@link BinomialDistribution}.
029 *
030 * @version $Id: BinomialDistributionImpl.java 1178295 2011-10-03 04:36:27Z psteitz $
031 */
032 public class BinomialDistributionImpl extends AbstractIntegerDistribution
033 implements BinomialDistribution, Serializable {
034 /** Serializable version identifier. */
035 private static final long serialVersionUID = 6751309484392813623L;
036 /** The number of trials. */
037 private final int numberOfTrials;
038 /** The probability of success. */
039 private final double probabilityOfSuccess;
040
041 /**
042 * Create a binomial distribution with the given number of trials and
043 * probability of success.
044 *
045 * @param trials Number of trials.
046 * @param p Probability of success.
047 * @throws NotPositiveException if {@code trials < 0}.
048 * @throws OutOfRangeException if {@code p < 0} or {@code p > 1}.
049 */
050 public BinomialDistributionImpl(int trials, double p) {
051 if (trials < 0) {
052 throw new NotPositiveException(LocalizedFormats.NUMBER_OF_TRIALS,
053 trials);
054 }
055 if (p < 0 || p > 1) {
056 throw new OutOfRangeException(p, 0, 1);
057 }
058
059 probabilityOfSuccess = p;
060 numberOfTrials = trials;
061 }
062
063 /**
064 * {@inheritDoc}
065 */
066 public int getNumberOfTrials() {
067 return numberOfTrials;
068 }
069
070 /**
071 * {@inheritDoc}
072 */
073 public double getProbabilityOfSuccess() {
074 return probabilityOfSuccess;
075 }
076
077 /**
078 * Access the domain value lower bound, based on {@code p}, used to
079 * bracket a PDF root.
080 *
081 * @param p Desired probability for the critical value.
082 * @return the domain value lower bound, i.e. {@code P(X < 'lower bound') < p}.
083 */
084 @Override
085 protected int getDomainLowerBound(double p) {
086 return -1;
087 }
088
089 /**
090 * Access the domain value upper bound, based on {@code p}, used to
091 * bracket a PDF root.
092 *
093 * @param p Desired probability for the critical value
094 * @return the domain value upper bound, i.e. {@code P(X < 'upper bound') > p}.
095 */
096 @Override
097 protected int getDomainUpperBound(double p) {
098 return numberOfTrials;
099 }
100
101 /**
102 * For this distribution, {@code X}, this method returns {@code P(X <= x)}.
103 *
104 * @param x Value at which the PDF is evaluated.
105 * @return PDF for this distribution.
106 * due to convergence or other numerical errors.
107 */
108 @Override
109 public double cumulativeProbability(int x) {
110 double ret;
111 if (x < 0) {
112 ret = 0.0;
113 } else if (x >= numberOfTrials) {
114 ret = 1.0;
115 } else {
116 ret = 1.0 - Beta.regularizedBeta(getProbabilityOfSuccess(),
117 x + 1.0, numberOfTrials - x);
118 }
119 return ret;
120 }
121
122 /**
123 * For this distribution, {@code X}, this method returns {@code P(X = x)}.
124 *
125 * @param x Value at which the PMF is evaluated.
126 * @return PMF for this distribution.
127 */
128 public double probability(int x) {
129 double ret;
130 if (x < 0 || x > numberOfTrials) {
131 ret = 0.0;
132 } else {
133 ret = FastMath.exp(SaddlePointExpansion.logBinomialProbability(x,
134 numberOfTrials, probabilityOfSuccess,
135 1.0 - probabilityOfSuccess));
136 }
137 return ret;
138 }
139
140 /**
141 * For this distribution, {@code X}, this method returns the largest
142 * {@code x}, such that {@code P(X < x) p}.
143 * It will return -1 when p = 0 and {@code Integer.MAX_VALUE} when p = 1.
144 *
145 * @param p Desired probability.
146 * @return the largest {@code x} such that {@code P(X < x) <= p}.
147 * @throws OutOfRangeException if {@code p < 0} or {@code p > 1}.
148 */
149 @Override
150 public int inverseCumulativeProbability(final double p) {
151 // handle extreme values explicitly
152 if (p == 0) {
153 return -1;
154 }
155 if (p == 1) {
156 return Integer.MAX_VALUE;
157 }
158
159 // use default bisection impl
160 return super.inverseCumulativeProbability(p);
161 }
162
163 /**
164 * {@inheritDoc}
165 *
166 * The lower bound of the support is always 0 no matter the number of trials
167 * and probability parameter.
168 *
169 * @return lower bound of the support (always 0)
170 */
171 @Override
172 public int getSupportLowerBound() {
173 return 0;
174 }
175
176 /**
177 * {@inheritDoc}
178 *
179 * The upper bound of the support is the number of trials.
180 *
181 * @return upper bound of the support (equal to number of trials)
182 */
183 @Override
184 public int getSupportUpperBound() {
185 return getNumberOfTrials();
186 }
187
188 /**
189 * {@inheritDoc}
190 *
191 * For <code>n</code> number of trials and
192 * probability parameter <code>p</code>, the mean is
193 * <code>n * p</code>
194 *
195 * @return {@inheritDoc}
196 */
197 @Override
198 protected double calculateNumericalMean() {
199 return (double)getNumberOfTrials() * getProbabilityOfSuccess();
200 }
201
202 /**
203 * {@inheritDoc}
204 *
205 * For <code>n</code> number of trials and
206 * probability parameter <code>p</code>, the variance is
207 * <code>n * p * (1 - p)</code>
208 *
209 * @return {@inheritDoc}
210 */
211 @Override
212 protected double calculateNumericalVariance() {
213 final double p = getProbabilityOfSuccess();
214 return (double)getNumberOfTrials() * p * (1 - p);
215 }
216 }