View Javadoc
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    *      https://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  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  
23  package org.hipparchus.distribution.continuous;
24  
25  import org.hipparchus.exception.LocalizedCoreFormats;
26  import org.hipparchus.exception.MathIllegalArgumentException;
27  import org.hipparchus.util.FastMath;
28  import org.hipparchus.util.MathUtils;
29  
30  /**
31   * Implementation of the triangular real distribution.
32   *
33   * @see <a href="http://en.wikipedia.org/wiki/Triangular_distribution">
34   * Triangular distribution (Wikipedia)</a>
35   */
36  public class TriangularDistribution extends AbstractRealDistribution {
37      /** Serializable version identifier. */
38      private static final long serialVersionUID = 20120112L;
39      /** Lower limit of this distribution (inclusive). */
40      private final double a;
41      /** Upper limit of this distribution (inclusive). */
42      private final double b;
43      /** Mode of this distribution. */
44      private final double c;
45  
46      /**
47       * Creates a triangular real distribution using the given lower limit,
48       * upper limit, and mode.
49       *
50       * @param a Lower limit of this distribution (inclusive).
51       * @param b Upper limit of this distribution (inclusive).
52       * @param c Mode of this distribution.
53       * @throws MathIllegalArgumentException if {@code a >= b} or if {@code c > b}.
54       * @throws MathIllegalArgumentException if {@code c < a}.
55       */
56      public TriangularDistribution(double a, double c, double b)
57          throws MathIllegalArgumentException {
58          super();
59  
60          if (a >= b) {
61              throw new MathIllegalArgumentException(
62                              LocalizedCoreFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND,
63                              a, b, false);
64          }
65          if (c < a) {
66              throw new MathIllegalArgumentException(
67                      LocalizedCoreFormats.NUMBER_TOO_SMALL, c, a, true);
68          }
69          if (c > b) {
70              throw new MathIllegalArgumentException(
71                      LocalizedCoreFormats.NUMBER_TOO_LARGE, c, b, true);
72          }
73  
74          this.a = a;
75          this.c = c;
76          this.b = b;
77      }
78  
79      /**
80       * Returns the mode {@code c} of this distribution.
81       *
82       * @return the mode {@code c} of this distribution
83       */
84      public double getMode() {
85          return c;
86      }
87  
88      /**
89       * {@inheritDoc}
90       *
91       * For lower limit {@code a}, upper limit {@code b} and mode {@code c}, the
92       * PDF is given by
93       * <ul>
94       * <li>{@code 2 * (x - a) / [(b - a) * (c - a)]} if {@code a <= x < c},</li>
95       * <li>{@code 2 / (b - a)} if {@code x = c},</li>
96       * <li>{@code 2 * (b - x) / [(b - a) * (b - c)]} if {@code c < x <= b},</li>
97       * <li>{@code 0} otherwise.
98       * </ul>
99       */
100     @Override
101     public double density(double x) {
102         if (x < a) {
103             return 0;
104         }
105         if (a <= x && x < c) {
106             double divident = 2 * (x - a);
107             double divisor = (b - a) * (c - a);
108             return divident / divisor;
109         }
110         if (x == c) {
111             return 2 / (b - a);
112         }
113         if (c < x && x <= b) {
114             double divident = 2 * (b - x);
115             double divisor = (b - a) * (b - c);
116             return divident / divisor;
117         }
118         return 0;
119     }
120 
121     /**
122      * {@inheritDoc}
123      *
124      * For lower limit {@code a}, upper limit {@code b} and mode {@code c}, the
125      * CDF is given by
126      * <ul>
127      * <li>{@code 0} if {@code x < a},</li>
128      * <li>{@code (x - a)^2 / [(b - a) * (c - a)]} if {@code a <= x < c},</li>
129      * <li>{@code (c - a) / (b - a)} if {@code x = c},</li>
130      * <li>{@code 1 - (b - x)^2 / [(b - a) * (b - c)]} if {@code c < x <= b},</li>
131      * <li>{@code 1} if {@code x > b}.</li>
132      * </ul>
133      */
134     @Override
135     public double cumulativeProbability(double x)  {
136         if (x < a) {
137             return 0;
138         }
139         if (a <= x && x < c) {
140             double divident = (x - a) * (x - a);
141             double divisor = (b - a) * (c - a);
142             return divident / divisor;
143         }
144         if (x == c) {
145             return (c - a) / (b - a);
146         }
147         if (c < x && x <= b) {
148             double divident = (b - x) * (b - x);
149             double divisor = (b - a) * (b - c);
150             return 1 - (divident / divisor);
151         }
152         return 1;
153     }
154 
155     /**
156      * {@inheritDoc}
157      *
158      * For lower limit {@code a}, upper limit {@code b}, and mode {@code c},
159      * the mean is {@code (a + b + c) / 3}.
160      */
161     @Override
162     public double getNumericalMean() {
163         return (a + b + c) / 3;
164     }
165 
166     /**
167      * {@inheritDoc}
168      *
169      * For lower limit {@code a}, upper limit {@code b}, and mode {@code c},
170      * the variance is {@code (a^2 + b^2 + c^2 - a * b - a * c - b * c) / 18}.
171      */
172     @Override
173     public double getNumericalVariance() {
174         return (a * a + b * b + c * c - a * b - a * c - b * c) / 18;
175     }
176 
177     /**
178      * {@inheritDoc}
179      *
180      * The lower bound of the support is equal to the lower limit parameter
181      * {@code a} of the distribution.
182      *
183      * @return lower bound of the support
184      */
185     @Override
186     public double getSupportLowerBound() {
187         return a;
188     }
189 
190     /**
191      * {@inheritDoc}
192      *
193      * The upper bound of the support is equal to the upper limit parameter
194      * {@code b} of the distribution.
195      *
196      * @return upper bound of the support
197      */
198     @Override
199     public double getSupportUpperBound() {
200         return b;
201     }
202 
203     /**
204      * {@inheritDoc}
205      *
206      * The support of this distribution is connected.
207      *
208      * @return {@code true}
209      */
210     @Override
211     public boolean isSupportConnected() {
212         return true;
213     }
214 
215     /** {@inheritDoc} */
216     @Override
217     public double inverseCumulativeProbability(double p)
218         throws MathIllegalArgumentException {
219         MathUtils.checkRangeInclusive(p, 0, 1);
220 
221         if (p == 0) {
222             return a;
223         }
224         if (p == 1) {
225             return b;
226         }
227         if (p < (c - a) / (b - a)) {
228             return a + FastMath.sqrt(p * (b - a) * (c - a));
229         }
230         return b - FastMath.sqrt((1 - p) * (b - a) * (b - c));
231     }
232 }