我有一组元素,我需要从中选择任何一个元素。每个元素都与百分比机会相关联。百分比增加到100。
我需要从这些元素中选择一个元素,以便选择元素的几率等于百分比值。因此,如果一个元素有25%的几率,它应该有25%的机会被选中。换句话说,如果我们选择1 mil的元素,那么该元素应该选择接近250k次。
答案 0 :(得分:5)
您所描述的是一个多项过程。
http://en.wikipedia.org/wiki/Multinomial_distribution#Sampling_from_a_multinomial_distribution
他们生成这样的随机过程的方式是这样的: (我将使用伪代码,但应该很容易将其用于实际代码。)
按照概率的相反顺序对“框”进行排序: (不需要。它只是一个优化) 所以你有例如值= [0.45,0.3,0.15,0.1]
然后创建'累积'分布,它是索引< = i的所有元素的总和。 伪代码:
cumulant=[0,0,0,0] // initiate it
s=0
for j=0 to size()-1 {
s=s+values[i] ;
cumulant[i]=s
}
在我们的案例中累积量= [0.45,0.70,0.85,1]
制作0到1之间的均匀随机数x。 对于php:http://php.net/manual/en/function.rand.php
得到的随机框索引i是
累积量[i]< X
伪代码:
for j=0 to size()-1 {
if !(cumulant[i]<){
print "your index is ",i
break;
}
就是这样。通过回到第3点获得另一个随机索引。
如果您按照上面的建议排序,这意味着最终搜索会更快。例如,如果你有这个概率向量:0.001 0.001 0.001 0.001 0.996那么,当你对它进行排序时,你几乎总是只需要看索引i = 0,因为随机数x几乎总是低于0.996 。如果排序得到回报,取决于你是否反复使用相同的“盒子”。所以,是的250k尝试它会有很大帮助。请记住,你获得的盒子索引是针对排序后的矢量。
答案 1 :(得分:1)
我想我写它比你向我们展示你到目前为止做的更快。
可能不是最好的解决方案,但就目前而言,看起来它是你唯一的解决方案。
你走了:
$elements = array(
'This' => 25,
'is' => 15,
'a' => 15,
'crappy' => 20,
'list' => 25
);
asort($elements);
$elements = array_reverse($elements);
// Precalc cumulative value
$cumulant = 0;
foreach ($elements as $key => &$value) {
$cumulant += $value;
$value = $cumulant;
}
function pickAnElement($elements) {
$random = rand(1, 100);
foreach ($elements as $key => $value) {
if ($random <= $value) {
return $key;
}
}
}
$picks = array();
for ($i = 0; $i < 10000; $i++) {
$element = pickAnElement($elements);
if (!array_key_exists($element, $picks)) {
$picks[$element] = 0;
}
$picks[$element]++;
}
var_dump($picks);
受Johans回答的启发,我添加了一个循环来排序和预先计算累积量。