生日悖论,如果房间内有23人以上,那么至少两人生日相同
的概率要大于50%。
我们来写个代码,模拟下结果。当然,可以从概率学上,写数学公式进行计算,但是,先假设我们不知道概率学,纯粹就是用计算机代码去模拟。
def create_birthday_list(people_count=23):
birthday_list = []
for i in range(people_count):
birthday_list.append(random.randint(1, 365))
return birthday_list
def simulate(total_times=100000, people_count=23):
hit_times = 0
for i in xrange(total_times):
birthday_list = create_birthday_list(people_count)
if has_same_birthday(birthday_list):
hit_times += 1
percent = round(100 * hit_times/float(total_times), 2)
return percent
if __name__ == '__main__':
t1 = time.time()
percent = simulate()
time_cost = time.time() - t1
print('percent: {0}%, time cost: {1}'.format(percent, time_cost))
其中 has_same_birthday
还没有定义,我们先从比较自然的方式去构建这个函数:
def has_same_birthday(birthday_list):
for day in birthday_list:
if birthday_list.count(day) > 1:
return True
return False
本质上就是一个 list 内有没有重复的元素,所以我们还可以使用 set 这个数据类型自动去重,然后比对 length 是否变化了,变化了自然就是有重复的生日:
def has_same_birthday(birthday_list):
if len(birthday_list) != len(set(birthday_list)):
return True
else:
return False
上面两种 has_same_birthday
,性能相差并不明显,当然使用 set 的会快一些,而且性能是稳定的,通过 for 循环,再 count 一个元素是否大于 1,性能是不稳定的,但是总体上又相差不大。
两种方式,都挺值得参考,一个是自然的逻辑,一个则是换下角度去看待并利用 Python 自带的数据类型来完成。
也有人或许会写成类似下面的代码:
def has_same_birthday(birthday_list):
for i, i_day in enumerate(birthday_list):
for j, j_day in enumerate(birthday_list):
if i_day == j_day and i != j:
return True
return False
如何判定一个 list 内有无重复元素? 给这个 list 做两次循环,相当于两个一模一样的 list 进行逐个比对。这种双重遍历,其实是比较高级的用法,但相对而言,大多数时候是用不到的。
新手也会自然地用到高级一些的写法,可能纯属巧合,巧合之后,又很可能是滥用,显得逻辑奇葩。比如此处多重遍历在一起,它的性能明显会低很多,除了增加费解之外,又没有其它好处。但另外一方面,性能 低很多
也未必真的会低多少,你可以自己跑一跑代码,看最终耗费的时间,就明白了。
这也就是我们不建议把代码写成 倒金字塔 的一个重要原因,对于初学者来说,倒金字塔的代码,基本上意味着这里可能存在问题。
但是,事无绝对,把它当做一件事,但也不要因此就用这条规则束缚了自己。