1 | /* |
---|
2 | Copyright 2006 by Sean Luke |
---|
3 | Licensed under the Academic Free License version 3.0 |
---|
4 | See the file "LICENSE" for more information |
---|
5 | */ |
---|
6 | |
---|
7 | |
---|
8 | package ec.util; |
---|
9 | |
---|
10 | /* |
---|
11 | * RandomChoice.java |
---|
12 | * |
---|
13 | * Created: January 5, 2001 |
---|
14 | * By: Sean Luke |
---|
15 | * |
---|
16 | */ |
---|
17 | |
---|
18 | /** |
---|
19 | * RandomChoice organizes arrays of floats into distributions which can |
---|
20 | * be used to pick randomly from. You can provide three kinds of arrays: |
---|
21 | * |
---|
22 | <ul> |
---|
23 | <li> An array of floats |
---|
24 | <li> An array of doubles |
---|
25 | <li> An array of arbitrary objects, plus a RandomChoiceChooser which knows |
---|
26 | how to get and set the appropriate "float" value of objects in this array. |
---|
27 | </ul> |
---|
28 | * |
---|
29 | * <p>Before the RandomChoice can pick randomly from your array, it must first |
---|
30 | * organize it. It does this by doing the following. First, it normalizes the |
---|
31 | * values in the array. Then it modifies them to their sums. That is, each item i |
---|
32 | * in the array is set to the sum of the original values for items 0...i. If you |
---|
33 | * cannot allow your objects to be modified, then this is not the class for you. |
---|
34 | * |
---|
35 | * <p>An array is valid if (1) it has no negative values and (2) not all of its |
---|
36 | * values are zero. This RandomChoice code <i>should</i> (I hope) guarantee that |
---|
37 | * an element of zero probability is never returned. RandomChoice uses a binary |
---|
38 | * search to find your index, followed by linear probing (marching up or down |
---|
39 | * the list) to find the first non-zero probability item in the vacinity of that |
---|
40 | * index. As long as there are not a whole lot of zero-valued items in a row, |
---|
41 | * RandomChoice is efficient. |
---|
42 | * |
---|
43 | * You organize your array with organizeDistribution(). Then you can have the |
---|
44 | * RandomChoice pick random items from the array and return their indexes to you. |
---|
45 | * You do this by calling pickFromDistribution(), passing it a random floating |
---|
46 | * point value between 0.0 and 1.0. You call organizeDistribution() only once; |
---|
47 | * after which you may call pickFromDistribution() as many times as you like. |
---|
48 | * You should not modify the array thereafter. |
---|
49 | * |
---|
50 | * @author Sean Luke |
---|
51 | * @version 1.0 |
---|
52 | */ |
---|
53 | |
---|
54 | public class RandomChoice |
---|
55 | { |
---|
56 | |
---|
57 | /** Same as organizeDistribution(probabilities, <b>false</b>); */ |
---|
58 | public static void organizeDistribution(final float[] probabilities) |
---|
59 | { |
---|
60 | organizeDistribution(probabilities, false); |
---|
61 | } |
---|
62 | |
---|
63 | /** Normalizes probabilities, then converts them into continuing |
---|
64 | sums. This prepares them for being usable in pickFromDistribution. |
---|
65 | If the probabilities are all 0, then selection is uniform, unless allowAllZeros |
---|
66 | is false, in which case an ArithmeticException is thrown. If any of them are negative, |
---|
67 | or if the distribution is empty, then an ArithmeticException is thrown. |
---|
68 | For example, |
---|
69 | {0.6, 0.4, 0.2, 0.8} -> {0.3, 0.2, 0.1, 0.4} -> {0.3, 0.5, 0.6, 1.0} */ |
---|
70 | |
---|
71 | public static void organizeDistribution(final float[] probabilities, final boolean allowAllZeros) |
---|
72 | { |
---|
73 | // first normalize |
---|
74 | double sum=0.0; |
---|
75 | if (probabilities.length == 0) |
---|
76 | throw new ArithmeticException("Distribution has no elements"); |
---|
77 | |
---|
78 | for(int x=0;x<probabilities.length;x++) |
---|
79 | { |
---|
80 | if (probabilities[x]<0.0) |
---|
81 | throw new ArithmeticException("Distribution has negative probabilities"); |
---|
82 | sum += probabilities[x]; |
---|
83 | } |
---|
84 | |
---|
85 | if (sum==0.0) |
---|
86 | if (!allowAllZeros) |
---|
87 | throw new ArithmeticException("Distribution has all zero probabilities"); |
---|
88 | else |
---|
89 | { |
---|
90 | for(int x=0;x<probabilities.length;x++) |
---|
91 | probabilities[x] = 1.0f; |
---|
92 | sum = probabilities.length; |
---|
93 | } |
---|
94 | |
---|
95 | for(int x=0;x<probabilities.length;x++) |
---|
96 | probabilities[x] /= sum; |
---|
97 | |
---|
98 | // now sum |
---|
99 | sum=0.0; |
---|
100 | for(int x=0;x<probabilities.length;x++) |
---|
101 | { |
---|
102 | sum += probabilities[x]; |
---|
103 | probabilities[x] = (float)sum; |
---|
104 | } |
---|
105 | |
---|
106 | // now we need to work backwards setting 0 values |
---|
107 | int x; |
---|
108 | for(x=probabilities.length-1; x > 0; x--) |
---|
109 | if (probabilities[x]==probabilities[x-1]) // we're 0.0 |
---|
110 | probabilities[x] = 1.0f; |
---|
111 | else break; |
---|
112 | probabilities[x] = 1.0f; |
---|
113 | } |
---|
114 | |
---|
115 | /** Same as organizeDistribution(probabilities, <b>false</b>); */ |
---|
116 | public static void organizeDistribution(final double[] probabilities) |
---|
117 | { |
---|
118 | organizeDistribution(probabilities, false); |
---|
119 | } |
---|
120 | |
---|
121 | /** Normalizes probabilities, then converts them into continuing |
---|
122 | sums. This prepares them for being usable in pickFromDistribution. |
---|
123 | If the probabilities are all 0, then selection is uniform, unless allowAllZeros |
---|
124 | is false, in which case an ArithmeticException is thrown. If any of them are negative, |
---|
125 | or if the distribution is empty, then an ArithmeticException is thrown. |
---|
126 | For example, |
---|
127 | {0.6, 0.4, 0.2, 0.8} -> {0.3, 0.2, 0.1, 0.4} -> {0.3, 0.5, 0.6, 1.0} */ |
---|
128 | |
---|
129 | public static void organizeDistribution(final double[] probabilities, final boolean allowAllZeros) |
---|
130 | { |
---|
131 | // first normalize |
---|
132 | double sum=0.0; |
---|
133 | |
---|
134 | if (probabilities.length == 0) |
---|
135 | throw new ArithmeticException("Distribution has no elements"); |
---|
136 | |
---|
137 | for(int x=0;x<probabilities.length;x++) |
---|
138 | { |
---|
139 | if (probabilities[x]<0.0) |
---|
140 | throw new ArithmeticException("Distribution has negative probabilities"); |
---|
141 | sum += probabilities[x]; |
---|
142 | } |
---|
143 | |
---|
144 | if (sum==0.0) |
---|
145 | if (!allowAllZeros) |
---|
146 | throw new ArithmeticException("Distribution has all zero probabilities"); |
---|
147 | else |
---|
148 | { |
---|
149 | for(int x=0;x<probabilities.length;x++) |
---|
150 | probabilities[x] = 1.0; |
---|
151 | sum = probabilities.length; |
---|
152 | } |
---|
153 | |
---|
154 | for(int x=0;x<probabilities.length;x++) |
---|
155 | probabilities[x] /= sum; |
---|
156 | |
---|
157 | // now sum |
---|
158 | sum=0.0; |
---|
159 | for(int x=0;x<probabilities.length;x++) |
---|
160 | { |
---|
161 | sum += probabilities[x]; |
---|
162 | probabilities[x] = sum; |
---|
163 | } |
---|
164 | |
---|
165 | // now we need to work backwards setting 0 values |
---|
166 | int x; |
---|
167 | for(x=probabilities.length-1; x > 0; x--) |
---|
168 | if (probabilities[x]==probabilities[x-1]) // we're 0.0 |
---|
169 | probabilities[x] = 1.0; |
---|
170 | else break; |
---|
171 | probabilities[x] = 1.0; |
---|
172 | |
---|
173 | } |
---|
174 | |
---|
175 | /** Same as organizeDistribution(objs, chooser, <b>false</b>); */ |
---|
176 | public static void organizeDistribution(final Object[] objs, |
---|
177 | final RandomChoiceChooser chooser) |
---|
178 | { |
---|
179 | organizeDistribution(objs,chooser, false); |
---|
180 | } |
---|
181 | |
---|
182 | /** Normalizes the probabilities associated |
---|
183 | with an array of objects, then converts them into continuing |
---|
184 | sums. This prepares them for being usable in pickFromDistribution. |
---|
185 | If the probabilities are all 0, then selection is uniform, unless allowAllZeros |
---|
186 | is false, in which case an ArithmeticException is thrown. If any of them are negative, |
---|
187 | or if the distribution is empty, then an ArithmeticException is thrown. |
---|
188 | For example, |
---|
189 | {0.6, 0.4, 0.2, 0.8} -> {0.3, 0.2, 0.1, 0.4} -> {0.3, 0.5, 0.6, 1.0} |
---|
190 | The probabilities are retrieved and set using chooser.*/ |
---|
191 | |
---|
192 | public static void organizeDistribution(final Object[] objs, |
---|
193 | final RandomChoiceChooser chooser, final boolean allowAllZeros) |
---|
194 | { |
---|
195 | // first normalize |
---|
196 | double sum=0.0; |
---|
197 | |
---|
198 | if (objs.length == 0) |
---|
199 | throw new ArithmeticException("Distribution has no elements"); |
---|
200 | |
---|
201 | for(int x=0;x<objs.length;x++) |
---|
202 | { |
---|
203 | if (chooser.getProbability(objs[x])<0.0) |
---|
204 | throw new ArithmeticException("Distribution has negative probabilities"); |
---|
205 | sum += chooser.getProbability(objs[x]); |
---|
206 | } |
---|
207 | |
---|
208 | if (sum==0.0) |
---|
209 | if (!allowAllZeros) |
---|
210 | throw new ArithmeticException("Distribution has all zero probabilities"); |
---|
211 | else |
---|
212 | { |
---|
213 | for(int x=0;x<objs.length;x++) |
---|
214 | chooser.setProbability(objs[x], 1.0f); |
---|
215 | sum = objs.length; |
---|
216 | } |
---|
217 | |
---|
218 | for(int x=0;x<objs.length;x++) |
---|
219 | chooser.setProbability(objs[x], |
---|
220 | (float)(chooser.getProbability(objs[x]) / sum)); |
---|
221 | |
---|
222 | // now sum |
---|
223 | sum=0.0; |
---|
224 | for(int x=0;x<objs.length;x++) |
---|
225 | { |
---|
226 | sum += chooser.getProbability(objs[x]); |
---|
227 | chooser.setProbability(objs[x],(float)sum); |
---|
228 | } |
---|
229 | |
---|
230 | // now we need to work backwards setting 0 values |
---|
231 | int x; |
---|
232 | for(x=objs.length-1; x > 0; x--) |
---|
233 | if (chooser.getProbability(objs[x])== |
---|
234 | chooser.getProbability(objs[x-1])) // we're 0.0 |
---|
235 | chooser.setProbability(objs[x],1.0f); |
---|
236 | else break; |
---|
237 | chooser.setProbability(objs[x],1.0f); |
---|
238 | } |
---|
239 | |
---|
240 | /** Same as organizeDistribution(objs, chooser, <b>false</b>); */ |
---|
241 | public static void organizeDistribution(final Object[] objs, |
---|
242 | final RandomChoiceChooserD chooser) |
---|
243 | { |
---|
244 | organizeDistribution(objs,chooser, false); |
---|
245 | } |
---|
246 | |
---|
247 | /** Normalizes the probabilities associated |
---|
248 | with an array of objects, then converts them into continuing |
---|
249 | sums. This prepares them for being usable in pickFromDistribution. |
---|
250 | If the probabilities are all 0, then selection is uniform, unless allowAllZeros |
---|
251 | is false, in which case an ArithmeticException is thrown. If any of them are negative, |
---|
252 | or if the distribution is empty, then an ArithmeticException is thrown. |
---|
253 | For example, |
---|
254 | {0.6, 0.4, 0.2, 0.8} -> {0.3, 0.2, 0.1, 0.4} -> {0.3, 0.5, 0.6, 1.0} |
---|
255 | The probabilities are retrieved and set using chooser.*/ |
---|
256 | |
---|
257 | public static void organizeDistribution(final Object[] objs, |
---|
258 | final RandomChoiceChooserD chooser, final boolean allowAllZeros) |
---|
259 | { |
---|
260 | // first normalize |
---|
261 | double sum=0.0; |
---|
262 | |
---|
263 | if (objs.length == 0) |
---|
264 | throw new ArithmeticException("Distribution has no elements"); |
---|
265 | |
---|
266 | for(int x=0;x<objs.length;x++) |
---|
267 | { |
---|
268 | if (chooser.getProbability(objs[x])<0.0) |
---|
269 | throw new ArithmeticException("Distribution has negative probabilities"); |
---|
270 | sum += chooser.getProbability(objs[x]); |
---|
271 | } |
---|
272 | |
---|
273 | if (sum==0.0) |
---|
274 | if (!allowAllZeros) |
---|
275 | throw new ArithmeticException("Distribution has all zero probabilities"); |
---|
276 | else |
---|
277 | { |
---|
278 | for(int x=0;x<objs.length;x++) |
---|
279 | chooser.setProbability(objs[x], 1.0); |
---|
280 | sum = objs.length; |
---|
281 | } |
---|
282 | |
---|
283 | for(int x=0;x<objs.length;x++) |
---|
284 | chooser.setProbability(objs[x], |
---|
285 | (double)(chooser.getProbability(objs[x]) / sum)); |
---|
286 | |
---|
287 | // now sum |
---|
288 | sum=0.0; |
---|
289 | for(int x=0;x<objs.length;x++) |
---|
290 | { |
---|
291 | sum += chooser.getProbability(objs[x]); |
---|
292 | chooser.setProbability(objs[x],(double)sum); |
---|
293 | } |
---|
294 | |
---|
295 | // now we need to work backwards setting 0 values |
---|
296 | int x; |
---|
297 | for(x=objs.length-1; x > 0; x--) |
---|
298 | if (chooser.getProbability(objs[x])== |
---|
299 | chooser.getProbability(objs[x-1])) // we're 0.0 |
---|
300 | chooser.setProbability(objs[x],1.0); |
---|
301 | else break; |
---|
302 | chooser.setProbability(objs[x],1.0); |
---|
303 | } |
---|
304 | |
---|
305 | |
---|
306 | // allows us to have zero-probability values |
---|
307 | private static final int exemptZeroes(final float[] probabilities, int index) |
---|
308 | { |
---|
309 | //System.out.println(index); |
---|
310 | if (probabilities[index]==0.0f) // I need to scan forward because I'm in a left-trail |
---|
311 | // scan forward |
---|
312 | { while(index < probabilities.length-1 && probabilities[index]==0.0f) index++; } |
---|
313 | else |
---|
314 | // scan backwards |
---|
315 | { while(index > 0 && probabilities[index]==probabilities[index-1]) index--; } |
---|
316 | return index; |
---|
317 | } |
---|
318 | |
---|
319 | // allows us to have zero-probability values |
---|
320 | private static final int exemptZeroes(final double[] probabilities, int index) |
---|
321 | { |
---|
322 | //System.out.println(index); |
---|
323 | if (probabilities[index]==0.0) // I need to scan forward because I'm in a left-trail |
---|
324 | // scan forward |
---|
325 | { while(index < probabilities.length-1 && probabilities[index]==0.0) index++; } |
---|
326 | else |
---|
327 | // scan backwards |
---|
328 | { while(index > 0 && probabilities[index]==probabilities[index-1]) index--; } |
---|
329 | return index; |
---|
330 | } |
---|
331 | |
---|
332 | |
---|
333 | // allows us to have zero-probability values |
---|
334 | private static final int exemptZeroes(final Object[] objs, |
---|
335 | final RandomChoiceChooser chooser, int index) |
---|
336 | { |
---|
337 | //System.out.println(index); |
---|
338 | if (chooser.getProbability(objs[index])==0.0f) // I need to scan forward because I'm in a left-trail |
---|
339 | // scan forward |
---|
340 | { while(index < objs.length-1 && chooser.getProbability(objs[index])==0.0f) index++; } |
---|
341 | else |
---|
342 | // scan backwards |
---|
343 | { while(index > 0 && chooser.getProbability(objs[index])== |
---|
344 | chooser.getProbability(objs[index-1])) index--; } |
---|
345 | return index; |
---|
346 | } |
---|
347 | |
---|
348 | |
---|
349 | // allows us to have zero-probability values |
---|
350 | private static final int exemptZeroes(final Object[] objs, |
---|
351 | final RandomChoiceChooserD chooser, int index) |
---|
352 | { |
---|
353 | //System.out.println(index); |
---|
354 | if (chooser.getProbability(objs[index])==0.0) // I need to scan forward because I'm in a left-trail |
---|
355 | // scan forward |
---|
356 | { while(index < objs.length-1 && chooser.getProbability(objs[index])==0.0) index++; } |
---|
357 | else |
---|
358 | // scan backwards |
---|
359 | { while(index > 0 && chooser.getProbability(objs[index])== |
---|
360 | chooser.getProbability(objs[index-1])) index--; } |
---|
361 | return index; |
---|
362 | } |
---|
363 | |
---|
364 | |
---|
365 | public static final int CHECKBOUNDARY = 8; |
---|
366 | |
---|
367 | /** Picks a random item from an array of probabilities, |
---|
368 | normalized and summed as follows: For example, |
---|
369 | if four probabilities are {0.3, 0.2, 0.1, 0.4}, then |
---|
370 | they should get normalized and summed by the outside owners |
---|
371 | as: {0.3, 0.5, 0.6, 1.0}. If probabilities.length < CHECKBOUNDARY, |
---|
372 | then a linear search is used, else a binary search is used. */ |
---|
373 | |
---|
374 | public static int pickFromDistribution(final float[] probabilities, |
---|
375 | final float prob) |
---|
376 | { |
---|
377 | return pickFromDistribution(probabilities, prob, CHECKBOUNDARY); |
---|
378 | } |
---|
379 | |
---|
380 | |
---|
381 | /** Picks a random item from an array of probabilities, |
---|
382 | normalized and summed as follows: For example, |
---|
383 | if four probabilities are {0.3, 0.2, 0.1, 0.4}, then |
---|
384 | they should get normalized and summed by the outside owners |
---|
385 | as: {0.3, 0.5, 0.6, 1.0}. If probabilities.length < checkboundary, |
---|
386 | then a linear search is used, else a binary search is used. |
---|
387 | @deprecated |
---|
388 | */ |
---|
389 | public static int pickFromDistribution(final float[] probabilities, |
---|
390 | final float prob, final int checkboundary) |
---|
391 | { |
---|
392 | if (prob<0.0f || prob>1.0f) |
---|
393 | throw new ArithmeticException("Invalid probability for pickFromDistribution (must be 0.0<=x<=1.0)"); |
---|
394 | else if (probabilities.length==1) // quick |
---|
395 | return 0; |
---|
396 | else if (probabilities.length<checkboundary) |
---|
397 | { |
---|
398 | // simple linear scan |
---|
399 | for(int x=0;x<probabilities.length-1;x++) |
---|
400 | if (probabilities[x]>prob) |
---|
401 | return exemptZeroes(probabilities,x); |
---|
402 | return exemptZeroes(probabilities,probabilities.length-1); |
---|
403 | } |
---|
404 | else |
---|
405 | { |
---|
406 | // binary search |
---|
407 | int top = probabilities.length-1; |
---|
408 | int bottom = 0; |
---|
409 | int cur; |
---|
410 | |
---|
411 | while(top!=bottom) |
---|
412 | { |
---|
413 | cur = (top + bottom) / 2; // integer division |
---|
414 | |
---|
415 | if (probabilities[cur] > prob) |
---|
416 | if (cur==0 || probabilities[cur-1] <= prob) |
---|
417 | return exemptZeroes(probabilities,cur); |
---|
418 | else // step down |
---|
419 | top = cur; |
---|
420 | else if (cur==probabilities.length-1) // oops |
---|
421 | return exemptZeroes(probabilities,cur); |
---|
422 | else if (bottom==cur) // step up |
---|
423 | bottom++; // (8 + 9)/2 = 8 |
---|
424 | else |
---|
425 | bottom = cur; // (8 + 10) / 2 = 9 |
---|
426 | } |
---|
427 | return exemptZeroes(probabilities,bottom); // oops |
---|
428 | } |
---|
429 | } |
---|
430 | |
---|
431 | |
---|
432 | |
---|
433 | /** Picks a random item from an array of probabilities, |
---|
434 | normalized and summed as follows: For example, |
---|
435 | if four probabilities are {0.3, 0.2, 0.1, 0.4}, then |
---|
436 | they should get normalized and summed by the outside owners |
---|
437 | as: {0.3, 0.5, 0.6, 1.0}. If probabilities.length < CHECKBOUNDARY, |
---|
438 | then a linear search is used, else a binary search is used. */ |
---|
439 | |
---|
440 | public static int pickFromDistribution(final double[] probabilities, |
---|
441 | final double prob) |
---|
442 | { |
---|
443 | return pickFromDistribution(probabilities, prob, CHECKBOUNDARY); |
---|
444 | } |
---|
445 | |
---|
446 | |
---|
447 | /** Picks a random item from an array of probabilities, |
---|
448 | normalized and summed as follows: For example, |
---|
449 | if four probabilities are {0.3, 0.2, 0.1, 0.4}, then |
---|
450 | they should get normalized and summed by the outside owners |
---|
451 | as: {0.3, 0.5, 0.6, 1.0}. If probabilities.length < checkboundary, |
---|
452 | then a linear search is used, else a binary search is used. |
---|
453 | @deprecated |
---|
454 | */ |
---|
455 | |
---|
456 | public static int pickFromDistribution(final double[] probabilities, |
---|
457 | final double prob, final int checkboundary) |
---|
458 | { |
---|
459 | if (prob<0.0 || prob>1.0) |
---|
460 | throw new ArithmeticException("Invalid probability for pickFromDistribution (must be 0.0<=x<=1.0)"); |
---|
461 | if (probabilities.length==1) // quick |
---|
462 | return 0; |
---|
463 | else if (probabilities.length<checkboundary) |
---|
464 | { |
---|
465 | // simple linear scan |
---|
466 | for(int x=0;x<probabilities.length-1;x++) |
---|
467 | if (probabilities[x]>prob) |
---|
468 | return exemptZeroes(probabilities,x); |
---|
469 | return exemptZeroes(probabilities,probabilities.length-1); |
---|
470 | } |
---|
471 | else |
---|
472 | { |
---|
473 | // binary search |
---|
474 | int top = probabilities.length-1; |
---|
475 | int bottom = 0; |
---|
476 | int cur; |
---|
477 | |
---|
478 | while(top!=bottom) |
---|
479 | { |
---|
480 | cur = (top + bottom) / 2; // integer division |
---|
481 | |
---|
482 | if (probabilities[cur] > prob) |
---|
483 | if (cur==0 || probabilities[cur-1] <= prob) |
---|
484 | return exemptZeroes(probabilities,cur); |
---|
485 | else // step down |
---|
486 | top = cur; |
---|
487 | else if (cur==probabilities.length-1) // oops |
---|
488 | return exemptZeroes(probabilities,cur); |
---|
489 | else if (bottom==cur) // step up |
---|
490 | bottom++; // (8 + 9)/2 = 8 |
---|
491 | else |
---|
492 | bottom = cur; // (8 + 10) / 2 = 9 |
---|
493 | } |
---|
494 | return exemptZeroes(probabilities,bottom); // oops |
---|
495 | } |
---|
496 | } |
---|
497 | |
---|
498 | |
---|
499 | /** Picks a random item from an array of objects, each with an |
---|
500 | associated probability that is accessed by taking an object |
---|
501 | and passing it to chooser.getProbability(obj). The objects' |
---|
502 | probabilities are |
---|
503 | normalized and summed as follows: For example, |
---|
504 | if four probabilities are {0.3, 0.2, 0.1, 0.4}, then |
---|
505 | they should get normalized and summed by the outside owners |
---|
506 | as: {0.3, 0.5, 0.6, 1.0}. If probabilities.length < CHECKBOUNDARY, |
---|
507 | then a linear search is used, else a binary search is used. */ |
---|
508 | |
---|
509 | public static int pickFromDistribution(final Object[] objs, |
---|
510 | final RandomChoiceChooser chooser, |
---|
511 | final float prob) |
---|
512 | { |
---|
513 | return pickFromDistribution(objs, chooser, prob, CHECKBOUNDARY); |
---|
514 | } |
---|
515 | |
---|
516 | /** Picks a random item from an array of objects, each with an |
---|
517 | associated probability that is accessed by taking an object |
---|
518 | and passing it to chooser.getProbability(obj). The objects' |
---|
519 | probabilities are |
---|
520 | normalized and summed as follows: For example, |
---|
521 | if four probabilities are {0.3, 0.2, 0.1, 0.4}, then |
---|
522 | they should get normalized and summed by the outside owners |
---|
523 | as: {0.3, 0.5, 0.6, 1.0}. If probabilities.length < checkboundary, |
---|
524 | then a linear search is used, else a binary search is used. |
---|
525 | @deprecated |
---|
526 | */ |
---|
527 | |
---|
528 | public static int pickFromDistribution(final Object[] objs, |
---|
529 | final RandomChoiceChooser chooser, |
---|
530 | final float prob, final int checkboundary) |
---|
531 | { |
---|
532 | if (prob<0.0f || prob>1.0f) |
---|
533 | throw new ArithmeticException("Invalid probability for pickFromDistribution (must be 0.0<=x<=1.0)"); |
---|
534 | if (objs.length==1) // quick |
---|
535 | return 0; |
---|
536 | else if (objs.length<checkboundary) |
---|
537 | { |
---|
538 | // simple linear scan |
---|
539 | for(int x=0;x<objs.length-1;x++) |
---|
540 | if (chooser.getProbability(objs[x]) >prob) |
---|
541 | return exemptZeroes(objs,chooser,x); |
---|
542 | return exemptZeroes(objs,chooser,objs.length-1); |
---|
543 | } |
---|
544 | else |
---|
545 | { |
---|
546 | // binary search |
---|
547 | int top = objs.length-1; |
---|
548 | int bottom = 0; |
---|
549 | int cur; |
---|
550 | |
---|
551 | while(top!=bottom) |
---|
552 | { |
---|
553 | cur = (top + bottom) / 2; // integer division |
---|
554 | |
---|
555 | if (chooser.getProbability(objs[cur]) > prob) |
---|
556 | if (cur==0 || chooser.getProbability(objs[cur-1]) <= prob) |
---|
557 | return exemptZeroes(objs,chooser,cur); |
---|
558 | else // step down |
---|
559 | top = cur; |
---|
560 | else if (cur==objs.length-1) // oops |
---|
561 | return exemptZeroes(objs,chooser,cur); |
---|
562 | else if (bottom==cur) // step up |
---|
563 | bottom++; // (8 + 9)/2 = 8 |
---|
564 | else |
---|
565 | bottom = cur; // (8 + 10) / 2 = 9 |
---|
566 | } |
---|
567 | return exemptZeroes(objs,chooser,bottom); // oops |
---|
568 | } |
---|
569 | } |
---|
570 | |
---|
571 | |
---|
572 | /** Picks a random item from an array of objects, each with an |
---|
573 | associated probability that is accessed by taking an object |
---|
574 | and passing it to chooser.getProbability(obj). The objects' |
---|
575 | probabilities are |
---|
576 | normalized and summed as follows: For example, |
---|
577 | if four probabilities are {0.3, 0.2, 0.1, 0.4}, then |
---|
578 | they should get normalized and summed by the outside owners |
---|
579 | as: {0.3, 0.5, 0.6, 1.0}. If probabilities.length < CHECKBOUNDARY, |
---|
580 | then a linear search is used, else a binary search is used. */ |
---|
581 | |
---|
582 | public static int pickFromDistribution(final Object[] objs, |
---|
583 | final RandomChoiceChooserD chooser, |
---|
584 | final double prob) |
---|
585 | { |
---|
586 | return pickFromDistribution(objs, chooser, prob, CHECKBOUNDARY); |
---|
587 | } |
---|
588 | |
---|
589 | /** Picks a random item from an array of objects, each with an |
---|
590 | associated probability that is accessed by taking an object |
---|
591 | and passing it to chooser.getProbability(obj). The objects' |
---|
592 | probabilities are |
---|
593 | normalized and summed as follows: For example, |
---|
594 | if four probabilities are {0.3, 0.2, 0.1, 0.4}, then |
---|
595 | they should get normalized and summed by the outside owners |
---|
596 | as: {0.3, 0.5, 0.6, 1.0}. If probabilities.length < checkboundary, |
---|
597 | then a linear search is used, else a binary search is used. |
---|
598 | @deprecated |
---|
599 | */ |
---|
600 | |
---|
601 | public static int pickFromDistribution(final Object[] objs, |
---|
602 | final RandomChoiceChooserD chooser, |
---|
603 | final double prob, final int checkboundary) |
---|
604 | { |
---|
605 | if (prob<0.0 || prob>1.0) |
---|
606 | throw new ArithmeticException("Invalid probability for pickFromDistribution (must be 0.0<=x<=1.0)"); |
---|
607 | if (objs.length==1) // quick |
---|
608 | return 0; |
---|
609 | else if (objs.length<checkboundary) |
---|
610 | { |
---|
611 | // simple linear scan |
---|
612 | for(int x=0;x<objs.length-1;x++) |
---|
613 | if (chooser.getProbability(objs[x]) >prob) |
---|
614 | return exemptZeroes(objs,chooser,x); |
---|
615 | return exemptZeroes(objs,chooser,objs.length-1); |
---|
616 | } |
---|
617 | else |
---|
618 | { |
---|
619 | // binary search |
---|
620 | int top = objs.length-1; |
---|
621 | int bottom = 0; |
---|
622 | int cur; |
---|
623 | |
---|
624 | while(top!=bottom) |
---|
625 | { |
---|
626 | cur = (top + bottom) / 2; // integer division |
---|
627 | |
---|
628 | if (chooser.getProbability(objs[cur]) > prob) |
---|
629 | if (cur==0 || chooser.getProbability(objs[cur-1]) <= prob) |
---|
630 | return exemptZeroes(objs,chooser,cur); |
---|
631 | else // step down |
---|
632 | top = cur; |
---|
633 | else if (cur==objs.length-1) // oops |
---|
634 | return exemptZeroes(objs,chooser,cur); |
---|
635 | else if (bottom==cur) // step up |
---|
636 | bottom++; // (8 + 9)/2 = 8 |
---|
637 | else |
---|
638 | bottom = cur; // (8 + 10) / 2 = 9 |
---|
639 | } |
---|
640 | return exemptZeroes(objs,chooser,bottom); // oops |
---|
641 | } |
---|
642 | } |
---|
643 | |
---|
644 | } |
---|
645 | |
---|
646 | |
---|
647 | |
---|
648 | |
---|