Generate Random Weighted value

前端 未结 6 1463
隐瞒了意图╮
隐瞒了意图╮ 2020-12-28 10:19

Edit: I\'ve rewritten the question in hopes that the goal is a little clearer.

This is an extended question to this question here, and I really like

6条回答
  •  情书的邮戳
    2020-12-28 10:51

    A general technique when generating non-uniform random number is using rejection sampling. Even though it may be ineffective in this case you still should know how to do this, because it works for any density function you provide.

    function random($density, $max) {
        do {
            $rand = lcg_value();
            $rand2 = lcg_value() * $max;
        } while ($density($rand) < $rand2);
        return $rand;
    }
    

    $density here is a density function accepting a floating point number between zero and one as argument and returning a value smaller then $max. For your example this density function could be:

    $density = function($x) {
        static $values = array(
            1 => 0.05,
            2 => 0.15,
            3 => 0.30,
            4 => 0.30,
            5 => 0.15,
            6 => 0.05,
        );
    
        return $values[ceil($x * 6)];
    };
    

    An example call then would be:

    ceil(random($density, 0.3) * 6); // 0.3 is the greatest value returned by $density
    // round and * 6 are used to map a 0 - 1 float to a 1 - 6 int.
    

    Rejection sampling is especially useful if you can't easily calculate the inverse of the distribution. As in this case it is pretty easy to calculate the inverse using inverse transform sampling is probably the better choice. But that is already covered in Jon's answer.

    PS: The above implementation is general and thus uses a random value between 0 and 1. By building a function that only works for your approach everything get's easier:

    function random() {
        static $values = array(
            1 => 0.05,
            2 => 0.15,
            3 => 0.30,
            4 => 0.30,
            5 => 0.15,
            6 => 0.05,
        );
    
        do {
            $rand = mt_rand(1, 6);
            $rand2 = lcg_value() * 0.3;
        } while ($values[$rand] < $rand2);
        return $rand;
    }
    
    random();
    

提交回复
热议问题