在PHP中按权重生成随机结果?

在PHP中按权重生成随机结果?,php,random,Php,Random,我知道如何在PHP中生成一个随机数,但假设我想要一个介于1-10之间的随机数,但我想要更多的3,4,5,然后是8,9,10。这怎么可能?我会发布我尝试过的东西,但老实说,我甚至不知道从哪里开始 基本上: 将所有数字的权重相加 选择一个小于该值的随机数 按顺序减去权重,直到结果为负数,如果为负数,则返回该数字 对于一个始终向刻度一端倾斜的有效随机数: 选择0到1之间的连续随机数 升到γ的幂,使其偏置。1是未加权的,越低给出的数字越高,反之亦然 按比例缩放到所需范围,并舍入到整数 例如,在PHP

我知道如何在PHP中生成一个随机数,但假设我想要一个介于1-10之间的随机数,但我想要更多的3,4,5,然后是8,9,10。这怎么可能?我会发布我尝试过的东西,但老实说,我甚至不知道从哪里开始

基本上:

  • 将所有数字的权重相加
  • 选择一个小于该值的随机数
  • 按顺序减去权重,直到结果为负数,如果为负数,则返回该数字

  • 对于一个始终向刻度一端倾斜的有效随机数:

    • 选择0到1之间的连续随机数
    • 升到γ的幂,使其偏置。1是未加权的,越低给出的数字越高,反之亦然
    • 按比例缩放到所需范围,并舍入到整数
    例如,在PHP中(未经测试):

    在PHP中,通过多种剪切和粘贴解决方案引导您完成此过程。请注意,由于下面的评论,这个例程与您在该页面上看到的略有不同

    摘自《邮报》的一项功能:

    /**
     * weighted_random_simple()
     * Pick a random item based on weights.
     *
     * @param array $values Array of elements to choose from 
     * @param array $weights An array of weights. Weight must be a positive number.
     * @return mixed Selected element.
     */
    
    function weighted_random_simple($values, $weights){ 
        $count = count($values); 
        $i = 0; 
        $n = 0; 
        $num = mt_rand(1, array_sum($weights)); 
        while($i < $count){
            $n += $weights[$i]; 
            if($n >= $num){
                break; 
            }
            $i++; 
        } 
        return $values[$i]; 
    }
    
    /**
    *加权随机简单()
    *根据权重选择一个随机项。
    *
    *@param array$values可供选择的元素数组
    *@param array$weights一个权重数组。权重必须是正数。
    *@return混合选定元素。
    */
    函数加权随机简单($value,$weights){
    $count=计数($value);
    $i=0;
    $n=0;
    $num=mt_rand(1,数组和($weights));
    而($i<$count){
    $n+=$weights[$i];
    如果($n>=$num){
    打破
    }
    $i++;
    } 
    返回$value[$i];
    }
    
    既然我使用了IainMH的解决方案,我不妨分享我的PHP代码:

    
    
    朴素而公平。 只需复制/粘贴并测试它

      /**
       * getRandomWeightedElement()
       * Utility function for getting random values with weighting.
       * Pass in an associative array, such as array('A'=>5, 'B'=>45, 'C'=>50)
       * An array like this means that "A" has a 5% chance of being selected, "B" 45%, and "C" 50%.
       * The return value is the array key, A, B, or C in this case.  Note that the values assigned
       * do not have to be percentages.  The values are simply relative to each other.  If one value
       * weight was 2, and the other weight of 1, the value with the weight of 2 has about a 66%
       * chance of being selected.  Also note that weights should be integers.
       * 
       * @param array $weightedValues
       */
      function getRandomWeightedElement(array $weightedValues) {
        $rand = mt_rand(1, (int) array_sum($weightedValues));
    
        foreach ($weightedValues as $key => $value) {
          $rand -= $value;
          if ($rand <= 0) {
            return $key;
          }
        }
      }
    
    /**
    *收益加权概率
    *@param(数组)prob=>item
    *@返回键
    */
    函数权重和($stream){
    $pos=mt_rand(1,数组和(数组键($stream)));
    $em=0;
    foreach($k=>v){
    $em+=$k;
    如果($em>=$pos)
    返回$v;
    }
    }
    $item['30']=“我比每个人都有更多的机会:”;
    $item['10']=“我有很好的机会”;
    $item['1']=“我很难出现…”;
    对于基于@Allain/的($i=1;$i),我用PHP编写了这个快速函数。如果要使用非整数加权,则必须修改它

    use function \nspl\a\pairs;
    use function \nspl\rnd\weightedChoice;
    
    $weights = pairs(array(
        1 => 10,
        2 => 15,
        3 => 15,
        4 => 15,
        5 => 15,
        6 => 10,
        7 => 5,
        8 => 5,
        9 => 5,
        10 => 5
    ));
    
    $number = weightedChoice($weights);
    
    /**
    *getRandomWeightedElement()
    *用于获取带有权重的随机值的实用函数。
    *传入关联数组,例如数组('A'=>5,'B'=>45,'C'=>50)
    *这样的数组意味着“A”被选中的几率为5%,“B”为45%,而“C”为50%。
    *在本例中,返回值是数组键A、B或C
    *不必是百分比。这些值只是彼此相对的。如果一个值
    *权重为2,另一个权重为1,权重为2的值约为66%
    *被选中的机会。还要注意权重应该是整数。
    * 
    *@param数组$weightedValues
    */
    函数getRandomWeightedElement(数组$weightedValues){
    $rand=mt_rand(1,(int)数组_和($weightedValues));
    foreach($key=>$value的加权值){
    $rand-=$value;
    if($rand您可以使用from。它接受对(项、权重)的列表,以便能够处理不能作为数组键的项。您可以使用函数将
    数组(项=>权重)
    转换为所需的格式

    /**
     * @param array $weightedValues
     * @return string
     */
    function getRandomWeightedElement(array $weightedValues)
    {
        $array = array();
    
        foreach ($weightedValues as $key => $weight) {
            $array = array_merge(array_fill(0, $weight, $key), $array);
        }
    
        return $array[array_rand($array)];
    }
    
    在本例中,2-5的出现频率是7-10的3倍。

    我刚刚轻松发布了一个

    它基于和answers中提到的相同算法,并针对速度进行了优化,针对均匀分布进行了单元测试,并支持任何PHP类型的元素

    使用它很简单。请实例化它:

    $picker=newbrick\Random\RandomPicker();
    
    然后将元素添加为加权值数组(仅当元素是字符串或整数时):

    $picker->addElements([
    ‘foo’=>25,
    'bar'=>50,
    ‘baz’=>100
    ]);
    
    或者使用对
    addElement()
    的单独调用。此方法支持任何类型的PHP值作为元素(字符串、数字、对象等),而不是数组方法:

    $picker->addElement($object1,$weight1);
    $picker->addElement($object2,$weight2);
    
    然后得到一个随机元素:

    $element=$picker->getRandomElement();
    
    获取其中一个元素的概率取决于其关联的权重。唯一的限制是权重必须是整数

    foreach ($values as $amount) {
        $total += $amount;
    }
    
    $rand = mt_rand(0, $total-1);
    
    foreach ($values as $amount) {
        $currentTotal += $amount;
    
        if ($rand => $currentTotal) {
            $bucket++;
        }
        else {
            break;
        }
    }
    
    return $bucket;
    
    getRandomWeightedElement(数组('A'=>10,'B'=>90));


    这是一个非常简单的方法。如何获取随机加权元素。我填充数组变量$key。我获取数组$key到数组$weight x。然后,使用数组\u rand到数组。我有随机值;)。

    函数getBucketFromWeights($value){ $total=$currentTotal=$bucket=0

    $values_and_weights=array(
        1=>1,
        2=>1,
        3=>2,
        4=>2,
        5=>2,
        6=>1,
        7=>1,
        8=>1,
        9=>1,
        10=>1
    );
    
    }

    我根据这里的答案修改了这个


    在我写了这篇文章之后,我看到其他人有了一个更加优雅的答案。他。

    这一页上的许多答案似乎使用了数组膨胀、过度迭代、库或难以阅读的过程。当然,每个人都认为自己的宝宝是最可爱的,但我诚实地认为我的方法是精简、简单和易于阅读/修改的…

    根据操作,我将创建一个从1到10的值数组(声明为键),其中3、4和5的权重是其他值(声明为值)的两倍

    如果您只打算进行一次随机选择,并且/或者您的阵列相对较小*(请自行进行基准测试),这可能是您的最佳选择:

    // Declare new array using array_walk one-liner:
    array_walk($values_and_weights,function($v,$k)use(&$limits_and_values,&$x){$limits_and_values[$x+=$v]=$k;});
    
    //Alternative declaration method - 4-liner, foreach() loop:
    /*$x=0;
    foreach($values_and_weights as $val=>$wgt){
        $limits_and_values[$x+=$wgt]=$val;
    }*/
    var_export($limits_and_values);
    
    这种方法不涉及数组修改,可能不需要迭代整个数组(但可能需要)


    另一方面,如果您要在阵列上进行多个随机选择,并且/或者您的阵列足够大*(请自行进行基准测试,以确保
    foreach ($values as $amount) {
        $total += $amount;
    }
    
    $rand = mt_rand(0, $total-1);
    
    foreach ($values as $amount) {
        $currentTotal += $amount;
    
        if ($rand => $currentTotal) {
            $bucket++;
        }
        else {
            break;
        }
    }
    
    return $bucket;
    
    $values_and_weights=array(
        1=>1,
        2=>1,
        3=>2,
        4=>2,
        5=>2,
        6=>1,
        7=>1,
        8=>1,
        9=>1,
        10=>1
    );
    
    $pick=mt_rand(1,array_sum($values_and_weights));
    $x=0;
    foreach($values_and_weights as $val=>$wgt){
        if(($x+=$wgt)>=$pick){
            echo "$val";
            break;
        }
    }
    
    // Declare new array using array_walk one-liner:
    array_walk($values_and_weights,function($v,$k)use(&$limits_and_values,&$x){$limits_and_values[$x+=$v]=$k;});
    
    //Alternative declaration method - 4-liner, foreach() loop:
    /*$x=0;
    foreach($values_and_weights as $val=>$wgt){
        $limits_and_values[$x+=$wgt]=$val;
    }*/
    var_export($limits_and_values);
    
    array (
      1 => 1,
      2 => 2,
      4 => 3,
      6 => 4,
      8 => 5,
      9 => 6,
      10 => 7,
      11 => 8,
      12 => 9,
      13 => 10,
    )
    
    // $x (from walk/loop) is the same as writing: end($limits_and_values); $x=key($limits_and_values);
    $pick=mt_rand(1,$x);  // pull random integer between 1 and highest limit/key
    while(!isset($limits_and_values[$pick])){++$pick;}  // smallest possible loop to find key
    echo $limits_and_values[$pick];  // this is your random (weighted) value