Generating random results by weight in PHP?

前端 未结 12 701
青春惊慌失措
青春惊慌失措 2020-11-22 07:35

I know how to generate a random number in PHP but lets say I want a random number between 1-10 but I want more 3,4,5\'s then 8,9,10\'s. How is this possible? I would post wh

相关标签:
12条回答
  • 2020-11-22 08:15

    The naive hack for this would be to build a list or array like

    1, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 9, 9, 10, 10

    And then select randomly from that.

    0 讨论(0)
  • 2020-11-22 08:17

    You can use weightedChoice from Non-standard PHP library. It accepts a list of pairs (item, weight) to have the possibility to work with items that can't be array keys. You can use pairs function to convert array(item => weight) to the needed format.

    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);
    

    In this example, 2-5 will appear 3 times more often than 7-10.

    0 讨论(0)
  • 2020-11-22 08:21

    This tutorial walks you through it, in PHP, with multiple cut and paste solutions. Note that this routine is slightly modified from what you'll find on that page, as a result of the comment below.

    A function taken from the post:

    /**
     * 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]; 
    }
    
    0 讨论(0)
  • 2020-11-22 08:21

    I just released a class to perform weighted sorting easily.

    It's based on the same algorithm mentioned in Brad's and Allain's answers, and is optimized for speed, unit-tested for uniform distribution, and supports elements of any PHP type.

    Using it is simple. Instantiate it:

    $picker = new Brick\Random\RandomPicker();
    

    Then add elements as an array of weighted values (only if your elements are strings or integers):

    $picker->addElements([
        'foo' => 25,
        'bar' => 50,
        'baz' => 100
    ]);
    

    Or use individual calls to addElement(). This method supports any kind of PHP values as elements (strings, numbers, objects, ...), as opposed to the array approach:

    $picker->addElement($object1, $weight1);
    $picker->addElement($object2, $weight2);
    

    Then get a random element:

    $element = $picker->getRandomElement();
    

    The probability of getting one of the elements depends on its associated weight. The only restriction is that weights must be integers.

    0 讨论(0)
  • 2020-11-22 08:22

    Based on @Allain's answer/link, I worked up this quick function in PHP. You will have to modify it if you want to use non-integer weighting.

      /**
       * 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;
          }
        }
      }
    
    0 讨论(0)
  • 2020-11-22 08:24

    Plain and fair. Just copy/paste and test it.

    /**
     * Return weighted probability
     * @param (array) prob=>item 
     * @return key
     */
    function weightedRand($stream) {
        $pos = mt_rand(1,array_sum(array_keys($stream)));           
        $em = 0;
        foreach ($stream as $k => $v) {
            $em += $k;
            if ($em >= $pos)
                return $v;
        }
    
    }
    
    $item['30'] = 'I have more chances than everybody :]';
    $item['10'] = 'I have good chances';
    $item['1'] = 'I\'m difficult to appear...';
    
    for ($i = 1; $i <= 10; $i++) {
        echo weightedRand($item).'<br />';
    }
    

    Edit: Added missing bracket at the end.

    0 讨论(0)
提交回复
热议问题