View Javadoc

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.math.distribution;
18  
19  import java.io.Serializable;
20  
21  import org.apache.commons.math.MathException;
22  import org.apache.commons.math.MathRuntimeException;
23  
24  
25  /**
26   * Base class for integer-valued discrete distributions.  Default
27   * implementations are provided for some of the methods that do not vary
28   * from distribution to distribution.
29   *  
30   * @version $Revision: 772119 $ $Date: 2009-05-06 05:43:28 -0400 (Wed, 06 May 2009) $
31   */
32  public abstract class AbstractIntegerDistribution extends AbstractDistribution
33      implements IntegerDistribution, Serializable {
34          
35      /** Serializable version identifier */
36      private static final long serialVersionUID = -1146319659338487221L;
37      
38      /**
39       * Default constructor.
40       */
41      protected AbstractIntegerDistribution() {
42          super();
43      }
44      
45      /**
46       * For a random variable X whose values are distributed according
47       * to this distribution, this method returns P(X ≤ x).  In other words,
48       * this method represents the  (cumulative) distribution function, or
49       * CDF, for this distribution.
50       * <p>
51       * If <code>x</code> does not represent an integer value, the CDF is 
52       * evaluated at the greatest integer less than x.
53       * 
54       * @param x the value at which the distribution function is evaluated.
55       * @return cumulative probability that a random variable with this
56       * distribution takes a value less than or equal to <code>x</code>
57       * @throws MathException if the cumulative probability can not be
58       * computed due to convergence or other numerical errors.
59       */
60      public double cumulativeProbability(double x) throws MathException {
61          return cumulativeProbability((int) Math.floor(x));  
62      }
63      
64      /**
65       * For a random variable X whose values are distributed according
66       * to this distribution, this method returns P(x0 &le; X &le; x1).
67       * 
68       * @param x0 the (inclusive) lower bound
69       * @param x1 the (inclusive) upper bound
70       * @return the probability that a random variable with this distribution
71       * will take a value between <code>x0</code> and <code>x1</code>,
72       * including the endpoints.
73       * @throws MathException if the cumulative probability can not be
74       * computed due to convergence or other numerical errors.
75       * @throws IllegalArgumentException if <code>x0 > x1</code>
76       */
77      @Override
78      public double cumulativeProbability(double x0, double x1)
79          throws MathException {
80          if (x0 > x1) {
81              throw MathRuntimeException.createIllegalArgumentException(
82                    "lower endpoint ({0}) must be less than or equal to upper endpoint ({1})",
83                    x0, x1);
84          }
85          if (Math.floor(x0) < x0) {
86              return cumulativeProbability(((int) Math.floor(x0)) + 1,
87                 (int) Math.floor(x1)); // don't want to count mass below x0
88          } else { // x0 is mathematical integer, so use as is
89              return cumulativeProbability((int) Math.floor(x0),
90                  (int) Math.floor(x1)); 
91          }
92      }
93      
94      /**
95       * For a random variable X whose values are distributed according
96       * to this distribution, this method returns P(X &le; x).  In other words,
97       * this method represents the probability distribution function, or PDF,
98       * for this distribution.
99       * 
100      * @param x the value at which the PDF is evaluated.
101      * @return PDF for this distribution. 
102      * @throws MathException if the cumulative probability can not be
103      *            computed due to convergence or other numerical errors.
104      */
105     abstract public double cumulativeProbability(int x) throws MathException;
106     
107     /**
108      * For a random variable X whose values are distributed according
109      * to this distribution, this method returns P(X = x). In other words, this
110      * method represents the probability mass function,  or PMF, for the distribution.
111      * <p>
112      * If <code>x</code> does not represent an integer value, 0 is returned.
113      * 
114      * @param x the value at which the probability density function is evaluated
115      * @return the value of the probability density function at x
116      */
117     public double probability(double x) {
118         double fl = Math.floor(x);
119         if (fl == x) {
120             return this.probability((int) x);
121         } else {
122             return 0;
123         }
124     }
125     
126     /**
127     * For a random variable X whose values are distributed according
128      * to this distribution, this method returns P(x0 &le; X &le; x1).
129      * 
130      * @param x0 the inclusive, lower bound
131      * @param x1 the inclusive, upper bound
132      * @return the cumulative probability. 
133      * @throws MathException if the cumulative probability can not be
134      *            computed due to convergence or other numerical errors.
135      * @throws IllegalArgumentException if x0 > x1
136      */
137     public double cumulativeProbability(int x0, int x1) throws MathException {
138         if (x0 > x1) {
139             throw MathRuntimeException.createIllegalArgumentException(
140                   "lower endpoint ({0}) must be less than or equal to upper endpoint ({1})",
141                   x0, x1);
142         }
143         return cumulativeProbability(x1) - cumulativeProbability(x0 - 1);
144     }
145     
146     /**
147      * For a random variable X whose values are distributed according
148      * to this distribution, this method returns the largest x, such
149      * that P(X &le; x) &le; <code>p</code>.
150      *
151      * @param p the desired probability
152      * @return the largest x such that P(X &le; x) <= p
153      * @throws MathException if the inverse cumulative probability can not be
154      *            computed due to convergence or other numerical errors.
155      * @throws IllegalArgumentException if p < 0 or p > 1
156      */
157     public int inverseCumulativeProbability(final double p) throws MathException{
158         if (p < 0.0 || p > 1.0) {
159             throw MathRuntimeException.createIllegalArgumentException(
160                   "{0} out of [{1}, {2}] range", p, 0.0, 1.0);
161         }
162         
163         // by default, do simple bisection.
164         // subclasses can override if there is a better method.
165         int x0 = getDomainLowerBound(p);
166         int x1 = getDomainUpperBound(p);
167         double pm;
168         while (x0 < x1) {
169             int xm = x0 + (x1 - x0) / 2;
170             pm = cumulativeProbability(xm);
171             if (pm > p) {
172                 // update x1
173                 if (xm == x1) {
174                     // this can happen with integer division
175                     // simply decrement x1
176                     --x1;
177                 } else {
178                     // update x1 normally
179                     x1 = xm;
180                 }
181             } else {
182                 // update x0
183                 if (xm == x0) {
184                     // this can happen with integer division
185                     // simply increment x0
186                     ++x0;
187                 } else {
188                     // update x0 normally
189                     x0 = xm;
190                 }
191             }
192         }
193         
194         // insure x0 is the correct critical point
195         pm = cumulativeProbability(x0);
196         while (pm > p) {
197             --x0;
198             pm = cumulativeProbability(x0);
199         }
200     
201         return x0;        
202     }
203     
204     /**
205      * Access the domain value lower bound, based on <code>p</code>, used to
206      * bracket a PDF root.  This method is used by
207      * {@link #inverseCumulativeProbability(double)} to find critical values.
208      * 
209      * @param p the desired probability for the critical value
210      * @return domain value lower bound, i.e.
211      *         P(X &lt; <i>lower bound</i>) &lt; <code>p</code> 
212      */
213     protected abstract int getDomainLowerBound(double p);
214     
215     /**
216      * Access the domain value upper bound, based on <code>p</code>, used to
217      * bracket a PDF root.  This method is used by
218      * {@link #inverseCumulativeProbability(double)} to find critical values.
219      * 
220      * @param p the desired probability for the critical value
221      * @return domain value upper bound, i.e.
222      *         P(X &lt; <i>upper bound</i>) &gt; <code>p</code> 
223      */
224     protected abstract int getDomainUpperBound(double p);
225 }