Suppose I have an array
var list = ['a', 'b', 'c', 'd'];
and I want to select one value from List
at random. If it is pletely random, each will have a 25% chance. Is there a way to select a value at random with a bias like so:
var bias = [0.1, 0.2, 0.1, 0.6];
(bias of course adds up to 1)
So 'a' has a 10% chance of being selected and 'b' has a 20% chance of being selected, etc
Edit: I know I can just modify do var list = ['a', 'b', 'b', 'c', 'd', 'd', 'd', 'd', 'd', 'd']
and just select a value at random but I am looking for a more efficient way of doing this by just having an array that contains the bias.
Suppose I have an array
var list = ['a', 'b', 'c', 'd'];
and I want to select one value from List
at random. If it is pletely random, each will have a 25% chance. Is there a way to select a value at random with a bias like so:
var bias = [0.1, 0.2, 0.1, 0.6];
(bias of course adds up to 1)
So 'a' has a 10% chance of being selected and 'b' has a 20% chance of being selected, etc
Edit: I know I can just modify do var list = ['a', 'b', 'b', 'c', 'd', 'd', 'd', 'd', 'd', 'd']
and just select a value at random but I am looking for a more efficient way of doing this by just having an array that contains the bias.
- 1 Unless your list gets really large, (which depends on the exact requirements, such as if you want something to be picked 2.3486% of the time) I suspect that your quick and dirty way is "more efficient" than the answers below which require summing things and their own array of floats. – user949300 Commented Jan 5, 2015 at 23:47
- @user949300: The cumulative bias array is nice if you need to pull several samples from the same list with same probabilities, so you can (and should) precalculate it. It is not required: you can just go summing-and-sequentially-searching every time you need a sample if the biases change all the time, or just want to do it once (however, then you can't use binary search). It is also the only way to do it if biases are not all integral multiples. – Amadan Commented Jan 6, 2015 at 0:47
3 Answers
Reset to default 6Make a cumulative bias list:
var sum = 0;
var cumulativeBias = bias.map(function(x) { sum += x; return sum; });
Then make a random number from 0
to the sum
(i.e. cumulativeBias[cumulativeBias.length - 1]
):
var choice = Math.random() * sum;
Then search for the first element in cumulativeBias
that is bigger than choice
. You can use binary search for speed, but for short lists a sequential search is good enough. The index of that element is the chosen index. For example, something like:
var chosenIndex = null;
cumulativeBias.some(function(el, i) {
return el > choice ? ((chosenIndex = i), true) : false;
});
chosenElement = list[chosenIndex];
I have written this in the past in the following way:
Replace your bias with a running total - i.e. add bias[0] to bias[1], then add bias[1] to bias[2], then add bias[2] to bias[3].
In your above example, you would get var bias = [0.1, 0.3, 0.4, 1.0]
- the last one should always be 1, otherwise you have done something wrong.
Now pick a random number between 0 and 1.0; find the largest number in the bias array, that is smaller than your random number. Pick the corresponding value from your list array.
Edit: @Amadan is right. I meant, find the smallest number in the bias array, that is larger than your random number. My "smallest" and "largest" were the wrong way round :)
Since your bias is simple, you can just make an array that offers 10% 'a'
, 20% 'b'
and so on like this and then just pick a random value from that:
var choices = ['a', 'b', 'b', 'c', 'd', 'd', 'd', 'd', 'd', 'd'];
var biasVal = choices[Math.floor(Math.random() * choices.length)];
The choices array could be built programmatically if needed. It would only have to be built once and from then on the selection of random choices would be significantly faster with this method.