我只是想知道,如果你绘制5位数字,mt_rand()数字有多独特? 在这个例子中,我尝试用这个函数得到500个随机数的列表,其中一些是重复的。
http://www.php.net/manual/en/function.mt-rand.php
<?php
header('Content-Type: text/plain');
$errors = array();
$uniques = array();
for($i = 0; $i < 500; ++$i)
{
$random_code = mt_rand(10000, 99999);
if(!in_array($random_code, $uniques))
{
$uniques[] = $random_code;
}
else
{
$errors[] = $random_code;
}
}
/**
* If you get any data in this array, it is not exactly unique
* Run this script for few times and you may see some repeats
*/
print_r($errors);
?>
可能需要多少位数才能确保循环中绘制的前500个随机数是唯一的?
答案 0 :(得分:7)
如果数字是真正随机的,那么数字将被重复的概率。没有多少数字并不重要 - 添加更多数字会使得重复的可能性降低,但它总是有可能。但
你最好先检查一下是否存在冲突,然后循环直到不是这样:
$uniques = array();
for($i = 0; $i < 500; $i++) {
do {
$code = mt_rand(10000, 99999);
} while(in_array($code, $uniques));
$uniques[] = $code
}
答案 1 :(得分:5)
为什么不使用范围,随机播放和切片?
<?php
$uniques = range(10000, 99999);
shuffle($uniques);
$uniques = array_slice($uniques, 0, 500);
print_r($uniques);
Array
(
[0] => 91652
[1] => 87559
[2] => 68494
[3] => 70561
[4] => 16514
[5] => 71605
[6] => 96725
[7] => 15908
[8] => 14923
[9] => 10752
[10] => 13816
*** truncated ***
)
此方法较便宜,因为每次都不搜索数组以查看是否已添加该项目。也就是说,它确实使这种方法不那么“随机”。应提供有关这些数字将在何处使用的更多信息。如果这是一个在线赌博网站,这将是最糟糕的!但是,如果这用于为星座网站返回“幸运”号码,我认为这样会很好。
此外,可以扩展此方法,将shuffle方法更改为使用mt_rand(其中原始方法仅使用rand)。它也可能使用openssl_random_pseudo_bytes,但这可能有点过分。
答案 2 :(得分:3)
生日悖论在这里发挥作用。如果您从10000-99999中抽取500次随机数,则很有可能出现重复。
小数字直观的想法
如果你翻了两次硬币,你会在一半时间内获得一份副本。如果您将六面模具滚动两次,那么您将获得1/6的重复时间。如果你滚动3次,你会得到重复的4/9(44%)时间。如果你滚动4次,你将获得至少一个重复的13/18(63.33%)。第五次滚动它是49/54(90.7%)。第六次滚动它是98.5%。第七次滚动它是100%。
如果用20面模具取代六面模具,概率会慢一些,但增长速度会慢一些。在3次掷骰后,您有14.5%的重复几率。 6轮后,它的比例为69.5%。经过10次滚动后,它几乎达到了96.7%。
数学
让我们定义一个函数f(num_rolls, num_sides)
,将其概括为任何随机数生成器的任意数量的卷,这些随机数生成器选择有限的一组选择。我们将f(num_rolls, num_sides)
定义为在num_rolls
侧死亡的num_sides
中没有重复的概率。
现在我们可以尝试为此构建递归定义。要获得num_rolls
个唯一号码,您需要首先滚动num_rolls-1
个唯一号码,然后再滚动一个唯一号码,现在已经取num_rolls-1
个号码。因此
f(num_rolls, num_sides) =
f(num_rolls-1, num_sides) * (num_sides - (num_rolls - 1)) / num_sides
可替换地,
f(num_rolls + 1, num_side) =
f(num_rolls, num_sides) * (num_sides - num_rolls) / num_sides
此函数遵循逻辑衰减曲线,从1开始并且移动非常缓慢(因为num_rolls
非常低,每步的变化非常小),然后慢慢加速num_rolls
随着函数的值越来越接近0,最终逐渐减少。
我已经创建了一个Google文档电子表格,该电子表格内置了此功能,可让您在此处使用此功能:https://docs.google.com/spreadsheets/d/1bNJ5RFBsXrBr_1BEXgWGein4iXtobsNjw9dCCVeI2_8
将此问题与特定问题联系起来
您已经生成500倍的90000双面模具。上面的电子表格表明,假设一个完全随机的mt_rand,大约75%的时间内至少会有一个重复对。在数学上,您的代码执行的操作是从具有替换 的集合中选择N个元素。换句话说,你从90000的东西中挑出一个随机数字,写下来,然后把它放回袋子里,然后挑选另一个随机数,重复500次。听起来你希望所有的数字都是截然不同的,换句话说你想要从一组中选择N个元素,而无需替换 。有一些算法可以做到这一点。 Dave Chen提出的洗牌和切片的建议是相对简单的。来自Qaribou的Josh建议分别拒绝重复是另一种可能性。
答案 3 :(得分:1)
您的问题涉及&#34;生日问题&#34;询问班上是否有N名学生,至少有两名学生生日相同的概率是多少?请参阅Wikipedia: The "Birthday Problem"。
您可以轻松修改此处显示的公式以解答您的问题。您可以在10000和99999之间生成90001(= 99999-10000 + 2)个同样可能的整数,而不是每个学生的生日有365个同样可能的可能性。如果您生成500个这样的数字至少两个数字是相同的:
P(500)= 1- 90001! /(90001 ^ n(90001 - 500)!)= 0.75
因此,有75%的可能性,您生成的500个数字中至少有两个会相同,或者换句话说,只有25%的几率使用您的方法成功获得500个不同的数字目前正在使用。
正如其他人已经建议的那样,我建议您检查算法中的重复数字,而不是盲目地生成随机数,并希望您在任何一对数字之间不匹配。