简体   繁体   中英

How to generate random numbers to produce a non-standard distributionin PHP

I've searched through a number of similar questions, but unfortunately I haven't been able to find an answer to this problem. I hope someone can point me in the right direction.

I need to come up with a PHP function which will produce a random number within a set range and mean. The range, in my case, will always be 1 to 100. The mean could be anything within the range.

For example...

r = f(x)

where...

r = the resulting random number

x = the mean

...running this function in a loop should produce random values where the average of the resulting values should be very close to x. (The more times we loop the closer we get to x)

Running the function in a loop, assuming x = 10, should produce a curve similar to this:

     +
    + +
   +     +
  +             +     
+                               +

Where the curve starts at 1, peeks at 10, and ends at 100.

Unfortunately, I'm not well versed in statistics. Perhaps someone can help me word this problem correctly to find a solution?

interesting question. I'll sum it up:

  1. We need a funcion f(x)
  2. f returns an integer
  3. if we run fa million times the average of the integer is x(or very close at least)

I am sure there are several approaches, but this uses the binomial distribution: http://en.wikipedia.org/wiki/Binomial_distribution

Here is the code:

function f($x){
    $min = 0;
    $max = 100;
    $curve = 1.1;
    $mean = $x;
    $precision = 5; //higher is more precise but slower

    $dist = array();

    $lastval = $precision;
    $belowsize = $mean-$min;
    $abovesize = $max-$mean;
    $belowfactor = pow(pow($curve,50),1/$belowsize);

    $left = 0;
    for($i = $min; $i< $mean; $i++){
        $dist[$i] = round($lastval*$belowfactor);
        $lastval = $lastval*$belowfactor;
        $left += $dist[$i];
    }
    $dist[$mean] = round($lastval*$belowfactor);

    $abovefactor = pow($left,1/$abovesize);
    for($i = $mean+1; $i <= $max; $i++){
        $dist[$i] = round($left-$left/$abovefactor);
        $left = $left/$abovefactor;
    }

    $map = array();
    foreach ($dist as $int => $quantity) {
        for ($x = 0; $x < $quantity; $x++) {
            $map[] = $int;
        }
    }

    shuffle($map);
    return current($map);
}

You can test it out like this(worked for me): $results = array();

for($i = 0;$i<100;$i++){
    $results[] = f(20);
}
$average = array_sum($results) / count($results);
echo $average;

It gives a distribution curve that looks like this: 分布曲线

I'm not sure if I got what you mean, even if I didn't this is still a pretty neat snippet:

<?php
    function array_avg($array) {  // Returns the average (mean) of the numbers in an array
        return array_sum($array)/count($array);
    }

    function randomFromMean($x, $min = 1, $max = 100, $leniency = 3) {

        /*
            $x          The number that you want to get close to
            $min        The minimum number in the range
            $max        Self-explanatory
            $leniency   How far off of $x can the result be
        */

        $res = [mt_rand($min,$max)];
        while (true) {
            $res_avg = array_avg($res);

            if ($res_avg >= ($x - $leniency) && $res_avg <= ($x + $leniency)) {
                return $res;
                break;
            }
            else if ($res_avg > $x && $res_avg < $max) {
                array_push($res,mt_rand($min, $x));
            }
            else if ($res_avg > $min && $res_avg < $x) {
                array_push($res, mt_rand($x,$max));
            }
        }
    }

    $res = randomFromMean(22);  // This function returns an array of random numbers that have a mean close to the first param.
?>

If you then var_dump($res) , You get something like this:

array (size=4)
  0 => int 18
  1 => int 54
  2 => int 22
  3 => int 4

EDIT: Using a low value for $leniency (like 1 or 2) will result in huge arrays, since testing, I recommend a leniency of around 3.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM