我想要一个非均匀分布的随机数生成器,即:
// prints 0 with 0.1 probability, and 1 with 0.9 probability
echo probRandom(array(10, 90));
这就是我现在所拥有的:
/**
* 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;
}
}
有人能想到更好的方法吗?可能是一个不需要我进行预处理的人吗?
答案 0 :(得分:2)
如果您知道$probs
中所有元素的总和,则无需预处理即可完成此操作。
像这样:
$max = sum($probs);
$r = rand(0,$max-1);
$tot = 0;
for ($i = 0; $i < length($probs); $i++) {
$tot += $probs[$i];
if ($r < $tot) {
return $i;
}
}
这将在O(N)时间内完成您想要的操作,其中N是数组的长度。这是这种算法的算法运行时的下限,因为必须考虑输入中的每个元素。
选择给定索引$i
的概率为$probs[$i]/sum($probs)
,假设rand
函数返回给定范围内的独立均匀分布的整数。
答案 1 :(得分:1)
在您的解决方案中,您会生成累积概率向量,这非常有用。
我有两个改进建议:
$probs
是静态的,即每次要生成随机数时它都是相同的向量,您只需预处理一次$prob_vector
并保留它。$i
(牛顿二分法)编辑:我现在看到您要求的解决方案没有预处理。
如果不进行预处理,最终会出现最坏情况的线性运行时间(即矢量长度加倍,运行时间也会加倍)。
这是一种不需要预处理的方法。但是,它确实要求您知道$probs
中元素的最大限制:
拒绝方法
$i
和X
之间选择一个随机索引,0
和一个随机数max($probs)-1
(统一)。X
小于$probs[$i]
,您就完成了 - $i
是您的随机数$i
(因此该方法的名称)并重新启动。