I want a random number generator with non-uniform distribution, ie:
// prints 0 with 0.1 probability, and 1 with 0.9 probability
echo probRandom(array(10, 90));
This is what I have right now:
/**
* method to generated a *not uniformly* random index
*
* @param array $probs int array with weights
* @return int a random index in $probs
*/
function probRandom($probs) {
$size = count($probs);
// construct probability vector
$prob_vector = array();
$ptr = 0;
for ($i=0; $i<$size; $i++) {
$ptr += $probs[$i];
$prob_vector[$i] = $ptr;
}
// get a random number
$rand = rand(0, $ptr);
for ($i=0, $ret = false; $ret === false; $i++) {
if ($rand <= $prob_vector[$i])
return $i;
}
}
Can anyone think of a better way? Possibly one that doesn’t require me to do pre-processing?
In your solution you generate an accumulated probability vector, which is very useful.
I have two suggestions for improvement:
$probsare static, i.e. it’s the same vector every time you want to generate a random number, you can preprocess$prob_vectorjust once and keep it.$i(Newton bisection method)EDIT: I now see that you ask for a solution without preprocessing.
Without preprocessing, you will end up with worst case linear runtime (i.e., double the length of the vector, and your running time will double as well).
Here is a method that doesn’t require preprocessing. It does, however, require you to know a maximum limit of the elements in
$probs:Rejection method
$iand a random number,X(uniformly) between0andmax($probs)-1, inclusive.Xis less than$probs[$i], you’re done –$iis your random number$i(hence the name of the method) and restart.