【问题标题】:PHP Choose Random Number With WeightsPHP 选择带权重的随机数
【发布时间】:2012-05-20 12:13:06
【问题描述】:

假设我想从1-10 随机分配一个数字choose,但每个数字都有权重。

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 中进行编码?

【问题讨论】:

  • 您可以使用平均值为 1 的标准分布(高斯)算法,但 ChristopheD 的答案要简单得多。

标签: php random probability


【解决方案1】:

我假设你的百分比加起来是 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 到 100,000 的东西。
  • 制作这样的数组似乎过分了,它不支持像 10.5% 这样的权重
  • @yamikoWebs:如果您需要 0.5% 的分辨率而不是 1% 的称重分辨率,您只需要一个 200 元素数组 ;-) 对我来说,这看起来很适合(简单)手头的问题恕我直言,我当然没有声称这个主题的所有可能变化都有明确的解决方案......
  • @ChristopheD 我不喜欢在没有必要的情况下制作那么大的数组的想法。顺便说一句,我添加了一个我认为更有效/可维护的类作为答案...它位于底部,因为我一直在对其进行大量编辑以进行改进...计划对其进行更新以使其返回字符串以外的值和整数使用多维数组,但不是今天:P
  • 数组解决方案允许您以 O(N) 空间为代价在 O(1) 中选择随机选项。你可以做相反的妥协,只存储一个选项列表及其权重,并遍历它们以获得正确的选项(这是 Mala 的解决方案)。这两种方法都有重要的缺点。您正在寻找的解决方案称为“别名方法”。这已在 StackOverflow 上讨论过多次。如果您有兴趣,Google 上有一个 Python 实现。
【解决方案2】:

将它们多次放入数组中,例如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);

【讨论】:

    【解决方案3】:

    如果您的权重是百分比,请在 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;
        }
    }
    ?>
    

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

    【讨论】:

    • 如果值具有相同的权重则不起作用,将 $weights 想象为 10 乘以 10 的数组。您总是会得到第一个值。还取决于要排序的 $weights(降序)。
    • 实际上它确实适用于等权重的值,$weights 不需要排序(范围 [0 - 0.2] 与范围 [0.8 - 1] 完全一样)...
    • 是的,但是您返回满足 $r $weights = array(1, 90, 9);。您的循环将 1 作为第一个元素进行迭代,它只有 2% 的机会返回 NOT。 ($r = 0 或 $r = 1)(例如,在本例中,有 98% 的时间您拥有 1% 概率的密钥)
    • 您是正确的,因为函数不返回的可能性很小(尽管当 $r 为 100 时它略小于 1%),但您错了,它返回具有正确概率的元素。我鼓励您用您自己的示例实际尝试代码。通过将 $r 更改为 0 到 99 之间(产生 100 种可能性)来修复函数未能返回略低于 1% 的时间)
    【解决方案4】:

    具有以下类的 OP 权重的示例回显值:

    echo 1+Rand::get_weighted_rand(array(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);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-01-22
      • 1970-01-01
      • 2011-04-29
      • 1970-01-01
      • 2010-09-26
      • 1970-01-01
      • 2015-07-05
      • 2010-09-08
      相关资源
      最近更新 更多