1 package org.opentrafficsim.core.distributions;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 import nl.tudelft.simulation.jstats.distributions.DistUniform;
7 import nl.tudelft.simulation.jstats.streams.StreamInterface;
8
9
10
11
12
13
14
15
16
17
18
19 public class Distribution<O> implements Generator<O>
20 {
21
22 private final List<FrequencyAndObject<O>> generators = new ArrayList<>();
23
24
25 private double cumulativeTotal;
26
27
28 private final DistUniform random;
29
30
31
32
33
34
35
36 public Distribution(final List<FrequencyAndObject<O>> generators, final StreamInterface stream)
37 throws ProbabilityException
38 {
39 this(stream);
40 ProbabilityException.failIf(null == generators, "generators may not be null");
41
42
43 this.generators.addAll(generators);
44 fixProbabilities();
45 }
46
47
48
49
50
51
52 public Distribution(final StreamInterface stream) throws ProbabilityException
53 {
54 ProbabilityException.failIf(null == stream, "random stream may not be null");
55 this.random = new DistUniform(stream, 0, 1);
56 }
57
58
59
60
61
62 private void fixProbabilities() throws ProbabilityException
63 {
64 if (0 == this.generators.size())
65 {
66 return;
67 }
68 this.cumulativeTotal = 0;
69 for (FrequencyAndObject<O> generator : this.generators)
70 {
71 double frequency = generator.getFrequency();
72 ProbabilityException.failIf(frequency < 0, "Negative frequency or probability is not allowed (got "
73 + frequency + ")");
74 this.cumulativeTotal += frequency;
75 }
76 }
77
78
79 public final O draw() throws ProbabilityException
80 {
81 ProbabilityException.failIf(0 == this.generators.size(), "Cannot draw from empty collection");
82 ProbabilityException.failIf(0 == this.cumulativeTotal, "Sum of frequencies or probabilities must be > 0");
83
84 double randomValue = this.random.draw() * this.cumulativeTotal;
85 for (FrequencyAndObject<O> fAndO : this.generators)
86 {
87 double frequency = fAndO.getFrequency();
88 if (frequency >= randomValue)
89 {
90 return fAndO.getObject();
91 }
92 randomValue -= frequency;
93 }
94
95 FrequencyAndObject<O> useThisOne = this.generators.get(0);
96 for (FrequencyAndObject<O> fAndO : this.generators)
97 {
98 if (fAndO.getFrequency() > 0)
99 {
100 useThisOne = fAndO;
101 break;
102 }
103 }
104 return useThisOne.getObject();
105 }
106
107
108
109
110
111
112
113 public final Distribution<O> add(final FrequencyAndObject<O> generator) throws ProbabilityException
114 {
115 return add(this.generators.size(), generator);
116 }
117
118
119
120
121
122
123
124
125 public final Distribution<O> add(final int index, final FrequencyAndObject<O> generator)
126 throws ProbabilityException
127 {
128 ProbabilityException.failIf(generator.getFrequency() < 0, "frequency (or probability) must be >= 0 (got "
129 + generator.getFrequency() + ")");
130 this.generators.add(index, generator);
131 fixProbabilities();
132 return this;
133 }
134
135
136
137
138
139
140
141
142 public final Distribution<O> remove(final int index) throws IndexOutOfBoundsException, ProbabilityException
143 {
144 this.generators.remove(index);
145 fixProbabilities();
146 return this;
147 }
148
149
150
151
152
153
154
155
156 public final Distribution<O> set(final int index, final FrequencyAndObject<O> generator)
157 throws ProbabilityException
158 {
159 ProbabilityException.failIf(generator.getFrequency() < 0, "frequency (or probability) must be >= 0 (got "
160 + generator.getFrequency() + ")");
161 try
162 {
163 this.generators.set(index, generator);
164 }
165 catch (IndexOutOfBoundsException ie)
166 {
167 throw new ProbabilityException("Index out of bounds for set operation, index=" + index);
168 }
169 fixProbabilities();
170 return this;
171 }
172
173
174
175
176
177
178
179
180 public final Distribution<O> modifyFrequency(final int index, final double frequency) throws ProbabilityException
181 {
182 return set(index, new FrequencyAndObject<O>(frequency, this.generators.get(index).getObject()));
183 }
184
185
186
187
188
189 public final Distribution<O> clear()
190 {
191 this.generators.clear();
192 return this;
193 }
194
195
196
197
198
199
200
201 public final FrequencyAndObject<O> get(final int index) throws ProbabilityException
202 {
203 try
204 {
205 return this.generators.get(index);
206 }
207 catch (IndexOutOfBoundsException ie)
208 {
209 throw new ProbabilityException("Index out of bounds for set operation, index=" + index);
210 }
211 }
212
213
214
215
216
217 public final int size()
218 {
219 return this.generators.size();
220 }
221
222
223 @Override
224 public final int hashCode()
225 {
226 final int prime = 31;
227 int result = 1;
228 long temp;
229 temp = Double.doubleToLongBits(this.cumulativeTotal);
230 result = prime * result + (int) (temp ^ (temp >>> 32));
231 result = prime * result + ((this.generators == null) ? 0 : this.generators.hashCode());
232 result = prime * result + ((this.random == null) ? 0 : this.random.hashCode());
233 return result;
234 }
235
236
237 @Override
238 @SuppressWarnings("checkstyle:needbraces")
239 public final boolean equals(final Object obj)
240 {
241 if (this == obj)
242 return true;
243 if (obj == null)
244 return false;
245 if (getClass() != obj.getClass())
246 return false;
247 Distribution<?> other = (Distribution<?>) obj;
248 if (Double.doubleToLongBits(this.cumulativeTotal) != Double.doubleToLongBits(other.cumulativeTotal))
249 return false;
250 if (this.generators == null)
251 {
252 if (other.generators != null)
253 return false;
254 }
255 else if (!this.generators.equals(other.generators))
256 return false;
257 if (this.random == null)
258 {
259 if (other.random != null)
260 return false;
261 }
262 else if (!this.random.equals(other.random))
263 return false;
264 return true;
265 }
266
267
268 public final String toString()
269 {
270 StringBuilder result = new StringBuilder();
271 result.append("Distribution [");
272 String separator = "";
273 for (FrequencyAndObject<O> fAndO : this.generators)
274 {
275 result.append(separator + fAndO.getFrequency() + "->" + fAndO.getObject());
276 separator = ", ";
277 }
278 result.append(']');
279 return result.toString();
280 }
281
282
283
284
285
286
287
288
289
290
291
292
293
294 public static class FrequencyAndObject<O>
295 {
296
297 private final double frequency;
298
299
300 private final O object;
301
302
303
304
305
306
307 public FrequencyAndObject(final double frequency, final O object)
308 {
309 this.frequency = frequency;
310 this.object = object;
311 }
312
313
314
315
316
317 public final double getFrequency()
318 {
319 return this.frequency;
320 }
321
322
323
324
325
326 public final O getObject()
327 {
328 return this.object;
329 }
330
331
332 @Override
333 public final int hashCode()
334 {
335 final int prime = 31;
336 int result = 1;
337 long temp;
338 temp = Double.doubleToLongBits(this.frequency);
339 result = prime * result + (int) (temp ^ (temp >>> 32));
340 result = prime * result + ((this.object == null) ? 0 : this.object.hashCode());
341 return result;
342 }
343
344
345 @Override
346 @SuppressWarnings("checkstyle:needbraces")
347 public final boolean equals(final Object obj)
348 {
349 if (this == obj)
350 return true;
351 if (obj == null)
352 return false;
353 if (getClass() != obj.getClass())
354 return false;
355 FrequencyAndObject<?> other = (FrequencyAndObject<?>) obj;
356 if (Double.doubleToLongBits(this.frequency) != Double.doubleToLongBits(other.frequency))
357 return false;
358 if (this.object == null)
359 {
360 if (other.object != null)
361 return false;
362 }
363 else if (!this.object.equals(other.object))
364 return false;
365 return true;
366 }
367
368
369 @Override
370 public final String toString()
371 {
372 return "FrequencyAndObject [frequency=" + this.frequency + ", object=" + this.object + "]";
373 }
374
375 }
376
377 }