【原创】给定随机数的取值范围(最小值、最大值),且要求多次取得的随机数最后的结果有一个固定的平均值
给定随机数的取值范围(最小值、最大值),且要求多次取得的随机数最后的结果有一个固定的平均值。
算法如下:
1 /****** 2 * author ztg 281099678 2018-12-06 3 * @param $min float 范围最小值 4 * @param $max float 范围最大值 5 * @param $avg float 最后的平均数 6 * @return float 结果随机数 7 */ 8 function avg_rand($min, $max, $avg) { 9 $maxAvg = ($max + $avg) / 2; // 取得大区间的平均数 10 $minAvg = ($min + $avg) / 2; // 取得小区间的平均数 11 12 if ($maxAvg - $avg == 0) { 13 return $max; 14 } 15 if ($avg - $minAvg == 0) { 16 return $min; 17 } 18 $multiple = ($avg - $minAvg) / ($maxAvg - $avg); //小区间的平均数与平均数的差距,是大区间与平均数的差距的多少倍? 19 20 /*** 为了更精确得到随机数,下面的取值,都*100 ***/ 21 $baseArea = 1000; //区间的基础范围是多少(随意设定,稍微大点更精确)? 22 $rMax = $baseArea + $multiple * $baseArea; //规划区间范围,在基础范围内加另外多余倍数的区间 23 24 $r = rand(0, $rMax); //区间随机取一个值 25 26 //打印测试 27 echo 'maxAvg:' . $maxAvg . ' -- minAvg:' . $minAvg . ' -- min:' . $min . ' -- max:' . $max . ' -- avg:' . $avg . ' -- rMax:' . $rMax . ' -- multiple:' . $multiple . ' -- r:' . $r ;echo "\n"; 28 //如果随机值在大区间内,则为小数额;否则为大数额 29 if ($r > $multiple * $baseArea) { 30 $i = rand($min * 100, $avg * 100) / 100; 31 } else { 32 $i = rand($avg * 100 + 1, $max * 100 ) / 100; 33 } 34 35 return $i; 36 } 37 38 //测试 39 $res = $resMax = $resMin = []; 40 for ($i = 0; $i < 1000; $i ++) { 41 $min = 1; 42 $avg = 9.2; 43 $avg = 2.2; 44 // $avg = 6; 45 // $avg = 1; 46 // $avg = 11; 47 $max = 11; 48 $re = avg_rand($min, $max, $avg); 49 $res[] = $re; 50 if ($re < $avg) { 51 $resMin[] = $re; 52 } else { 53 $resMax[] = $re; 54 } 55 } 56 57 echo "\n"; 58 echo "\n"; 59 echo 'avg:' . $avg . "\n\n"; 60 echo 'result:'.( array_sum($res) / count($res)) . ' -- sum:' . array_sum($res) . ' -- count:' . count($res) ; 61 echo "\n"; 62 echo "max -- avg:";echo ($max + $avg) / 2; echo ' -- count:'; print_r(count($resMax)); echo ' -- resultAvg:' . (array_sum($resMax) / count($resMax)); 63 echo "\n"; 64 echo 'min -- avg:' ; echo ($avg + $min) / 2; echo ' -- count:'; print_r(count($resMin)); echo ' -- resultAvg:' . (count($resMin) ? (array_sum($resMin) / count($resMin)) : 0); 65 echo "\nresMin:";print_r($resMin); 66 echo "\nresMax:";print_r($resMax); 67 echo "\nres:";print_r($res); 68 69 exit;