python与概率简单实践——掷骰子
独立掷一六面骰子(即点数为1,2,3,4,5,6),记录每次掷出的点数,当点数大于20时,停止投掷,问投掷次数的期望?
推而广之,设面数为x,设定的点数和为y,满足此题意的期望为多少?
不妨先将问题简化一下,两面的骰子,大于3停止,此时可以用人脑穷举

都满足题意
简单计算可得期望为,投掷次数的加权平均,投掷次数越多,权值越小
(相当于多投掷了,得到这个结果的概率也就小,本题中得到任一结果的概率为(1/2)^n,n为投掷次数,2为骰子面数
(注意这些结果的概率和可不唯一,因为我们只是筛选出了一些合题意的结果,加权平均的真正目的是每种投掷情况出现的概率不一样)
结果为:2.875)
我们考虑用计算机模拟
两种思路:
1,暴力穷举出所有情况,求加权平均
2,用生成随机数的功能模拟投掷过程,用频率逼近概率
思路1代码如下:
#穷举
import numpy as np
surface_number=2 #设定骰子面数
define_sum=3 #设定超过的值,大于此值即停止投掷
success_list=[] #记录所有穷举出合题意的结果
def method_of_exhaustion(lists): #此函数用来穷举
for i in range(surface_number,0,-1):
#从面数到1循环(1到面数也可),覆盖所有的情况
if sum(lists)+i>define_sum:
#原来的和加上某点超过规定值,则合适,记录入succee_list
success_list.append(lists+[i])
else:
method_of_exhaustion(lists+[i])
#加上还不够,加上此值后递归运行次函数继续掷骰子
medium=[]#从没有掷骰子开始
method_of_exhaustion(medium)#运行函数
#print(success_list) #显示结果(因后续情况可能过于冗长,此处注释掉)
#print(len(success_list)) #结果数
#需要求加权平均值的数据列表
elements = []
#对应的权值列表
weights = []
for i in success_list:
length=len(i)
elements.append(length)
weights.append((1/surface_number)**(length))
a=np.average(elements, weights=weights)
print(a)
'''
验证数据的正确性,可自行验证,此处注释掉
每个单独的结果需要满足:
1,和大于规定值
2,除去最后一个数,和小于规定值
ha=0
for i in success_list:
if sum(i)<=20:
ha=1
medium=i
medium.pop()
if sum(medium)>20:
ha=1
print(ha)#ha为0说明正确
'''
其中用到了numpy求加权平均

思路2代码如下:
import random
#用随机数模拟
experiment=10 #模拟次数
tries=5000#每次模拟实验次数
surface_number=2 #设定骰子面数
define_sum=3 #设定和超过多少停止
for _ in range(experiment):
success_list=[] #用于收集所有合法的结果
for _ in range(0,tries):
medium = [] #单次实验前要清空
while True:
value=random.randint(1,surface_number) #等概率生成随机数
medium.append(value)
if sum(medium)>define_sum:#直到大于所规定值,停止投掷
success_list.append(medium)
break
sums=0
for i in success_list:
sums+=len(i)
print(sums/len(success_list))#期望值
我们选择模拟10次,每次有5000组平行实验,下面看输出结果:

观察到每次实验都很接近理论值
进一步,我们可以任意修改surface_number,define_sum
来任意设定骰子面数以及和超过多少停止
比如6面骰子,大于20停止(不包括20),仍是上述代码,修改参数即可:


可以看到两者十分接近
数学推导待续