Add weight to randomization
I am struggling to find any resources to help with this question:
Let's say I want to generate a random number between 1 and 5, but wish the randomization is weighted at the lower end (for example, when generating a random number of children that a pair may have, which is likely to be 1-5). Where to begin? Will it involve creating a complex function to map this curve, or is there a method available to do this?
For very simple sets, I can create an array, so in the example above I could do, for example:
var children = [1,1,1,1,2,2,2,3,4,5];
return children[Math.floor(Math.random() * children.length)];
But this does not require flexibility and assumes that the dataset is created manually in a script.
source to share
The “probability of having children” has a Gaussian distribution, which is the “continuous limit” of the binomial distribution. How about using binomial spread ?
var val = Math.random()*5 + Math.random()*5;
// "val" will be between 0 and 10, but values around 5 are more probable
return 1 + Math.floor(Math.abs(val-5));
Just put it in a function and make the parameter "5".
Small test ( http://jsfiddle.net/yt6zvs8n/ ):
100 output values: 1, 1, 2, 3, 2, 4, 2, 2, 2, 3, 3, 1, 2, 1, 4, 1, 2, 3, 2, 1, 4, 4, 2, 1, 4, 3, 2, 2, 1, 3, 2, 4, 1, 3, 2, 4, 2, 2, 1, 2, 1, 1, 1, 4, 1, 4, 1, 3, 1, 2, 1, 2, 1, 2, 2, 3, 2, 1, 3, 1, 2, 1, 3, 2, 1, 1, 1, 1, 2, 1, 1, 2, 1, 3, 4, 1, 3, 1, 2, 5, 1, 3, 1, 3, 1, 3, 1, 4, 1, 3, 1, 3, 2, 2, 3, 1, 3, 4, 13
Origins (histogram): 38, 28, 21, 12, 1
source to share
Ok, as long as you are working with integers where N is the maximum child value and N is not that big, you will need O (n) space and time
I will answer the assumption that you are only using integers going from 0
to N
, see the USING section of my answer for why you don't need to specify them manually when their weight is 0.
var weights = [0,4,3,1,1,1];
var number = weights.reduce(function (previous, current, index) {
var value = Math.random() * (current|0);
if (value > previous[0]) {
return [value, index];
}
return previous;
}, [Math.random() * (weights[0]|0), 0])[0];
Description
Using the indices as integers, we have:
- 0 with weight 0 (it will never appear)
- 1 with weight 4
- 2 with weight 3
- 3 with weight 1
- 4 with weight 1
- 5 with weight 1
Using
How difficult it is to think in terms of a range, or if you don't want to specify all the values before N
manually, you can use an object and convert it to an array by specifying a key length
equal N + 1
, like this
var N = 130,
weights = Array.apply(null, {
12: 1,
53: 3,
130: 10,
length: N + 1
});
source to share
You can use the idea of assigning arbitrary weights to each possible result without having to create an array of that size. This is enough to assign an arbitrary weight to each possible choice, and then convert the random number to the corresponding result. The possible outcomes of a random throw do not have to be a consecutive whole range.
For example:
var weights = { 1: 4, 2: 3, 3: 1, 4: 1, 5: 1 }
// pre-processing; modifies weights!
var runningTotal = 0;
for (var number in weights) {
runningTotal += weights[number];
weights[number] = runningTotal;
}
// generation
var seed = Math.floor(Math.random() * runningTotal);
// conversion of seed to result based on weights
for (var number in weights) {
if (seed < weights[number]) {
console.log("result: " + number);
break;
}
}
Note that the above is meant to be illustrative and not as the best way to write this code.
You can generalize this even further so that you don't even have to enter data that explicitly points to every possible outcome, but the same principle will still apply.
source to share
since the number Math.random()
is between 0 and 1, all you have to do is create an array with the probability that the answer is 1,2,3,4 or 5. for example
var px = [
{ answer: 1, pxi: 0.0, pxf:0.40 },
{ answer: 2, pxi: 0.40, pxf:0.60 },
{ answer: 3, pxi: 0.60, pxf:0.80 },
{ answer: 4, pxi: 0.80, pxf:0.90 },
{ answer: 5, pxi: 0.90, pxf:1 }
]
lets say r
- a random number, so if r > pxi && r<=pxf
, it will return the answer x
.
so if I'm clear enough you can see that the ability this method returns 1 is 40%, 2 is 20%, 3 is 20%, 4 is 10% and 5 is 10%, you can easily change the probability of anwer. by changing the pxi-pxf range
source to share