function get_slanted_rands() {
$rands = [];
for($i = 1; $i <= 630; $i++) {
// For every 4th iteration, get random from the full range
$rands[] = $i % 4 === 0
? mt_rand(1,20)
: mt_rand(1,4);
}
$sum = array_sum($rands);
// If the sum is over the max, recall:
if($sum > 3000) {
return get_slanted_rands();
}
// Log the average for debugging:
$avg = round($sum / count($rands), 2);
return compact('sum', 'avg', 'rands');
}
Your problem is that 3000 / 630 = 4.76 which is far from the average number between 1-20. What we can do is constrain the random numbers' ranges to accommodate this, and get a distribution slant with an average sum approximate to the maximum sum.
Given the required maximum average of 4.76, we can for example generate 1/4 of the random numbers as between 1-20, and 3/4 as between 1-4 to slant the distribution:
function get_slanted_rands() {
$rands = [];
for($i = 1; $i <= 630; $i++) {
// For every 4th iteration, get random from the full range
$rands[] = $i % 4 === 0
? mt_rand(1,20)
: mt_rand(1,4);
}
$sum = array_sum($rands);
// If the sum is over the max, recall:
if($sum > 3000) {
return get_slanted_rands();
}
// Log the average for debugging:
$avg = round($sum / count($rands), 2);
return compact('sum', 'avg', 'rands');
}
Notice that the function checks the sum and recalls itself on the odd occasion that the sum exceeds 3000. On a typical call, the sum 3000 isn't exceeded with the above "calibration" — since we split 1:3 in favor of the lower range. But random is random, so there's no "average" guaranteed.
Let's test-drive this contraption for 100 iterations and see what we get:
This yields for the following (reverse sorted), sums between 2600+ and 3000 and averages between 4.75 and 4.19. (On average, ~1:100 iterations exceeds 3000 and is recalled.)
You can always tweak the slanting condition if you feel it should on average be closer to 3000... Or simply add a recall condition with a "minimum sum" check and "brute force" it. But this suffices to give you the general idea: You need to generate random numbers of a limited range in proportion to what your "average for target sum" demands. Demo at 3v4l: https://3v4l.org/fG74X
Finally, a sample of the sort of distribution you get. Given as "number" => "instances" in a set of 630. It's heavily slanted to 1, 2, 3, 4 there — and that's how it must be to meet your specs!
发布评论
评论(2)
您的问题是
3000 / 630 = 4.76
与 1-20 之间的平均值相差甚远。我们可以做的是限制随机数的范围以适应这种情况,并获得平均和接近最大和的分布倾斜。给定所需的最大平均值 4.76,我们可以生成
1/4
之间的随机数1-20
和3/4
> 在1-4
之间倾斜分布:请注意,函数检查总和并在总和超过 3000 的奇怪情况下调用自身。在典型的调用中,总和 3000不会超过上述“校准”——因为我们以 1:3 的比例分割以支持较低的范围。但随机就是随机,所以不能保证“平均”。
让我们测试这个装置 100 次迭代,看看我们得到了什么:
这会产生以下结果(反向排序),总和在 2600+ 到 3000 之间,平均值在 4.75 到 4.19 之间。 (平均而言,~1:100 迭代超过 3000 并被召回。)
如果您认为平均应该接近 3000,您可以随时调整倾斜条件...或者简单地添加带有“最小总和”检查的召回条件并“暴力”它。但这足以让您了解总体思路:您需要根据“目标总和的平均值”的要求生成有限范围内的随机数。 3v4l 演示:https://3v4l.org/fG74X
最后,您获得的发行版示例。给出“数字” => 630 个集合中的“实例”。这里严重倾向于 1、2、3、4 — 这就是满足您的规格的方式!
Your problem is that
3000 / 630 = 4.76
which is far from the average number between 1-20. What we can do is constrain the random numbers' ranges to accommodate this, and get a distribution slant with an average sum approximate to the maximum sum.Given the required maximum average of 4.76, we can for example generate
1/4
of the random numbers as between1-20
, and3/4
as between1-4
to slant the distribution:Notice that the function checks the sum and recalls itself on the odd occasion that the sum exceeds 3000. On a typical call, the sum 3000 isn't exceeded with the above "calibration" — since we split 1:3 in favor of the lower range. But random is random, so there's no "average" guaranteed.
Let's test-drive this contraption for 100 iterations and see what we get:
This yields for the following (reverse sorted), sums between 2600+ and 3000 and averages between 4.75 and 4.19. (On average, ~1:100 iterations exceeds 3000 and is recalled.)
You can always tweak the slanting condition if you feel it should on average be closer to 3000... Or simply add a recall condition with a "minimum sum" check and "brute force" it. But this suffices to give you the general idea: You need to generate random numbers of a limited range in proportion to what your "average for target sum" demands. Demo at 3v4l: https://3v4l.org/fG74X
Finally, a sample of the sort of distribution you get. Given as "number" => "instances" in a set of 630. It's heavily slanted to 1, 2, 3, 4 there — and that's how it must be to meet your specs!