我想这是关于SO的第一个问题。
我目前正在一个网站上工作,我必须在1到29之间生成6个数字(每个最大一个)用于抽奖。因为它们可以按任何顺序排列,我只是在事后对它们进行排序。
如果我没弄错的话,那应该意味着(29*28*27*26*25*24) / 6! = 475020
种不同的组合。
我尝试过使用mt_rand或random_int(来自random_compat)生成序列的不同方法,但是当我用10k次迭代测试它时,我总是得到大约100个重复,即使它们像465k组合仍然可用。
以下是我一直在尝试的代码示例:
// Using an array and mt_rand (or random_int, giving same results)
// Also tried shuffling the array instead of simply reindexing it, not better
$values = range(1, 29);
while(count($values) > 6) {
unset($values[mt_rand(0, count($values) - 1)]);
$values = array_values($values);
}
// Creating the array from random numbers (same results using random_int)
$values = array();
while (count($values) < 6) {
$r = mt_rand(1, 29);
if (in_array($r, $values)) {
continue;
} else {
$values[] = $r;
}
}
那么好......我的问题是:
谢谢!
林恩。
PS:看了很多问题,但没有找到满足我需求的任何内容,抱歉,如果我看起来不够好的话!
只是为了清楚地说明一些事情:使用random_int(使用/ dev / urandom或openssl_random_pseudo_bytes)并没有改进任何东西,我认为这样做。如果可能的话,我不想使用任何外部API(如random.org)。
答案 0 :(得分:1)
根据我的计算(bc calculator),获得29个项目中的6个的重复组合的概率为50%或更好,812个序列数。
define p(n, k) { return (n-k)/n; }
n=475020
m=1; for (k=0; k<811; k++) m *= p(n, k); m
.500649663424
m=1; for (k=0; k<812; k++) m *= p(n, k); m
.499794905988
答案 1 :(得分:1)
使用random_int(使用/ dev / urandom或openssl_random_pseudo_bytes)并没有改进任何东西,我认为这样做。
当然可以,它不是你可以直观识别的东西。 mt_rand()
和rand()
仅允许约2 32 可能的种子和2 32 可能的输出,最重要的是,具有确定性序列:if you know a few outputs, you can predict the rest until it's reseeded。
您的操作系统的CSPRNG没有任何此类限制。知道少数random_int()
输出(在PHP中限制为32位系统上的2 32 可能值,64位系统上2 64 )没有给你任何关于未来产出的信息。
我目前正在一个网站上工作,我必须在1到29之间生成6个数字(每个最大一个)用于抽奖。因为它们可以按任何顺序排列,我只是在事后对它们进行排序。
好的,这是一个很好的主意。你肯定想要一个CSPRNG。
当我用10k迭代测试它时,我总是得到大约100个重复,即使它们仍然像465k组合仍然可用。
正如其他人所指出的,这是birthday problem/paradox正在发挥作用。
如果您需要解决方案,请尝试以下方法:
function random_unique_select($num, array $possible_values)
{
$sizeof = count($possible_values);
if ($num > $sizeof) {
throw new InvalidArgumentException('$num is too large');
}
$selected = [];
for ($i = 0; $i < $num; ++$i) {
// Grab a random int [0, ... N - 1]
$r = random_int(0, $sizeof - 1);
// Copy the selected value into $selected
$selected[] = $possible_values[$r];
// Delete it from the range of possible values
unset($possible_values[$r]);
// N has grown smaller by 1
--$sizeof;
// Reset keys; we want this to be zero-indexed.
$possible_values = array_values($possible_values);
}
return $selected;
}
$lottery = random_unique_select(6, range(1,29));
演示:
openssl_random_pseudo_bytes()
)答案 2 :(得分:0)
要改善“随机性”,您可以尝试使用加密库,例如phpseclib
他们的数学库here中有一个random()函数。
编辑:计算机生成的数字不能随机。使用加密库可以得到最好的伪随机结果,最简单,最随机的解决方案是@Matthias Leuffen。
答案 3 :(得分:0)
rand()和mt_rand()依靠纯数学来产生伪随机数。
要获得真实的随机数,您可以使用http://www.random.org
的网络服务答案 4 :(得分:-1)
如果安装了正确的扩展程序,您可以使用openssl_random_pseudo_bytes()
。
示例:
function strong_random() {
return hexdec(bin2hex(openssl_random_pseudo_bytes(20)));
}
注意:由于openssl_random_pseudo_bytes()
的实施,此功能将非常慢。
当然,快速和肮脏可以使用添加最大长度参数。