TriangularDistribution.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.math3.distribution;

  18. import org.apache.commons.math3.exception.NumberIsTooLargeException;
  19. import org.apache.commons.math3.exception.NumberIsTooSmallException;
  20. import org.apache.commons.math3.exception.OutOfRangeException;
  21. import org.apache.commons.math3.exception.util.LocalizedFormats;
  22. import org.apache.commons.math3.random.RandomGenerator;
  23. import org.apache.commons.math3.random.Well19937c;
  24. import org.apache.commons.math3.util.FastMath;

  25. /**
  26.  * Implementation of the triangular real distribution.
  27.  *
  28.  * @see <a href="http://en.wikipedia.org/wiki/Triangular_distribution">
  29.  * Triangular distribution (Wikipedia)</a>
  30.  *
  31.  * @since 3.0
  32.  */
  33. public class TriangularDistribution extends AbstractRealDistribution {
  34.     /** Serializable version identifier. */
  35.     private static final long serialVersionUID = 20120112L;
  36.     /** Lower limit of this distribution (inclusive). */
  37.     private final double a;
  38.     /** Upper limit of this distribution (inclusive). */
  39.     private final double b;
  40.     /** Mode of this distribution. */
  41.     private final double c;
  42.     /** Inverse cumulative probability accuracy. */
  43.     private final double solverAbsoluteAccuracy;

  44.     /**
  45.      * Creates a triangular real distribution using the given lower limit,
  46.      * upper limit, and mode.
  47.      * <p>
  48.      * <b>Note:</b> this constructor will implicitly create an instance of
  49.      * {@link Well19937c} as random generator to be used for sampling only (see
  50.      * {@link #sample()} and {@link #sample(int)}). In case no sampling is
  51.      * needed for the created distribution, it is advised to pass {@code null}
  52.      * as random generator via the appropriate constructors to avoid the
  53.      * additional initialisation overhead.
  54.      *
  55.      * @param a Lower limit of this distribution (inclusive).
  56.      * @param b Upper limit of this distribution (inclusive).
  57.      * @param c Mode of this distribution.
  58.      * @throws NumberIsTooLargeException if {@code a >= b} or if {@code c > b}.
  59.      * @throws NumberIsTooSmallException if {@code c < a}.
  60.      */
  61.     public TriangularDistribution(double a, double c, double b)
  62.         throws NumberIsTooLargeException, NumberIsTooSmallException {
  63.         this(new Well19937c(), a, c, b);
  64.     }

  65.     /**
  66.      * Creates a triangular distribution.
  67.      *
  68.      * @param rng Random number generator.
  69.      * @param a Lower limit of this distribution (inclusive).
  70.      * @param b Upper limit of this distribution (inclusive).
  71.      * @param c Mode of this distribution.
  72.      * @throws NumberIsTooLargeException if {@code a >= b} or if {@code c > b}.
  73.      * @throws NumberIsTooSmallException if {@code c < a}.
  74.      * @since 3.1
  75.      */
  76.     public TriangularDistribution(RandomGenerator rng,
  77.                                   double a,
  78.                                   double c,
  79.                                   double b)
  80.         throws NumberIsTooLargeException, NumberIsTooSmallException {
  81.         super(rng);

  82.         if (a >= b) {
  83.             throw new NumberIsTooLargeException(
  84.                             LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND,
  85.                             a, b, false);
  86.         }
  87.         if (c < a) {
  88.             throw new NumberIsTooSmallException(
  89.                     LocalizedFormats.NUMBER_TOO_SMALL, c, a, true);
  90.         }
  91.         if (c > b) {
  92.             throw new NumberIsTooLargeException(
  93.                     LocalizedFormats.NUMBER_TOO_LARGE, c, b, true);
  94.         }

  95.         this.a = a;
  96.         this.c = c;
  97.         this.b = b;
  98.         solverAbsoluteAccuracy = FastMath.max(FastMath.ulp(a), FastMath.ulp(b));
  99.     }

  100.     /**
  101.      * Returns the mode {@code c} of this distribution.
  102.      *
  103.      * @return the mode {@code c} of this distribution
  104.      */
  105.     public double getMode() {
  106.         return c;
  107.     }

  108.     /**
  109.      * {@inheritDoc}
  110.      *
  111.      * <p>
  112.      * For this distribution, the returned value is not really meaningful,
  113.      * since exact formulas are implemented for the computation of the
  114.      * {@link #inverseCumulativeProbability(double)} (no solver is invoked).
  115.      * </p>
  116.      * <p>
  117.      * For lower limit {@code a} and upper limit {@code b}, the current
  118.      * implementation returns {@code max(ulp(a), ulp(b)}.
  119.      * </p>
  120.      */
  121.     @Override
  122.     protected double getSolverAbsoluteAccuracy() {
  123.         return solverAbsoluteAccuracy;
  124.     }

  125.     /**
  126.      * {@inheritDoc}
  127.      *
  128.      * For lower limit {@code a}, upper limit {@code b} and mode {@code c}, the
  129.      * PDF is given by
  130.      * <ul>
  131.      * <li>{@code 2 * (x - a) / [(b - a) * (c - a)]} if {@code a <= x < c},</li>
  132.      * <li>{@code 2 / (b - a)} if {@code x = c},</li>
  133.      * <li>{@code 2 * (b - x) / [(b - a) * (b - c)]} if {@code c < x <= b},</li>
  134.      * <li>{@code 0} otherwise.
  135.      * </ul>
  136.      */
  137.     public double density(double x) {
  138.         if (x < a) {
  139.             return 0;
  140.         }
  141.         if (a <= x && x < c) {
  142.             double divident = 2 * (x - a);
  143.             double divisor = (b - a) * (c - a);
  144.             return divident / divisor;
  145.         }
  146.         if (x == c) {
  147.             return 2 / (b - a);
  148.         }
  149.         if (c < x && x <= b) {
  150.             double divident = 2 * (b - x);
  151.             double divisor = (b - a) * (b - c);
  152.             return divident / divisor;
  153.         }
  154.         return 0;
  155.     }

  156.     /**
  157.      * {@inheritDoc}
  158.      *
  159.      * For lower limit {@code a}, upper limit {@code b} and mode {@code c}, the
  160.      * CDF is given by
  161.      * <ul>
  162.      * <li>{@code 0} if {@code x < a},</li>
  163.      * <li>{@code (x - a)^2 / [(b - a) * (c - a)]} if {@code a <= x < c},</li>
  164.      * <li>{@code (c - a) / (b - a)} if {@code x = c},</li>
  165.      * <li>{@code 1 - (b - x)^2 / [(b - a) * (b - c)]} if {@code c < x <= b},</li>
  166.      * <li>{@code 1} if {@code x > b}.</li>
  167.      * </ul>
  168.      */
  169.     public double cumulativeProbability(double x)  {
  170.         if (x < a) {
  171.             return 0;
  172.         }
  173.         if (a <= x && x < c) {
  174.             double divident = (x - a) * (x - a);
  175.             double divisor = (b - a) * (c - a);
  176.             return divident / divisor;
  177.         }
  178.         if (x == c) {
  179.             return (c - a) / (b - a);
  180.         }
  181.         if (c < x && x <= b) {
  182.             double divident = (b - x) * (b - x);
  183.             double divisor = (b - a) * (b - c);
  184.             return 1 - (divident / divisor);
  185.         }
  186.         return 1;
  187.     }

  188.     /**
  189.      * {@inheritDoc}
  190.      *
  191.      * For lower limit {@code a}, upper limit {@code b}, and mode {@code c},
  192.      * the mean is {@code (a + b + c) / 3}.
  193.      */
  194.     public double getNumericalMean() {
  195.         return (a + b + c) / 3;
  196.     }

  197.     /**
  198.      * {@inheritDoc}
  199.      *
  200.      * For lower limit {@code a}, upper limit {@code b}, and mode {@code c},
  201.      * the variance is {@code (a^2 + b^2 + c^2 - a * b - a * c - b * c) / 18}.
  202.      */
  203.     public double getNumericalVariance() {
  204.         return (a * a + b * b + c * c - a * b - a * c - b * c) / 18;
  205.     }

  206.     /**
  207.      * {@inheritDoc}
  208.      *
  209.      * The lower bound of the support is equal to the lower limit parameter
  210.      * {@code a} of the distribution.
  211.      *
  212.      * @return lower bound of the support
  213.      */
  214.     public double getSupportLowerBound() {
  215.         return a;
  216.     }

  217.     /**
  218.      * {@inheritDoc}
  219.      *
  220.      * The upper bound of the support is equal to the upper limit parameter
  221.      * {@code b} of the distribution.
  222.      *
  223.      * @return upper bound of the support
  224.      */
  225.     public double getSupportUpperBound() {
  226.         return b;
  227.     }

  228.     /** {@inheritDoc} */
  229.     public boolean isSupportLowerBoundInclusive() {
  230.         return true;
  231.     }

  232.     /** {@inheritDoc} */
  233.     public boolean isSupportUpperBoundInclusive() {
  234.         return true;
  235.     }

  236.     /**
  237.      * {@inheritDoc}
  238.      *
  239.      * The support of this distribution is connected.
  240.      *
  241.      * @return {@code true}
  242.      */
  243.     public boolean isSupportConnected() {
  244.         return true;
  245.     }

  246.     @Override
  247.     public double inverseCumulativeProbability(double p)
  248.         throws OutOfRangeException {
  249.         if (p < 0 || p > 1) {
  250.             throw new OutOfRangeException(p, 0, 1);
  251.         }
  252.         if (p == 0) {
  253.             return a;
  254.         }
  255.         if (p == 1) {
  256.             return b;
  257.         }
  258.         if (p < (c - a) / (b - a)) {
  259.             return a + FastMath.sqrt(p * (b - a) * (c - a));
  260.         }
  261.         return b - FastMath.sqrt((1 - p) * (b - a) * (b - c));
  262.     }
  263. }