您是否有理由尝试模拟此问题而不是使用封闭形式的解决方案来解决此问题?有一个相当不错的近似值,它快速且易于编码:
import math
def closed_form_approx_birthday_collision_probability(num_people):
return 1 - math.exp(-num_people * (num_people - 1) / (2 * 365.0))
您还可以实现一个非常好的“精确”解决方案(在引号中,因为在转换为浮点数时会丢失一些保真度):
import operator
import functools
import fractions
def slow_fac(n):
return functools.reduce(operator.mul, range(2, n+1), 1)
def closed_form_exact_birthday_collision_probability(num_people):
p_no_collision = fractions.Fraction(slow_fac(365), 365 ** num_people * slow_fac(365 - num_people))
return float(1 - p_no_collision)
要进行模拟,你会做这样的事情。我使用的是列表而不是集合,因为可能性的数量很小,这避免了使用集合会做的一些额外工作:
import random
def birthday_collision_simulate_once(num_people):
s = [False] * 365
for _ in range(num_people):
birthday = random.randint(0, 364)
if s[birthday]:
return True
else:
s[birthday] = True
return False
def birthday_collision_simulation(num_people, runs):
collisions = 0
for _ in range(runs):
if birthday_collision_simulate_once(num_people):
collisions += 1
return collisions / float(runs)
我从模拟和封闭形式解决方案中获得的数字看起来类似于http://en.wikipedia.org/wiki/Birthday_problem上的表格
>>> closed_form_approx_birthday_collision_probability(20)
0.40580512747932584
>>> closed_form_exact_birthday_collision_probability(20)
0.41143838358058
>>> birthday_collision_simulation(20, 100000)
0.41108
当然,运行这么多的模拟更接近实际的 41.1%,计算速度要慢得多。我会选择一种封闭形式的解决方案,具体取决于它需要有多准确。