题目:假如有了一个从自然数中随机取一个数的函数rand(0,7)——从0到7中随机取一个自然数出来,问如何用这个函数构造rand(0,10)——随机从0到10中取一个自然数出来。
PS:该题目是在rand(10)没有现成函数的前提下实现的,必须通过rand(7)去构造
(提示:需要注意函数的分布,可以用蒙特卡洛的方法进行验证)
错误思路:
设A为rand(0,7),B为rand(0,7),这两个都是从0到7中随机取得数,然后把取到的结果相加,如果结果大于10,就重新执行,否则就返回相加的结果。python实现代码如下:
import random
def rand_10():
C = 11
while C>10:
A = random.randint(0,7)
B = random.randint(0,7)
C = A+B
return C
python执行0次的获得的结果如下:
这种解题思路获得的结果是0到10之间的自然数,但是结果却是不对的,先看正确的解题思路,再说明为什么这样取值不对。
正确思路:
设A为rand(0,7),B为rand(0,7),这两个都是从0到7中随机取得数。A作为高位(十位),B作为低位(个位),计算A*8+B的值,得到的是63以内的整数,得到的数据仍为均匀分布,然后我们把结果模11,得到0到10以内的自然数,但是63模11结果为8,那么0~8比9、10结果就会多一次,所以我们只取0~54的数,去模11,代码如下:
def rand_10_2():
rand_all = 55
while rand_all >= 55: #A*8+B最大取值为63,模11得8,0~8比9,10就会多一次,所以计算到54,每个数平均出现5次
A = random.randint(0,7) #8进制高位
B = random.randint(0, 7) #8进制低位
rand_all = A*8+B
C = rand_all%11 #0~10的自然数,包含0和10
return C
也执行20次 ,得到的数都是0到10之间的整数。
, 验证结果:
要验证两种结果哪种是对的,首先我们得明白,rand函数是从一组数中随机取一个数的函数,那么它要服从均匀分布,这样才能保证每个数取到的概率都一样。我们用蒙特卡洛方法进行验证,即模拟100000或更多次数,然后统计每个数出现的次数,看统计出的结果的直方图,从直方图就能看出是不是服从均匀分布。
1、验证系统自带的函数服从的分布,代码如下:
import random
import numpy as np
import matplotlib.pyplot as plt
y = [0]*8 # 存放rand函数得到的值的个数
for i in range(100000): # 十万个数据中取值,进行验证
num_1 = random.randint(0,7)
for j in range(0,8):
if j == num_1:
y[j] +=1
x = np.linspace(0, 7, num=8)
plt.bar(x, y, align='center', alpha=0.7)
plt.xlabel('num for rand')
plt.ylabel('count for rand')
plt.title('rand(7)')
plt.show() #用直方图显示得到的结果
运行后得到的直方图如下:
8个数得到的结果基本相等,说明python 系统自带的函数是服从均匀分布的。
2、两个rand函数相加的方法得到rand(10)的方法,用该方法进行验证
代码如下:
import random
import numpy as np
import matplotlib.pyplot as plt
def rand_10():
C = 11
while C>10:
A = random.randint(0,7)
B = random.randint(0,7)
C = A+B
return C
y = [0]*11 # 存放rand函数得到的值的个数
for i in range(150000): # 十万个数据中取值,进行验证
num_1 = rand_10()
for j in range(0,11):
if j == num_1:
y[j] +=1
x = np.linspace(0, 10, num=11)
plt.bar(x, y, align='center', alpha=0.7)
plt.xlabel('num for rand')
plt.ylabel('count for rand')
plt.title('rand(10)_add')
plt.show() #用直方图显示得到的结果
运行后得到的直方图如下:
从图中可以看出该分布不是均匀分布,而是服从泊松分布即正态分布的,至于为什么服从这种分布,后面有时间再讲解。
3、高位*进制+低位的方法得到的结果进行验证
def rand_10_2():
rand_all = 55
while rand_all >= 55: #A*8+B最大取值为63,模11得8,0~8比9,10就会多一次,所以计算到54,每个数平均出现5次
A = random.randint(0,7) #8进制高位
B = random.randint(0, 7) #8进制低位
rand_all = A*8+B
C = rand_all%11 #0~10的自然数,包含0和10
return C
y = [0]*11 # 存放rand函数得到的值的个数
for i in range(150000): # 十万个数据中取值,进行验证
num_1 = rand_10_2()
for j in range(0,11):
if j == num_1:
y[j] +=1
x = np.linspace(0, 10, num=11)
plt.bar(x, y, align='center', alpha=0.7)
plt.xlabel('num for rand')
plt.ylabel('count for rand')
plt.title('rand(10)_ocx')
plt.show() #用直方图显示得到的结果
运行后得到结果如下:
从直方图可以看到该结果服从均匀分布。