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 018package org.apache.commons.statistics.distribution; 019 020import org.apache.commons.rng.UniformRandomProvider; 021import org.apache.commons.rng.sampling.distribution.ContinuousUniformSampler; 022 023/** 024 * Implementation of the uniform distribution. 025 * 026 * <p>The probability density function of \( X \) is: 027 * 028 * <p>\[ f(x; a, b) = \frac{1}{b-a} \] 029 * 030 * <p>for \( -\infty \lt a \lt b \lt \infty \) and 031 * \( x \in [a, b] \). 032 * 033 * @see <a href="https://en.wikipedia.org/wiki/Uniform_distribution_(continuous)"> 034 * Uniform distribution (Wikipedia)</a> 035 * @see <a href="https://mathworld.wolfram.com/UniformDistribution.html"> 036 * Uniform distribution (MathWorld)</a> 037 */ 038public final class UniformContinuousDistribution extends AbstractContinuousDistribution { 039 /** Lower bound of this distribution (inclusive). */ 040 private final double lower; 041 /** Upper bound of this distribution (exclusive). */ 042 private final double upper; 043 /** Range between the upper and lower bound of this distribution (cached for computations). */ 044 private final double upperMinusLower; 045 /** Cache of the density. */ 046 private final double pdf; 047 /** Cache of the log density. */ 048 private final double logPdf; 049 050 /** 051 * @param lower Lower bound of this distribution (inclusive). 052 * @param upper Upper bound of this distribution (inclusive). 053 */ 054 private UniformContinuousDistribution(double lower, 055 double upper) { 056 this.lower = lower; 057 this.upper = upper; 058 upperMinusLower = upper - lower; 059 pdf = 1.0 / upperMinusLower; 060 logPdf = -Math.log(upperMinusLower); 061 } 062 063 /** 064 * Creates a uniform continuous distribution. 065 * 066 * @param lower Lower bound of this distribution (inclusive). 067 * @param upper Upper bound of this distribution (inclusive). 068 * @return the distribution 069 * @throws IllegalArgumentException if {@code lower >= upper} or the range between the bounds 070 * is not finite 071 */ 072 public static UniformContinuousDistribution of(double lower, 073 double upper) { 074 if (lower >= upper) { 075 throw new DistributionException(DistributionException.INVALID_RANGE_LOW_GTE_HIGH, 076 lower, upper); 077 } 078 if (!Double.isFinite(upper - lower)) { 079 throw new DistributionException("Range %s is not finite", upper - lower); 080 } 081 return new UniformContinuousDistribution(lower, upper); 082 } 083 084 /** {@inheritDoc} */ 085 @Override 086 public double density(double x) { 087 if (x < lower || 088 x > upper) { 089 return 0; 090 } 091 return pdf; 092 } 093 094 /** {@inheritDoc} */ 095 @Override 096 public double probability(double x0, 097 double x1) { 098 if (x0 > x1) { 099 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}