生日悖论在这里发挥作用。如果你从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时逐渐减弱。
我创建了一个谷歌文档电子表格,其中内置此函数作为公式,以便您在此处进行操作:https://docs.google.com/spreadsheets/d/1bNJ5RFBsXrBr_1BEXgWGein4iXtobsNjw9dCCVeI2_8
将其与您的特定问题联系起来
您已经掷了一个90000面骰子500次。上面的电子表格显示,如果假设完全随机的mt_rand,您至少会有75%的几率出现至少一对重复数字。从数学上讲,您的代码执行的操作是从一个带有替换的集合中选择N个元素。换句话说,您从90000个物品的袋子中随机选择一个数字,写下来,然后将其放回袋子中,再选择另一个随机数字,重复500次。听起来您想要所有的数字都是不同的,换句话说,您想要从一个不带替换的集合中选择N个元素。有一些算法可以实现这一点。Dave Chen的建议是洗牌然后切片,这是一个相对简单的方法。Qaribou的Josh的建议是分别拒绝重复项。