I'm trying to implement a weighted random numbers. I'm currently just banging my head against the wall and cannot figure this out.
In my project (Hold'em hand-ranges, subjective all-in equity analysis), I'm using Boost's random -functions. So, let's say I want to pick a random number between 1 and 3 (so either 1, 2 or 3). Boost's mersenne twister generator works like a charm for this. However, I want the pick to be weighted for example like this:
1 (weight: 90)
2 (weight: 56)
3 (weight: 4)
Does Boost have some sort of functionality for this?
There is a straightforward algorithm for picking an item at random, where items have individual weights:
1) calculate the sum of all the weights
2) pick a random number that is 0 or greater and is less than the sum of the weights
3) go through the items one at a time, subtracting their weight from your random number, until you get the item where the random number is less than that item's weight
Pseudo-code illustrating this:
int sum_of_weight = 0;
for(int i=0; i<num_choices; i++) {
sum_of_weight += choice_weight[i];
}
int rnd = random(sum_of_weight);
for(int i=0; i<num_choices; i++) {
if(rnd < choice_weight[i])
return i;
rnd -= choice_weight[i];
}
assert(!"should never get here");
This should be straightforward to adapt to your boost containers and such.
If your weights are rarely changed but you often pick one at random, and as long as your container is storing pointers to the objects or is more than a few dozen items long (basically, you have to profile to know if this helps or hinders), then there is an optimisation:
By storing the cumulative weight sum in each item you can use a binary search to pick the item corresponding to the pick weight.
If you do not know the number of items in the list, then there's a very neat algorithm called reservoir sampling that can be adapted to be weighted.