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 package org.apache.commons.math.distribution;
018
019 import java.io.Serializable;
020
021 import org.apache.commons.math.MathException;
022 import org.apache.commons.math.MathRuntimeException;
023
024 /**
025 * The default implementation of {@link ExponentialDistribution}.
026 *
027 * @version $Revision: 772119 $ $Date: 2009-05-06 05:43:28 -0400 (Wed, 06 May 2009) $
028 */
029 public class ExponentialDistributionImpl extends AbstractContinuousDistribution
030 implements ExponentialDistribution, Serializable {
031
032 /** Serializable version identifier */
033 private static final long serialVersionUID = 2401296428283614780L;
034
035 /** The mean of this distribution. */
036 private double mean;
037
038 /**
039 * Create a exponential distribution with the given mean.
040 * @param mean mean of this distribution.
041 */
042 public ExponentialDistributionImpl(double mean) {
043 super();
044 setMean(mean);
045 }
046
047 /**
048 * Modify the mean.
049 * @param mean the new mean.
050 * @throws IllegalArgumentException if <code>mean</code> is not positive.
051 */
052 public void setMean(double mean) {
053 if (mean <= 0.0) {
054 throw MathRuntimeException.createIllegalArgumentException(
055 "mean must be positive ({0})", mean);
056 }
057 this.mean = mean;
058 }
059
060 /**
061 * Access the mean.
062 * @return the mean.
063 */
064 public double getMean() {
065 return mean;
066 }
067
068 /**
069 * Return the probability density for a particular point.
070 *
071 * @param x The point at which the density should be computed.
072 * @return The pdf at point x.
073 */
074 public double density(Double x) {
075 if (x < 0) {
076 return 0;
077 }
078 return Math.exp(-x / getMean()) / getMean();
079 }
080
081 /**
082 * For this distribution, X, this method returns P(X < x).
083 *
084 * The implementation of this method is based on:
085 * <ul>
086 * <li>
087 * <a href="http://mathworld.wolfram.com/ExponentialDistribution.html">
088 * Exponential Distribution</a>, equation (1).</li>
089 * </ul>
090 *
091 * @param x the value at which the CDF is evaluated.
092 * @return CDF for this distribution.
093 * @throws MathException if the cumulative probability can not be
094 * computed due to convergence or other numerical errors.
095 */
096 public double cumulativeProbability(double x) throws MathException{
097 double ret;
098 if (x <= 0.0) {
099 ret = 0.0;
100 } else {
101 ret = 1.0 - Math.exp(-x / getMean());
102 }
103 return ret;
104 }
105
106 /**
107 * For this distribution, X, this method returns the critical point x, such
108 * that P(X < x) = <code>p</code>.
109 * <p>
110 * Returns 0 for p=0 and <code>Double.POSITIVE_INFINITY</code> for p=1.</p>
111 *
112 * @param p the desired probability
113 * @return x, such that P(X < x) = <code>p</code>
114 * @throws MathException if the inverse cumulative probability can not be
115 * computed due to convergence or other numerical errors.
116 * @throws IllegalArgumentException if p < 0 or p > 1.
117 */
118 @Override
119 public double inverseCumulativeProbability(double p) throws MathException {
120 double ret;
121
122 if (p < 0.0 || p > 1.0) {
123 throw MathRuntimeException.createIllegalArgumentException(
124 "{0} out of [{1}, {2}] range", p, 0.0, 1.0);
125 } else if (p == 1.0) {
126 ret = Double.POSITIVE_INFINITY;
127 } else {
128 ret = -getMean() * Math.log(1.0 - p);
129 }
130
131 return ret;
132 }
133
134 /**
135 * Access the domain value lower bound, based on <code>p</code>, used to
136 * bracket a CDF root.
137 *
138 * @param p the desired probability for the critical value
139 * @return domain value lower bound, i.e.
140 * P(X < <i>lower bound</i>) < <code>p</code>
141 */
142 @Override
143 protected double getDomainLowerBound(double p) {
144 return 0;
145 }
146
147 /**
148 * Access the domain value upper bound, based on <code>p</code>, used to
149 * bracket a CDF root.
150 *
151 * @param p the desired probability for the critical value
152 * @return domain value upper bound, i.e.
153 * P(X < <i>upper bound</i>) > <code>p</code>
154 */
155 @Override
156 protected double getDomainUpperBound(double p) {
157 // NOTE: exponential is skewed to the left
158 // NOTE: therefore, P(X < μ) > .5
159
160 if (p < .5) {
161 // use mean
162 return getMean();
163 } else {
164 // use max
165 return Double.MAX_VALUE;
166 }
167 }
168
169 /**
170 * Access the initial domain value, based on <code>p</code>, used to
171 * bracket a CDF root.
172 *
173 * @param p the desired probability for the critical value
174 * @return initial domain value
175 */
176 @Override
177 protected double getInitialDomain(double p) {
178 // TODO: try to improve on this estimate
179 // TODO: what should really happen here is not derive from AbstractContinuousDistribution
180 // TODO: because the inverse cumulative distribution is simple.
181 // Exponential is skewed to the left, therefore, P(X < μ) > .5
182 if (p < .5) {
183 // use 1/2 mean
184 return getMean() * .5;
185 } else {
186 // use mean
187 return getMean();
188 }
189 }
190 }