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  
18  package org.apache.commons.statistics.distribution;
19  
20  import org.apache.commons.rng.UniformRandomProvider;
21  import org.apache.commons.rng.sampling.distribution.ContinuousUniformSampler;
22  
23  /**
24   * Implementation of the uniform distribution.
25   *
26   * <p>The probability density function of \( X \) is:
27   *
28   * <p>\[ f(x; a, b) = \frac{1}{b-a} \]
29   *
30   * <p>for \( -\infty \lt a \lt b \lt \infty \) and
31   * \( x \in [a, b] \).
32   *
33   * @see <a href="https://en.wikipedia.org/wiki/Uniform_distribution_(continuous)">
34   * Uniform distribution (Wikipedia)</a>
35   * @see <a href="https://mathworld.wolfram.com/UniformDistribution.html">
36   * Uniform distribution (MathWorld)</a>
37   */
38  public final class UniformContinuousDistribution extends AbstractContinuousDistribution {
39      /** Lower bound of this distribution (inclusive). */
40      private final double lower;
41      /** Upper bound of this distribution (exclusive). */
42      private final double upper;
43      /** Range between the upper and lower bound of this distribution (cached for computations). */
44      private final double upperMinusLower;
45      /** Cache of the density. */
46      private final double pdf;
47      /** Cache of the log density. */
48      private final double logPdf;
49  
50      /**
51       * @param lower Lower bound of this distribution (inclusive).
52       * @param upper Upper bound of this distribution (inclusive).
53       */
54      private UniformContinuousDistribution(double lower,
55                                            double upper) {
56          this.lower = lower;
57          this.upper = upper;
58          upperMinusLower = upper - lower;
59          pdf = 1.0 / upperMinusLower;
60          logPdf = -Math.log(upperMinusLower);
61      }
62  
63      /**
64       * Creates a uniform continuous distribution.
65       *
66       * @param lower Lower bound of this distribution (inclusive).
67       * @param upper Upper bound of this distribution (inclusive).
68       * @return the distribution
69       * @throws IllegalArgumentException if {@code lower >= upper} or the range between the bounds
70       * is not finite
71       */
72      public static UniformContinuousDistribution of(double lower,
73                                                     double upper) {
74          if (lower >= upper) {
75              throw new DistributionException(DistributionException.INVALID_RANGE_LOW_GTE_HIGH,
76                                              lower, upper);
77          }
78          if (!Double.isFinite(upper - lower)) {
79              throw new DistributionException("Range %s is not finite", upper - lower);
80          }
81          return new UniformContinuousDistribution(lower, upper);
82      }
83  
84      /** {@inheritDoc} */
85      @Override
86      public double density(double x) {
87          if (x < lower ||
88              x > upper) {
89              return 0;
90          }
91          return pdf;
92      }
93  
94      /** {@inheritDoc} */
95      @Override
96      public double probability(double x0,
97                                double x1) {
98          if (x0 > x1) {
99              throw new DistributionException(DistributionException.INVALID_RANGE_LOW_GT_HIGH, x0, x1);
100         }
101         if (x0 >= upper || x1 <= lower) {
102             // (x0, x1] does not overlap [lower, upper]
103             return 0;
104         }
105 
106         // x0 < upper
107         // x1 >= lower
108 
109         // Find the range between x0 and x1 that is within [lower, upper].
110         final double l = Math.max(lower, x0);
111         final double u = Math.min(upper, x1);
112 
113         return (u - l) / upperMinusLower;
114     }
115 
116     /** {@inheritDoc} */
117     @Override
118     public double logDensity(double x) {
119         if (x < lower ||
120             x > upper) {
121             return Double.NEGATIVE_INFINITY;
122         }
123         return logPdf;
124     }
125 
126     /** {@inheritDoc} */
127     @Override
128     public double cumulativeProbability(double x)  {
129         if (x <= lower) {
130             return 0;
131         }
132         if (x >= upper) {
133             return 1;
134         }
135         return (x - lower) / upperMinusLower;
136     }
137 
138     /** {@inheritDoc} */
139     @Override
140     public double survivalProbability(double x) {
141         if (x <= lower) {
142             return 1;
143         }
144         if (x >= upper) {
145             return 0;
146         }
147         return (upper - x) / upperMinusLower;
148     }
149 
150     /** {@inheritDoc} */
151     @Override
152     public double inverseCumulativeProbability(double p) {
153         ArgumentUtils.checkProbability(p);
154         // Avoid floating-point error for lower + p * (upper - lower) when p == 1.
155         return p == 1 ? upper : p * upperMinusLower + lower;
156     }
157 
158     /** {@inheritDoc} */
159     @Override
160     public double inverseSurvivalProbability(double p) {
161         ArgumentUtils.checkProbability(p);
162         // Avoid floating-point error for upper - p * (upper - lower) when p == 1.
163         return p == 1 ? lower : upper - p * upperMinusLower;
164     }
165 
166     /**
167      * {@inheritDoc}
168      *
169      * <p>For lower bound \( a \) and upper bound \( b \), the mean is \( \frac{1}{2} (a + b) \).
170      */
171     @Override
172     public double getMean() {
173         // Avoid overflow
174         return 0.5 * lower + 0.5 * upper;
175     }
176 
177     /**
178      * {@inheritDoc}
179      *
180      * <p>For lower bound \( a \) and upper bound \( b \), the variance is \( \frac{1}{12} (b - a)^2 \).
181      */
182     @Override
183     public double getVariance() {
184         return upperMinusLower * upperMinusLower / 12;
185     }
186 
187     /**
188      * {@inheritDoc}
189      *
190      * <p>The lower bound of the support is equal to the lower bound parameter
191      * of the distribution.
192      */
193     @Override
194     public double getSupportLowerBound() {
195         return lower;
196     }
197 
198     /**
199      * {@inheritDoc}
200      *
201      * <p>The upper bound of the support is equal to the upper bound parameter
202      * of the distribution.
203      */
204     @Override
205     public double getSupportUpperBound() {
206         return upper;
207     }
208 
209     /** {@inheritDoc} */
210     @Override
211     public ContinuousDistribution.Sampler createSampler(final UniformRandomProvider rng) {
212         // Uniform distribution sampler.
213         return ContinuousUniformSampler.of(rng, lower, upper)::sample;
214     }
215 }