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.rng.UniformRandomProvider; 020import org.apache.commons.rng.sampling.distribution.ZigguratSampler; 021 022/** 023 * Implementation of the exponential distribution. 024 * 025 * <p>The probability density function of \( X \) is: 026 * 027 * <p>\[ f(x; \mu) = \frac{1}{\mu} e^{-x / \mu} \] 028 * 029 * <p>for \( \mu > 0 \) the mean and 030 * \( x \in [0, \infty) \). 031 * 032 * <p>This implementation uses the scale parameter \( \mu \) which is the mean of the distribution. 033 * A common alternative parameterization uses the rate parameter \( \lambda \) which is the reciprocal 034 * of the mean. The distribution can be be created using \( \mu = \frac{1}{\lambda} \). 035 * 036 * @see <a href="https://en.wikipedia.org/wiki/Exponential_distribution">Exponential distribution (Wikipedia)</a> 037 * @see <a href="https://mathworld.wolfram.com/ExponentialDistribution.html">Exponential distribution (MathWorld)</a> 038 */ 039public final class ExponentialDistribution extends AbstractContinuousDistribution { 040 /** Support lower bound. */ 041 private static final double SUPPORT_LO = 0; 042 /** Support upper bound. */ 043 private static final double SUPPORT_HI = Double.POSITIVE_INFINITY; 044 /** ln(2). */ 045 private static final double LN_2 = 0.6931471805599453094172; 046 /** The mean of this distribution. */ 047 private final double mean; 048 /** The logarithm of the mean, stored to reduce computing time. */ 049 private final double logMean; 050 051 /** 052 * @param mean Mean of this distribution. 053 */ 054 private ExponentialDistribution(double mean) { 055 this.mean = mean; 056 logMean = Math.log(mean); 057 } 058 059 /** 060 * Creates an exponential distribution. 061 * 062 * @param mean Mean of this distribution. This is a scale parameter. 063 * @return the distribution 064 * @throws IllegalArgumentException if {@code mean <= 0}. 065 */ 066 public static ExponentialDistribution of(double mean) { 067 if (mean <= 0) { 068 throw new DistributionException(DistributionException.NOT_STRICTLY_POSITIVE, mean); 069 } 070 return new ExponentialDistribution(mean); 071 } 072 073 /** {@inheritDoc} */ 074 @Override 075 public double density(double x) { 076 if (x < SUPPORT_LO) { 077 return 0; 078 } 079 return Math.exp(-x / mean) / mean; 080 } 081 082 /** {@inheritDoc} **/ 083 @Override 084 public double logDensity(double x) { 085 if (x < SUPPORT_LO) { 086 return Double.NEGATIVE_INFINITY; 087 } 088 return -x / mean - logMean; 089 } 090 091 /** {@inheritDoc} */ 092 @Override 093 public double cumulativeProbability(double x) { 094 if (x <= SUPPORT_LO) { 095 return 0; 096 } 097 return -Math.expm1(-x / mean); 098 } 099 100 /** {@inheritDoc} */ 101 @Override 102 public double survivalProbability(double x) { 103 if (x <= SUPPORT_LO) { 104 return 1; 105 } 106 return Math.exp(-x / mean); 107 } 108 109 /** 110 * {@inheritDoc} 111 * 112 * <p>Returns {@code 0} when {@code p == 0} and 113 * {@link Double#POSITIVE_INFINITY} when {@code p == 1}. 114 */ 115 @Override 116 public double inverseCumulativeProbability(double p) { 117 ArgumentUtils.checkProbability(p); 118 if (p == 1) { 119 return Double.POSITIVE_INFINITY; 120 } 121 // Subtract from zero to prevent returning -0.0 for p=-0.0 122 return 0 - mean * Math.log1p(-p); 123 } 124 125 /** 126 * {@inheritDoc} 127 * 128 * <p>Returns {@code 0} when {@code p == 1} and 129 * {@link Double#POSITIVE_INFINITY} when {@code p == 0}. 130 */ 131 @Override 132 public double inverseSurvivalProbability(double p) { 133 ArgumentUtils.checkProbability(p); 134 if (p == 0) { 135 return Double.POSITIVE_INFINITY; 136 } 137 // Subtract from zero to prevent returning -0.0 for p=1 138 return 0 - mean * Math.log(p); 139 } 140 141 /** {@inheritDoc} */ 142 @Override 143 public double getMean() { 144 return mean; 145 } 146 147 /** 148 * {@inheritDoc} 149 * 150 * <p>For mean \( \mu \), the variance is \( \mu^2 \). 151 */ 152 @Override 153 public double getVariance() { 154 return mean * mean; 155 } 156 157 /** 158 * {@inheritDoc} 159 * 160 * <p>The lower bound of the support is always 0. 161 * 162 * @return 0. 163 */ 164 @Override 165 public double getSupportLowerBound() { 166 return SUPPORT_LO; 167 } 168 169 /** 170 * {@inheritDoc} 171 * 172 * <p>The upper bound of the support is always positive infinity. 173 * 174 * @return {@link Double#POSITIVE_INFINITY positive infinity}. 175 */ 176 @Override 177 public double getSupportUpperBound() { 178 return SUPPORT_HI; 179 } 180 181 /** {@inheritDoc} */ 182 @Override 183 double getMedian() { 184 // Overridden for the probability(double, double) method. 185 // This is intentionally not a public method. 186 // ln(2) / rate = mean * ln(2) 187 return mean * LN_2; 188 } 189 190 /** {@inheritDoc} */ 191 @Override 192 public ContinuousDistribution.Sampler createSampler(final UniformRandomProvider rng) { 193 // Exponential distribution sampler. 194 return ZigguratSampler.Exponential.of(rng, getMean())::sample; 195 } 196}