I've been looking for a good algorithm for selecting a random object from a list of objects with differently weighted probabilities, and have found a veritable trove of possibilities, from putting each object in an array n times, to performing a binary search on an array containing the discrete cumulative density function (CDF), to putting the CDF in buckets, and a few more confusing responses, especially courtesy of Weighted random selection from array.
But the responses all seem to focus on efficiency of making the random selection instead of the cost of building or adjusting the weights dynamically. As my application seems that it might require the weights being adjusted at runtime nearly as often as a selection is made, what would be the most efficient algorithm allowing for dynamic adjustments to the probability weights? Adding new objects to the list will probably only be done at initialization, but removal of objects could be done a bit more infrequently than merely changing the values (perhaps setting the probability to zero would suffice).
My initial impression is that using an array of CDFs is my best bet; applying the same adjustment to everything after the probability being targeted seems trivial enough, but I'm not seeing anything quite so easy for the CDF bucket, for example. Any ideas?
If anyone cares, I'm implementing this with haxe.
-
You could use a Fenwick tree for O(log n)-time samples and updates, but depending on what the adjustments are, you may be able to do better.David Eisenstat– David Eisenstat2016年03月11日 18:16:09 +00:00Commented Mar 11, 2016 at 18:16
1 Answer 1
Suppose you have and array of objects {O.1, O.2, ... , O.N} and an array of associated probability weights {w.1, w.2, ..., w.N}
You can setup ranges of values ("buckets") between 0 and 1 that are sized according to each of the weights, then just pick a random value (with uniform distribution) between 0 and 1. The random value will fall in one of the buckets you've defined. If it falls in the i'th bucket, pick the i'th object from the array.
sorry, I don't know haxe, but here is some java code to illustrate:
Random mRandom = new Random();
double[] weights = {0.3, 0.2, 0.1, 0.5}; //Make sure these sum to 1!! But at least you can change these at run-time.
int getRandomObject() {
double randNum = mRandom.nextDouble();
double sumOfWeightSoFar = 0;
int currentIndex = 0;
while (true) {
if (randNum < weights[currentIndex] + sumOfWeightSoFar)
return currentIndex;
else {
currentIndex++;
sumOfWeightSoFar = sumOfWeightSoFar + weights[currentIndex];
}
}
//If your weights don't sum to 1 or more, then "currentIndex" will eventually exceed your array bounds. But you get the idea.
}