前几天在网上看到这样一道面试题,据说是阿里的题目
自己想到了一个方法,希望大家指正。代码如下:
import random
# 题中foo()为已知方法,所以这里我们用内置random模块实现
def foo():
return random.randint(0,1)
# 这里开始实现boo()方法
def boo():
# 初始化空字符串,用来存储二进制数
s = ''
for _ in range(4):
# 拼接foo()方法生成的数字
s += str(foo())
# 二进制转十进制
i = int(s,2)
# 如果数字不大于9,则直接返回结果。否则重新运行,即一次递归
if i <= 9:
return i
else:
return boo()
接着运行我们的方法10万次,看看概率是不是约为10%
n_loop = 100000
tmp_dict = {}
for _ in range(n_loop):
i = boo()
tmp_dict[i] = tmp_dict.get(i,0) + 1
print(f'在{n_loop}次测试中:')
for i in range(10):
n = tmp_dict[i]
prob = n/n_loop
print(f'数字 {i} 出现了 {n:>5} 次,概率约为 {prob*100:>5.2f}%')
输出为:
在100000次测试中:
数字 0 出现了 9858 次,概率约为 9.86%
数字 1 出现了 10118 次,概率约为 10.12%
数字 2 出现了 10122 次,概率约为 10.12%
数字 3 出现了 10114 次,概率约为 10.11%
数字 4 出现了 9873 次,概率约为 9.87%
数字 5 出现了 9923 次,概率约为 9.92%
数字 6 出现了 9856 次,概率约为 9.86%
数字 7 出现了 10222 次,概率约为 10.22%
数字 8 出现了 9936 次,概率约为 9.94%
数字 9 出现了 9978 次,概率约为 9.98%
下面开始解析:
方法的核心思想为,利用每次生成的0或1,拼接为一个二进制数字,并且将其转换为10进制。由于要求[0-9]的数字,所以我们选择4位的二进制。即运行foo()方法4次,生成一个4位的二进制数,其取值范围为[0-15] (即二进制范围[0000 - 1111])。假如我们不做任何处理,那么这个方法会以各1/16的概率输出[0-15]中任意一数。接着我们做一个限制,当数字大于9时,重新运行此方法,直到数字在[0-9]之间。由于每个数字是等概率出现的,所以该方法的实际输出概率为10%。
更直观的来说,本题实际要求是构造一个数字[0-9]的10面骰子,然而我们不知道如何直接构造10面骰子,所以我们构造了一个数字[0-15]的16面骰子,并制定了如下规则:“如果摇到大于9的数字则重新摇,直到结果在[0-9]之间”。
有的人可能会感到疑惑:这样概率难道不是1/16吗?事实上,如果我们不做任何限制,那么的确如此。但同时我们也应该知道一个事实:每个数字是等概率的。所以我们并不关心每个数字在方法内部生成时的概率,只要每个数字是等概率的,并且该方法只返回[0-9]的数字,那么该方法输出每个数字的实际概率就是1/10 = 10%
。
思考: 我暂时没想出来如何直接构造一个10面的骰子,如果有人能想到,请一定要告诉我。