PHP选择带权重的随机数

PHP选择带权重的随机数,php,random,probability,Php,Random,Probability,假设我想从1-10中随机选择一个数字,但每个数字都有权重 1 - 15% chance 2 - 15% chance 3 - 12% chance 4 - 12% chance 5 - 10% chance 6 - 10% chance 7 - 8% chance 8 - 8% chance 9 - 5% chance 10 - 5% chance 我该如何在PHP中进行编码呢?我假设你的百分比加起来是100% 使用 15 times a '1' value, 15 times a '2'

假设我想从
1-10
中随机选择一个数字,但每个数字都有权重

1 - 15% chance
2 - 15% chance
3 - 12% chance
4 - 12% chance
5 - 10% chance
6 - 10% chance
7 - 8% chance
8 - 8% chance
9 - 5% chance
10 - 5% chance

我该如何在
PHP
中进行编码呢?

我假设你的百分比加起来是100%

使用

15 times a '1' value, 
15 times a '2' value, 
... 
10 times a '6' value, 
8 times a '7' value,
...
5 times 1 '10' value
您将得到一个包含100个元素的数组


随机选取一个元素(并将其从数组中弹出)。

将所有元素多次放入数组中,例如,1 15次、3 12次等等。 然后从该数组中选择一个随机数

$array = array_merge (array_fill (0, 15, 1), array_fill (0, 15, 2), array_fill (0, 12, 3), array_fill (0, 12, 4), array_fill (0, 10, 5), array_fill (0, 10, 6), array_fill (0, 8, 7), array_fill (0, 8, 8), array_fill (0, 5, 9), array_fill (0, 5, 10));
$random_number = array_rand ($array);

如果权重以百分比表示,请选择一个介于0和100之间的随机数,然后迭代减去百分比,直到过零:

<?php
function getWeightedRandom() {
    $weights = array(15, 15, 12, ...); // these should add up to 100
    $r = rand(0, 99);
    for ($i=0; $i<count($weights); $i++) {
        $r -= $weights[$i];
        if ($r < 0)
            return $i+1;
    }
}
?>


这还具有支持非整数权重的额外好处。

下面是一个使用OPs权重的值与下面的类相呼应的示例:

echo 1+Rand::获取加权Rand(数组(15,15,12,12,10,10,8,8,5,5))

班级:

class Rand
{
    /*
     * generates a random value based on weight
     * @RETURN MIXED: returns the key of an array element
     * @PARAM $a ARRAY:
     *  the array key is the value returned and the array value is the weight
     *      if the values sum up to less than 100 than the last element of the array 
     *      is the default value when the number is out of the range of other values
     * @PARAM $p INT: number of digits after decimal
     *
     * i.e array(1=>20, 'foo'=>80): has an 80 chance of returning Foo
     * i.e array('bar'=>0.5, 2=>1, 'default'=>0), 1: 98.5% chance of returning default
     */
    public static function get_weighted_rand($a, $p=0)
    {
        if(array_sum($a)>100)
            return FALSE;#total must be less than 100
        $p=pow(10, $p+2);
        $n=mt_rand(1,$p)*(100/$p);
        $range=100;
        foreach($a as $k=>$v)
        {
            $range-=$v;
            if($n>$range)
                return $k;
        }
            #returning default value
        end($a);
        return key($a);
    }
}

你可以使用平均值为1的标准分布(高斯)算法,但ChristopheD的答案要简单得多。有没有一种数学方法可以做到这一点而不需要阵列开销?我们如何对1-100000之间的数据进行编码。制作这样一个数组似乎有些过分,它无法支持10.5%这样的权重@yamikoWebs:如果需要0.5%而不是1%的分辨率来进行称重,那么只需要一个200元素的数组;-)在我看来,这似乎是一个很好的(简单的)适合于手头的问题,伊姆霍,我当然不是要对这个主题的每一个可能的变化都提出明确的解决方案…@ChristopheD如果没有必要,我不喜欢制作这么大的数组。顺便说一句,我添加了一个我认为更有效/更可维护的类作为答案…它一直在底部,因为我一直在编辑它以进行改进…计划更新它,允许它通过使用多维数组返回字符串和整数以外的值,但不是今天:p数组解决方案允许您选择随机值O(1)中的选择以O(N)空间为代价。您可以做相反的折衷,只需存储一个选项列表及其权重,然后遍历它们以获得正确的选项(这是Mala的解决方案)。这两种方法都有重要的缺点。您正在寻找的解决方案称为“别名方法”。这已经在StackOverflow上讨论过好几次了。如果你感兴趣的话,Google上有一个Python实现。如果值的权重相等,那么它就不起作用,想象一下$weights是10乘以10的数组。你总是会得到第一个值。还取决于要排序的$weights(降序)。实际上,它确实适用于相等的加权值,$weights不需要排序(范围[0-0.2]与范围[0.8-1]完全相同)……是的,但返回满足$r<0的第一个值。想象一下,
$weights=array(1,90,9)。循环将1作为第一个元素进行迭代,它不返回的概率只有2%。($r=0或$r=1)(例如,在本例中,您有98%的时间拥有概率为1%的密钥)您是正确的,因为函数不返回的概率很小(虽然$r为100时,它略小于1%),但您是错误的,因为它返回的元素概率正确。我鼓励您使用自己的示例实际尝试代码。通过将$r更改为0到99之间(这将产生100种可能性),可以修复返回时间略小于1%的函数