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 */
017package org.apache.commons.statistics.distribution;
018
019import org.apache.commons.numbers.gamma.RegularizedBeta;
020
021/**
022 * Implementation of the <a href="http://en.wikipedia.org/wiki/Binomial_distribution">binomial distribution</a>.
023 */
024public class BinomialDistribution extends AbstractDiscreteDistribution {
025    /** The number of trials. */
026    private final int numberOfTrials;
027    /** The probability of success. */
028    private final double probabilityOfSuccess;
029
030    /**
031     * Creates a binomial distribution.
032     *
033     * @param trials Number of trials.
034     * @param p Probability of success.
035     * @throws IllegalArgumentException if {@code trials < 0}, or if
036     * {@code p < 0} or {@code p > 1}.
037     */
038    public BinomialDistribution(int trials,
039                                double p) {
040        if (trials < 0) {
041            throw new DistributionException(DistributionException.NEGATIVE,
042                                           trials);
043        }
044        if (p < 0 ||
045            p > 1) {
046            throw new DistributionException(DistributionException.INVALID_PROBABILITY, p);
047        }
048
049        probabilityOfSuccess = p;
050        numberOfTrials = trials;
051    }
052
053    /**
054     * Access the number of trials for this distribution.
055     *
056     * @return the number of trials.
057     */
058    public int getNumberOfTrials() {
059        return numberOfTrials;
060    }
061
062    /**
063     * Access the probability of success for this distribution.
064     *
065     * @return the probability of success.
066     */
067    public double getProbabilityOfSuccess() {
068        return probabilityOfSuccess;
069    }
070
071    /** {@inheritDoc} */
072    @Override
073    public double probability(int x) {
074        final double logProbability = logProbability(x);
075        return logProbability == Double.NEGATIVE_INFINITY ? 0 : Math.exp(logProbability);
076    }
077
078    /** {@inheritDoc} **/
079    @Override
080    public double logProbability(int x) {
081        if (numberOfTrials == 0) {
082            return (x == 0) ? 0. : Double.NEGATIVE_INFINITY;
083        }
084        double ret;
085        if (x < 0 || x > numberOfTrials) {
086            ret = Double.NEGATIVE_INFINITY;
087        } else {
088            ret = SaddlePointExpansionUtils.logBinomialProbability(x,
089                    numberOfTrials, probabilityOfSuccess,
090                    1.0 - probabilityOfSuccess);
091        }
092        return ret;
093    }
094
095    /** {@inheritDoc} */
096    @Override
097    public double cumulativeProbability(int x) {
098        double ret;
099        if (x < 0) {
100            ret = 0.0;
101        } else if (x >= numberOfTrials) {
102            ret = 1.0;
103        } else {
104            ret = 1.0 - RegularizedBeta.value(probabilityOfSuccess,
105                                              x + 1.0, (double) numberOfTrials - x);
106        }
107        return ret;
108    }
109
110    /**
111     * {@inheritDoc}
112     *
113     * For {@code n} trials and probability parameter {@code p}, the mean is
114     * {@code n * p}.
115     */
116    @Override
117    public double getMean() {
118        return numberOfTrials * probabilityOfSuccess;
119    }
120
121    /**
122     * {@inheritDoc}
123     *
124     * For {@code n} trials and probability parameter {@code p}, the variance is
125     * {@code n * p * (1 - p)}.
126     */
127    @Override
128    public double getVariance() {
129        final double p = probabilityOfSuccess;
130        return numberOfTrials * p * (1 - p);
131    }
132
133    /**
134     * {@inheritDoc}
135     *
136     * The lower bound of the support is always 0 except for the probability
137     * parameter {@code p = 1}.
138     *
139     * @return lower bound of the support (0 or the number of trials)
140     */
141    @Override
142    public int getSupportLowerBound() {
143        return probabilityOfSuccess < 1.0 ? 0 : numberOfTrials;
144    }
145
146    /**
147     * {@inheritDoc}
148     *
149     * The upper bound of the support is the number of trials except for the
150     * probability parameter {@code p = 0}.
151     *
152     * @return upper bound of the support (number of trials or 0)
153     */
154    @Override
155    public int getSupportUpperBound() {
156        return probabilityOfSuccess > 0.0 ? numberOfTrials : 0;
157    }
158
159    /**
160     * {@inheritDoc}
161     *
162     * The support of this distribution is connected.
163     *
164     * @return {@code true}
165     */
166    @Override
167    public boolean isSupportConnected() {
168        return true;
169    }
170}