Search code examples
phprandomprobability

Generate random numbers with fix probability


I red a lot in the forum about this, but all answers were so specific to the the asked question. The nearest one I found to my need was:Probability Random Number Generator by Alon Gubkin.

The difference is that, Alon ask to give a one face (which is six) extra chance. In my case, I want to divide the chance for the six faces so that they add up to 100%. For example, face 1 has chance of 40%, face 2 has only 10%, face 3 has 25%, ... etc.

How can I do that?


Solution

  • The single probability check with linear probability can be easily done with:

    function checkWithProbability($probability=0.1, $length=10000)
    {
       $test = mt_rand(1, $length);
       return $test<=$probability*$length;
    }
    

    For example, this will produce:

    for($i=0; $i<10; $i++)
    {
       var_dump(checkWithProbability(1/3));
    }
    

    Something like:

    bool(false)
    bool(true)
    bool(false)
    bool(false)
    bool(false)
    bool(false)
    bool(false)
    bool(false)
    bool(true)
    bool(false)
    

    And you can use that principle to get your edges check with desired probability:

    function checkWithSet(array $set, $length=10000)
    {
       $left = 0;
       foreach($set as $num=>$right)
       {
          $set[$num] = $left + $right*$length;
          $left = $set[$num];
       }
       $test = mt_rand(1, $length);
       $left = 1;
       foreach($set as $num=>$right)
       {
          if($test>=$left && $test<=$right)
          {
             return $num;
          }
          $left = $right;
       }
       return null;//debug, no event realized
    }
    

    The idea is to use geometry probability - i.e. split some line part into pieces with corresponding length and then check to which part our random number belongs.

                     0.75  0.9
                      |    |
                      V    V
    *--------*--*-----*-*--*--* <-- (length)
    ^        ^  ^       ^     ^
    |        |  |       |     |
    0      0.4 0.5     0.8    1
    

    Sample will be:

    $set = [
      1 => 0.4,
      2 => 0.1,
      3 => 0.25,
      4 => 0.05,
      5 => 0.1,
      6 => 0.1
    ];
    for($i=0; $i<10; $i++)
    {
       var_dump(checkWithSet($set));
    }
    

    With result like:

    int(1)
    int(2)
    int(2)
    int(6)
    int(3)
    int(1)
    int(1)
    int(6)
    int(1)
    int(1)
    

    You can increase $length - in theory, this will increase "quality" of randomize check, but that's not too easy thing - because mt_rand() uses pseudo-random generator, Mersenne Twister (and in ideal case that's not true linear probability)