具有非均匀分布的随机值

时间:2010-07-08 19:37:08

标签: php random

我想要一个非均匀分布的随机数生成器,即:

// 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;
    }   
}

有人能想到更好的方法吗?可能是一个不需要我进行预处理的人吗?

2 个答案:

答案 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中元素的最大限制:

拒绝方法

  • $iX之间选择一个随机索引,0和一个随机数max($probs)-1(统一)。
  • 如果X小于$probs[$i],您就完成了 - $i是您的随机数
  • 否则拒绝 $i(因此该方法的名称)并重新启动。