1 package org.opentrafficsim.core.dsol;
2
3 import static nl.tudelft.simulation.jstats.distributions.DistNormal.CUMULATIVE_NORMAL_PROBABILITIES;
4
5 import java.util.Locale;
6
7 import nl.tudelft.simulation.jstats.distributions.DistContinuous;
8 import nl.tudelft.simulation.jstats.streams.MersenneTwister;
9 import nl.tudelft.simulation.jstats.streams.StreamInterface;
10
11
12
13
14
15
16
17
18
19
20
21
22
23 public class DistNormalTrunc extends DistContinuous
24 {
25
26 private static final long serialVersionUID = 1L;
27
28
29 private final double mu;
30
31
32 private final double sigma;
33
34
35 private final double min;
36
37
38 private final double max;
39
40
41 private final double cumulMin;
42
43
44 private final double cumulDiff;
45
46
47 private final double probDensFactor;
48
49
50
51
52
53
54
55
56 public DistNormalTrunc(final StreamInterface stream, final double min, final double max)
57 {
58 this(stream, 0.0, 1.0, min, max);
59 }
60
61
62
63
64
65
66
67
68
69 public DistNormalTrunc(final StreamInterface stream, final double mu, final double sigma, final double min,
70 final double max)
71 {
72 super(stream);
73 if (max < min)
74 {
75 throw new IllegalArgumentException("Error Normal Truncated - max < min");
76 }
77 this.mu = mu;
78 this.sigma = sigma;
79 this.min = min;
80 this.max = max;
81 this.cumulMin = getCumulativeProbabilityNotTruncated(min);
82 this.cumulDiff = getCumulativeProbabilityNotTruncated(max) - this.cumulMin;
83 this.probDensFactor = 1.0 / this.cumulDiff;
84 }
85
86
87 @Override
88 public double draw()
89 {
90 return getInverseCumulativeProbabilityNotTruncated(this.cumulMin + this.cumulDiff * this.stream.nextDouble());
91 }
92
93
94
95
96
97
98 public double getCumulativeProbability(final double x)
99 {
100 if (x < this.min)
101 {
102 return 0.0;
103 }
104 if (x > this.max)
105 {
106 return 1.0;
107 }
108 return (getCumulativeProbabilityNotTruncated(x) - this.cumulMin) * this.probDensFactor;
109 }
110
111
112
113
114
115
116 private double getCumulativeProbabilityNotTruncated(final double x)
117 {
118 double z = (x - this.mu) / this.sigma * 100;
119 double absZ = Math.abs(z);
120 int intZ = (int) absZ;
121 double f = 0.0;
122 if (intZ >= 1000)
123 {
124 intZ = 999;
125 f = 1.0;
126 }
127 else
128 {
129 f = absZ - intZ;
130 }
131 if (z >= 0)
132 {
133 return (1 - f) * CUMULATIVE_NORMAL_PROBABILITIES[intZ] + f * CUMULATIVE_NORMAL_PROBABILITIES[intZ + 1];
134 }
135 return 1 - ((1 - f) * CUMULATIVE_NORMAL_PROBABILITIES[intZ] + f * CUMULATIVE_NORMAL_PROBABILITIES[intZ + 1]);
136 }
137
138
139
140
141
142
143 public double getInverseCumulativeProbability(final double cumulativeProbability)
144 {
145 if (cumulativeProbability < 0 || cumulativeProbability > 1)
146 {
147 throw new IllegalArgumentException("1<cumulativeProbability<0 ?");
148 }
149
150
151
152
153
154
155 if (cumulativeProbability == 0.0)
156 {
157 return this.min;
158 }
159 if (cumulativeProbability == 1.0)
160 {
161 return this.max;
162 }
163 return getInverseCumulativeProbabilityNotTruncated(this.cumulMin + cumulativeProbability * this.cumulDiff);
164 }
165
166
167 @Override
168 public double probDensity(final double x)
169 {
170 if (x < this.min || x > this.max)
171 {
172 return 0.0;
173 }
174 return this.probDensFactor / (Math.sqrt(2 * Math.PI * Math.pow(this.sigma, 2)))
175 * Math.exp(-1 * Math.pow(x - this.mu, 2) / (2 * Math.pow(this.sigma, 2)));
176 }
177
178
179 @Override
180 public String toString()
181 {
182 return "NormalTrunc(" + this.mu + "," + this.sigma + "," + this.min + "," + this.max + ")";
183 }
184
185
186
187
188
189 public static void main(final String[] args)
190 {
191 StreamInterface stream = new MersenneTwister();
192 double mu = 2.0;
193 double sigma = 3.0;
194 double min = -5.0;
195 double max = 4.0;
196 DistNormalTrunc dist = new DistNormalTrunc(stream, mu, sigma, min, max);
197
198 System.out.println("<< probability density >>");
199 double sum = 0.0;
200 double step = (max - min) / 96;
201 for (double x = min - 2 * step; x <= max + 2 * step; x += step)
202 {
203 double p = dist.probDensity(x);
204 System.out.println(String.format(Locale.GERMAN, "%.8f;%.8f", x, p));
205 sum += p * step;
206 }
207 System.out.println(String.format(Locale.GERMAN, "Approx. sum = %.8f", sum));
208 System.out.println("");
209
210 System.out.println("<< cumulative density >>");
211 for (double x = min - 2 * step; x <= max + 2 * step; x += step)
212 {
213 double c = dist.getCumulativeProbability(x);
214 System.out.println(String.format(Locale.GERMAN, "%.8f;%.8f", x, c));
215 }
216 System.out.println("");
217
218 System.out.println("<< inverse cumulative density >>");
219 for (double c = 0.0; c < 1.005; c += 0.01)
220 {
221 double x = dist.getInverseCumulativeProbability(Math.min(c, 1.0));
222 System.out.println(String.format(Locale.GERMAN, "%.8f;%.8f", c, x));
223 }
224 System.out.println("");
225
226 System.out.println("<< 10000 random numbers. >>");
227 for (int i = 1; i < 10000; i++)
228 {
229 System.out.println(String.format(Locale.GERMAN, "%,8f", dist.draw()));
230 }
231
232 }
233
234
235
236
237
238
239 private double getInverseCumulativeProbabilityNotTruncated(final double cumulativeProbability)
240 {
241 if (cumulativeProbability < 0 || cumulativeProbability > 1)
242 {
243 throw new IllegalArgumentException("1<cumulativeProbability<0 ?");
244 }
245 boolean located = false;
246 double prob = cumulativeProbability;
247 if (cumulativeProbability < 0.5)
248 {
249 prob = 1 - cumulativeProbability;
250 }
251 int i = 0;
252 double f = 0.0;
253 while (!located)
254 {
255 if (CUMULATIVE_NORMAL_PROBABILITIES[i] < prob && CUMULATIVE_NORMAL_PROBABILITIES[i + 1] >= prob)
256 {
257 located = true;
258 if (CUMULATIVE_NORMAL_PROBABILITIES[i] < CUMULATIVE_NORMAL_PROBABILITIES[i + 1])
259 {
260 f = (prob - CUMULATIVE_NORMAL_PROBABILITIES[i])
261 / (CUMULATIVE_NORMAL_PROBABILITIES[i + 1] - CUMULATIVE_NORMAL_PROBABILITIES[i]);
262 }
263 }
264 else
265 {
266 i++;
267 }
268 }
269 if (cumulativeProbability < 0.5)
270 {
271 return this.mu - ((f + i) / 100.0) * this.sigma;
272 }
273 return ((f + i) / 100.0) * this.sigma + this.mu;
274 }
275
276 }