DistGamma.java
package nl.tudelft.simulation.jstats.distributions;
import org.djutils.exceptions.Throw;
import org.djutils.logger.CategoryLogger;
import nl.tudelft.simulation.jstats.math.ProbMath;
import nl.tudelft.simulation.jstats.streams.StreamInterface;
/**
* The Gamma distribution. For more information on this distribution see
* <a href="https://mathworld.wolfram.com/GammaDistribution.html"> https://mathworld.wolfram.com/GammaDistribution.html </a><br>
* The parameters are not rate-related, but average-related, so the mean is shape*scale (or αθ), and the variance is
* αθ<sup>2</sup>.
* <p>
* Copyright (c) 2002-2024 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
* for project information <a href="https://simulation.tudelft.nl/" target="_blank"> https://simulation.tudelft.nl</a>. The DSOL
* project is distributed under a three-clause BSD-style license, which can be found at
* <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">
* https://https://simulation.tudelft.nl/dsol/docs/latest/license.html</a>.
* </p>
* @author <a href="https://www.linkedin.com/in/peterhmjacobs">Peter Jacobs </a>
* @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
*/
public class DistGamma extends DistContinuous
{
/** */
private static final long serialVersionUID = 1L;
/** the shape parameter of the distribution, also often called α or k. */
private final double shape;
/** the scale parameter of the distribution, also often called θ. */
private final double scale;
/**
* constructs a new gamma distribution. The gamma distribution represents the time to complete some task, e.g. customer
* service or machine repair. The parameters are not rate-related, but average-related, so the mean is shape*scale (or
* αθ or kθ), and the variance is αθ<sup>2</sup>.
* @param stream StreamInterface; the random number stream
* @param shape double; is the shape parameter > 0, also known as α or k
* @param scale double; is the scale parameter> 0, also known as θ
* @throws IllegalArgumentException when shape <= 0.0 or scale <= 0.0
*/
public DistGamma(final StreamInterface stream, final double shape, final double scale)
{
super(stream);
Throw.when(shape <= 0.0 || scale <= 0.0, IllegalArgumentException.class, "Error Gamma - shape <= 0.0 or scale <= 0.0");
this.shape = shape;
this.scale = scale;
}
/** {@inheritDoc} */
@Override
public double draw()
{
// according to Law and Kelton, Simulation Modeling and Analysis, 1991
// pages 488-489
if (this.shape < 1.0)
{
double b = (Math.E + this.shape) / Math.E;
int counter = 0;
while (counter < 1000)
{
// step 1.
double p = b * this.stream.nextDouble();
if (p <= 1.0d)
{
// step 2.
double y = Math.pow(p, 1.0d / this.shape);
double u2 = this.stream.nextDouble();
if (u2 <= Math.exp(-y))
{
return this.scale * y;
}
}
else
{
// step 3.
double y = -Math.log((b - p) / this.shape);
double u2 = this.stream.nextDouble();
if (u2 <= Math.pow(y, this.shape - 1.0d))
{
return this.scale * y;
}
}
counter++;
}
CategoryLogger.always().info("Gamma distribution -- 1000 tries for alpha<1.0");
return 1.0d;
}
else if (this.shape > 1.0)
{
// according to Law and Kelton, Simulation Modeling and
// Analysis, 1991, pages 488-489
double a = 1.0d / Math.sqrt(2.0d * this.shape - 1.0d);
double b = this.shape - Math.log(4.0d);
double q = this.shape + (1.0d / a);
double theta = 4.5d;
double d = 1.0d + Math.log(theta);
int counter = 0;
while (counter < 1000)
{
// step 1.
double u1 = this.stream.nextDouble();
double u2 = this.stream.nextDouble();
// step 2.
double v = a * Math.log(u1 / (1.0d - u1));
double y = this.shape * Math.exp(v);
double z = u1 * u1 * u2;
double w = b + q * v - y;
// step 3.
if ((w + d - theta * z) >= 0.0d)
{
return this.scale * y;
}
// step 4.
if (w > Math.log(z))
{
return this.scale * y;
}
counter++;
}
CategoryLogger.always().info("Gamma distribution -- 1000 tries for alpha>1.0");
return 1.0d;
}
else
// shape == 1.0
{
// Gamma(1.0, scale) ~ exponential with mean = scale
return -this.scale * Math.log(this.stream.nextDouble());
}
}
/** {@inheritDoc} */
@Override
public double getProbabilityDensity(final double x)
{
if (x <= 0)
{
return 0.0;
}
return (Math.pow(this.scale, -this.shape) * Math.pow(x, this.shape - 1) * Math.exp(-1 * x / this.scale))
/ ProbMath.gamma(this.shape);
}
/**
* @return alpha
*/
public double getShape()
{
return this.shape;
}
/**
* @return beta
*/
public double getScale()
{
return this.scale;
}
/** {@inheritDoc} */
@Override
public String toString()
{
return "Gamma(" + this.shape + "," + this.scale + ")";
}
}