在 数论及Python实践一文中,我们介绍了组合的基本定义以及一些常规实现方法,并未充分发挥python语言的优势,本文我们从reduce
函数的角度(从这个角度我们应当恢复reduce
正宫娘娘的地位,因为在python3中Guido将reduce
从系统内置函数降格为functools
中的函数),重新实现给出排列组合的各自实现,以及据此给出”生日问题”的概率解释。
2 n 这不正是二进制嘛!这个结论有什么用?举个栗子,给定三个卡片,编号为 1−3 ,使用该等式可得其会有 2 3 =8 种组合(combinations或叫subsets)(包括空集):
再来考虑这样一种情形,从 n 个数中随机选择 k 个,再从余下的 n−k 个随机选择 p 个,组合数一共多少:
再来看几个结论:
左边允许重复,右边不允许重复;
当我们试图用reduce
实现组合数的计算时,
from functools import reduce
import operator
def fac(n):
return reduce(operator.mul, range(1, n+1), 1)
# 阶乘n!的定义
# reduce与operator.mul结合
def perm(n, k):
return reduce(operator.mul, range(n-k+1, n+1), 1)
# 排列数的定义
def comb(n, k):
return perm(n, k)//fac(k)
def test():
print('{}!={}'.format(5, fac(5)))
print('A_{}^{}={}'.format(5, 2, perm(5, 2)))
print('C_{}^{}={}'.format(5, 2, comb(5, 2)))
if __name__ == '__main__':
test()
运行结果为:
5!=120 A_5^2=20 C_5^2=10
由以上准备,我们可求解概率论史上的经典问题(概率论史上的经典问题一般是指违反直觉的那些问题)”生日问题”:
一次聚会上,只要有23个人,就有50%的可能性其中至少有两个人生日相同,如果人数达到50人,至少有两个人生日相同的概率达到97%。(这个结论很恐怖,只要是班上的人数超过50,老师便可以说,我们班至少有两个人生日相同,其实在人数超过23的时候,我们便可以这么说,应为概率占优,注意,是班上会有来个人生日相同,不是说,班上至少存在一个人生日生日与我相同)。
当然这类问题从其反问题对立事件出发, 1−P(所有人都不在同一天)=P(至少有两人在同一天) ,
这里的 A 50 365 可以理解为,随意指定一个生日,他生日所在的自由度
(或者作可选空间)为365,则下一个人只有365-1的自由度
,依次类推。 365 50 是考虑到这50个人的生日大体独立,也即每一个的生日都有365个自由度
(也即365种选择)。所谓概率,频率的观点(另有贝叶斯的观点)来看就是出现的次数与总的可能性之比。
>>> 1-perm(365, 23)/(365**23)
0.5072972343239854
>>> 1-perm(365, 50)/(365**50)
0.9703735795779884
注意:如果预先指定一个生日,随机选取125人,250人,500人,出现某人生日正好是这一生日的概率分别是:
比想象的要小很多,再次说明概率中的许多问题都比较违反直觉。
补充: