如何生成随机数以产生非标准分布 PHP
How to generate random numbers to produce a non-standard distributionin PHP
我搜索了很多类似的问题,但不幸的是我没能找到这个问题的答案。我希望有人能指出我正确的方向。
我需要想出一个 PHP 函数,它会产生一个在设定范围内的随机数和均值。在我的例子中,范围始终是 1 到 100。平均值可以是范围内的任何值。
例如...
r = f(x)
其中...
r = the resulting random number
x = the mean
...运行 这个循环中的函数应该产生随机值,其中结果值的平均值应该非常接近 x。 (循环次数越多越接近x)
运行 循环中的函数,假设 x = 10,应该产生类似于这样的曲线:
+
+ +
+ +
+ +
+ +
曲线从 1 开始,从 10 开始,到 100 结束。
不幸的是,我不太精通统计学。也许有人可以帮助我正确地表达这个问题以找到解决方案?
有趣的问题。我总结一下:
- 我们需要一个函数 f(x)
- f returns 一个整数
- 如果我们运行 f 一百万次整数的平均值是x(或者至少非常接近)
我确信有几种方法,但这使用了二项分布:http://en.wikipedia.org/wiki/Binomial_distribution
代码如下:
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);
}
你可以这样测试(对我有用):
$results = array();
for($i = 0;$i<100;$i++){
$results[] = f(20);
}
$average = array_sum($results) / count($results);
echo $average;
它给出的分布曲线如下所示:
我不确定我是否明白你的意思,即使我没有,这仍然是一个非常简洁的片段:
<?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.
?>
如果你然后 var_dump($res)
,你会得到这样的东西:
array (size=4)
0 => int 18
1 => int 54
2 => int 22
3 => int 4
编辑:为 $leniency
使用较低的值(如 1 或 2)会导致数组很大,自测试以来,我建议宽大处理 3 左右。
我搜索了很多类似的问题,但不幸的是我没能找到这个问题的答案。我希望有人能指出我正确的方向。
我需要想出一个 PHP 函数,它会产生一个在设定范围内的随机数和均值。在我的例子中,范围始终是 1 到 100。平均值可以是范围内的任何值。
例如...
r = f(x)
其中...
r = the resulting random number
x = the mean
...运行 这个循环中的函数应该产生随机值,其中结果值的平均值应该非常接近 x。 (循环次数越多越接近x)
运行 循环中的函数,假设 x = 10,应该产生类似于这样的曲线:
+
+ +
+ +
+ +
+ +
曲线从 1 开始,从 10 开始,到 100 结束。
不幸的是,我不太精通统计学。也许有人可以帮助我正确地表达这个问题以找到解决方案?
有趣的问题。我总结一下:
- 我们需要一个函数 f(x)
- f returns 一个整数
- 如果我们运行 f 一百万次整数的平均值是x(或者至少非常接近)
我确信有几种方法,但这使用了二项分布:http://en.wikipedia.org/wiki/Binomial_distribution
代码如下:
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);
}
你可以这样测试(对我有用): $results = array();
for($i = 0;$i<100;$i++){
$results[] = f(20);
}
$average = array_sum($results) / count($results);
echo $average;
它给出的分布曲线如下所示:
我不确定我是否明白你的意思,即使我没有,这仍然是一个非常简洁的片段:
<?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.
?>
如果你然后 var_dump($res)
,你会得到这样的东西:
array (size=4)
0 => int 18
1 => int 54
2 => int 22
3 => int 4
编辑:为 $leniency
使用较低的值(如 1 或 2)会导致数组很大,自测试以来,我建议宽大处理 3 左右。