DistNormalTrunc.java
- package org.opentrafficsim.core.dsol;
- import static nl.tudelft.simulation.jstats.distributions.DistNormal.CUMULATIVE_NORMAL_PROBABILITIES;
- import java.util.Locale;
- import nl.tudelft.simulation.jstats.distributions.DistContinuous;
- import nl.tudelft.simulation.jstats.streams.MersenneTwister;
- import nl.tudelft.simulation.jstats.streams.StreamInterface;
- /**
- * The Normal Truncated distribution. For more information on the normal distribution see
- * <a href="http://mathworld.wolfram.com/NormalDistribution.html"> http://mathworld.wolfram.com/NormalDistribution.html </a>
- * <p>
- * This version of the normal distribution uses the numerically approached inverse cumulative distribution.
- * <p>
- * (c) copyright 2002-2018 <a href="http://www.simulation.tudelft.nl">Delft University of Technology </a>, the Netherlands. <br>
- * See for project information <a href="http://www.simulation.tudelft.nl"> www.simulation.tudelft.nl </a> <br>
- * License of use: <a href="http://www.gnu.org/copyleft/lesser.html">Lesser General Public License (LGPL) </a>, no warranty.
- * @author <a href="mailto:a.verbraeck@tudelft.nl"> Alexander Verbraeck </a> <br>
- * <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
- */
- public class DistNormalTrunc extends DistContinuous
- {
- /** */
- private static final long serialVersionUID = 1L;
- /** mu refers to the mean of the normal distribution. */
- private final double mu;
- /** mu refers to the mean of the normal distribution. */
- private final double sigma;
- /** minimum x-value of the distribution. */
- private final double min;
- /** maximum x-value of the distribution. */
- private final double max;
- /** cumulative distribution value of the minimum. */
- private final double cumulMin;
- /** cumulative distribution value difference between max and min. */
- private final double cumulDiff;
- /** factor on probability density to normalize to 1. */
- private final double probDensFactor;
- /**
- * constructs a normal distribution with mu=0 and sigma=1. Errors of various types, e.g., in the impact point of a bomb;
- * quantities that are the sum of a large number of other quantities by the virtue of the central limit theorem.
- * @param stream StreamInterface; the numberstream
- * @param min double; minimum x-value of the distribution
- * @param max double; maximum x-value of the distribution
- */
- public DistNormalTrunc(final StreamInterface stream, final double min, final double max)
- {
- this(stream, 0.0, 1.0, min, max);
- }
- /**
- * constructs a normal distribution with mu and sigma.
- * @param stream StreamInterface; the numberstream
- * @param mu double; the medium
- * @param sigma double; the standard deviation
- * @param min double; minimum x-value of the distribution
- * @param max double; maximum x-value of the distribution
- */
- public DistNormalTrunc(final StreamInterface stream, final double mu, final double sigma, final double min,
- final double max)
- {
- super(stream);
- if (max < min)
- {
- throw new IllegalArgumentException("Error Normal Truncated - max < min");
- }
- this.mu = mu;
- this.sigma = sigma;
- this.min = min;
- this.max = max;
- this.cumulMin = getCumulativeProbabilityNotTruncated(min);
- this.cumulDiff = getCumulativeProbabilityNotTruncated(max) - this.cumulMin;
- this.probDensFactor = 1.0 / this.cumulDiff;
- }
- /** {@inheritDoc} */
- @Override
- public double draw()
- {
- return getInverseCumulativeProbabilityNotTruncated(this.cumulMin + this.cumulDiff * this.stream.nextDouble());
- }
- /**
- * returns the cumulative probability of the x-value.
- * @param x double; the observation x
- * @return double the cumulative probability
- */
- public double getCumulativeProbability(final double x)
- {
- if (x < this.min)
- {
- return 0.0;
- }
- if (x > this.max)
- {
- return 1.0;
- }
- return (getCumulativeProbabilityNotTruncated(x) - this.cumulMin) * this.probDensFactor;
- }
- /**
- * returns the cumulative probability of the x-value.
- * @param x double; the observation x
- * @return double the cumulative probability
- */
- private double getCumulativeProbabilityNotTruncated(final double x)
- {
- double z = (x - this.mu) / this.sigma * 100;
- double absZ = Math.abs(z);
- int intZ = (int) absZ;
- double f = 0.0;
- if (intZ >= 1000)
- {
- intZ = 999;
- f = 1.0;
- }
- else
- {
- f = absZ - intZ;
- }
- if (z >= 0)
- {
- return (1 - f) * CUMULATIVE_NORMAL_PROBABILITIES[intZ] + f * CUMULATIVE_NORMAL_PROBABILITIES[intZ + 1];
- }
- return 1 - ((1 - f) * CUMULATIVE_NORMAL_PROBABILITIES[intZ] + f * CUMULATIVE_NORMAL_PROBABILITIES[intZ + 1]);
- }
- /**
- * returns the x-value of the given cumulativePropability.
- * @param cumulativeProbability double; reflects cum prob
- * @return double the inverse cumulative probability
- */
- public double getInverseCumulativeProbability(final double cumulativeProbability)
- {
- if (cumulativeProbability < 0 || cumulativeProbability > 1)
- {
- throw new IllegalArgumentException("1<cumulativeProbability<0 ?");
- }
- /*
- * For extreme cases we return the min and max directly. The method getInverseCumulativeProbabilityNotTruncated() can
- * only return values from "mu - 10*sigma" to "mu + 10*sigma". If min or max is beyond these values, those values would
- * result erroneously. For any cumulative probability that is slightly above 0.0 or slightly below 1.0, values in the
- * range from "mu - 10*sigma" to "mu + 10*sigma" will always result.
- */
- if (cumulativeProbability == 0.0)
- {
- return this.min;
- }
- if (cumulativeProbability == 1.0)
- {
- return this.max;
- }
- return getInverseCumulativeProbabilityNotTruncated(this.cumulMin + cumulativeProbability * this.cumulDiff);
- }
- /** {@inheritDoc} */
- @Override
- public double probDensity(final double x)
- {
- if (x < this.min || x > this.max)
- {
- return 0.0;
- }
- return this.probDensFactor / (Math.sqrt(2 * Math.PI * Math.pow(this.sigma, 2)))
- * Math.exp(-1 * Math.pow(x - this.mu, 2) / (2 * Math.pow(this.sigma, 2)));
- }
- /** {@inheritDoc} */
- @Override
- public String toString()
- {
- return "NormalTrunc(" + this.mu + "," + this.sigma + "," + this.min + "," + this.max + ")";
- }
- /**
- * Test.
- * @param args String[]; args
- */
- public static void main(final String[] args)
- {
- StreamInterface stream = new MersenneTwister();
- double mu = 2.0;
- double sigma = 3.0;
- double min = -5.0;
- double max = 4.0;
- DistNormalTrunc dist = new DistNormalTrunc(stream, mu, sigma, min, max);
- System.out.println("<< probability density >>");
- double sum = 0.0;
- double step = (max - min) / 96;
- for (double x = min - 2 * step; x <= max + 2 * step; x += step)
- {
- double p = dist.probDensity(x);
- System.out.println(String.format(Locale.GERMAN, "%.8f;%.8f", x, p));
- sum += p * step;
- }
- System.out.println(String.format(Locale.GERMAN, "Approx. sum = %.8f", sum));
- System.out.println("");
- System.out.println("<< cumulative density >>");
- for (double x = min - 2 * step; x <= max + 2 * step; x += step)
- {
- double c = dist.getCumulativeProbability(x);
- System.out.println(String.format(Locale.GERMAN, "%.8f;%.8f", x, c));
- }
- System.out.println("");
- System.out.println("<< inverse cumulative density >>");
- for (double c = 0.0; c < 1.005; c += 0.01)
- {
- double x = dist.getInverseCumulativeProbability(Math.min(c, 1.0)); // want to include 1.0, also if 1.0000000000001
- System.out.println(String.format(Locale.GERMAN, "%.8f;%.8f", c, x));
- }
- System.out.println("");
- System.out.println("<< 10000 random numbers. >>");
- for (int i = 1; i < 10000; i++)
- {
- System.out.println(String.format(Locale.GERMAN, "%,8f", dist.draw()));
- }
- }
- /**
- * returns the x-value of the given cumulativePropability.
- * @param cumulativeProbability double; reflects cum prob
- * @return double the inverse cumulative probability
- */
- private double getInverseCumulativeProbabilityNotTruncated(final double cumulativeProbability)
- {
- if (cumulativeProbability < 0 || cumulativeProbability > 1)
- {
- throw new IllegalArgumentException("1<cumulativeProbability<0 ?");
- }
- boolean located = false;
- double prob = cumulativeProbability;
- if (cumulativeProbability < 0.5)
- {
- prob = 1 - cumulativeProbability;
- }
- int i = 0;
- double f = 0.0;
- while (!located)
- {
- if (CUMULATIVE_NORMAL_PROBABILITIES[i] < prob && CUMULATIVE_NORMAL_PROBABILITIES[i + 1] >= prob)
- {
- located = true;
- if (CUMULATIVE_NORMAL_PROBABILITIES[i] < CUMULATIVE_NORMAL_PROBABILITIES[i + 1])
- {
- f = (prob - CUMULATIVE_NORMAL_PROBABILITIES[i])
- / (CUMULATIVE_NORMAL_PROBABILITIES[i + 1] - CUMULATIVE_NORMAL_PROBABILITIES[i]);
- }
- }
- else
- {
- i++;
- }
- }
- if (cumulativeProbability < 0.5)
- {
- return this.mu - ((f + i) / 100.0) * this.sigma;
- }
- return ((f + i) / 100.0) * this.sigma + this.mu;
- }
- }