我是python的新手,想通过解决生日问题来测试自己。我想对其进行仿真,而不是进行数学计算,以查看是否能得到正确的答案。因此,我将sieve []列表中的所有布尔值都分配为False,然后从0到364中随机选择一个值并将其更改为True,如果已经为True,则输出必须迭代多少次作为答案。 >
由于某种原因,每次我运行代码时,我都会得到一个介于24.5和24.8之间的值
50%的预期结果是23个人,那为什么我的结果比预期高6%?我的代码有错误吗?
import random
def howManyPeople():
sieve = [False] * 365
count = 1
while True:
newBirthday = random.randint(0,364)
if sieve[newBirthday]:
return count
else:
sieve[newBirthday] = True
count += 1
def multipleRun():
global timesToRun
results = []
for i in range(timesToRun):
results.append(howManyPeople())
finalResultAverage = sum(results)
return (finalResultAverage / timesToRun)
timesToRun = int(input("How many times would you like to run this code?"))
print("Average of all solutions = " + str(multipleRun()) + " people")
答案 0 :(得分:13)
您的代码没有错误。当您真正感兴趣的(以及生日悖论告诉您的)是中位数<时,您正在计算howManyPeople
返回值样本的平均值 / em>。
也就是说,您有一个随机过程,您可以在此过程中逐步添加人员,然后在第一次生日碰撞时报告该组中的总人数。生日悖论意味着,至少有50%的时间,您的集合将只有23个或更少的人。这与说集合中的预期人数为23.0或更少不是同一件事。
这是我从您的howManyPeople
函数的一百万个样本中看到的。
In [4]: sample = [howManyPeople() for _ in range(1000000)]
In [5]: import numpy as np
In [6]: np.median(sample)
Out[6]: 23.0
In [7]: np.mean(sample)
Out[7]: 24.617082
In [8]: np.mean([x <= 23 for x in sample])
Out[8]: 0.506978
请注意,这里运气非常好:返回值howManyPeople
的分布的中位数为23
(至少根据Wikipedia的定义),但是有可能一个不寻常的 sample 可能纯粹通过随机性而具有不同的中位数。在这种特殊情况下,这种机会是完全可以忽略的。正如user2357112在评论中指出的那样,在2天的年份示例中,情况有些混乱,其中2.0
和3.0
(包括)之间的任何实数都是有效的分布中位数,并且我们可以合理地期望样本中位数为2
或3
。
我们也可以直接计算howManyPeople
的每个输出的概率:对于任何正整数k
,输出严格大于k
的概率为与第一个k
人有不同生日的概率(由Python factorial(365)/factorial(k)/365**k
给出)的概率相同,我们可以用它来计算各个输出的概率。在这里,我为X
表示的随机变量使用名称howManyPeople
。一些效率低下的代码:
from math import factorial
def prob_X_greater_than(k):
"""Probability that the output of howManyPeople is > k."""
if k <= 0:
return 1.0
elif k > 365:
return 0.0
else:
return factorial(365) / factorial(365 - k) / 365**k
def prob_X_equals(k):
"""Probability that the output of howManyPeople is == k."""
return prob_x_greater_than(k-1) - prob_x_greater_than(k)
有了这个,我们可以得到精确的(好吧,精确到数值误差)平均值,并验证它与我们从样本中获得的结果大致相符:
In [18]: sum(k*prob_x_equals(k) for k in range(1, 366))
Out[18]: 24.616585894598863
在这种情况下的生日悖论应该告诉我们k <= 23
的概率之和大于0.5
:
In [19]: sum(prob_x_equals(k) for k in range(1, 24))
Out[19]: 0.5072972343239854
答案 1 :(得分:10)
您看到的是正常现象。在一个由23个人组成的房间中重复生日的可能性可能大于50%(忽略leap年和不均匀的生日分布),但这并不意味着如果您一个人一个房间地增加一个人,则意味着得到副本的点将是23。
要对此有一个直观的感觉,请想象如果几年只有两天。在这种情况下,很明显,在有2个人的房间里有一个重复生日的机会是50%。但是,如果您将一个随机的人一个接一个地添加到房间中,则至少需要两个人-50%的机会在2点停下来,而50%的机会停在3点。平均停顿点是2.5,而不是2。