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 }