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    }