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 }