现在有一个随机数生成器, 1/3概率生成1, 2/3生成0, 如何基于这个生成器, 得出一个1/2概率是0, 1/2概率是1的生成器?
可以将结果看过一个二元组,那么有4种情况分别是(0,0),(0,1), (1,0),(1,1)
其中 p((0,0)) = 4/9
p((1,1)) = 1/9
p((0,1)) = 2/9
p((1,0)) = 2/9
那么,我们只需按 1/3概率生成1, 2/3生成0 这个规则,生成二元组,将结果中的(0,0)和(1,1)抛弃,剩下的(0,1)即为1,(1,0)即为0,
import random
from fractions import Fraction
# 返回类型是tuple,因为list不可哈希
def random_pick(some_list, probabilities): # 按照规则生成二元组
result = () # 二元组结果
for i in range(0, 2): # 构造二元组
cumulative_probability = 0.0 # 初始化概率值
x = random.uniform(0, 1) # 随机生成一个0到1之间的值
for item, item_probability in zip(some_list, probabilities):
cumulative_probability += item_probability # 累加规则中的概率值
if x < cumulative_probability: # 如果x在0到1/3内,添加1;如果在1/3到1,添加0
result += (item,)
break
return result
def random_filter(num):
a = [1, 0]
b = [Fraction(1, 3), Fraction(2, 3)] # 概率分布
all_collection = [(0, 1), (1, 0), (0, 0), (1, 1)] # 所有结果
result_collection = [(0, 1), (1, 0)] # 我们需要的结果
all, re, count = dict(zip(all_collection, [0] * 4)), dict(zip(result_collection, [0] * 2)), 0 # 前两个变量为所有结果和需要结果的dict,最后一个count变量为需要结果的计数
for x in range(num):
result = random_pick(a, b)
all[result] += 1
if result in result_collection:
count += 1
re[result] += 1
for v, value in re.items():
re[v] = float(value)/count
for v, value in all.items():
all[v] = float(value)/num
return all, re # 返回所有结果和需要结果,所有结果all 用来验证四种情况是不是2/9 2/9 4/9 1/9分布,需要结果re 用来验证是不是得到了最后的1/2的0和1/2的1的生成器
for item in random_filter(100000): # 做10万次生成试验
print(item)
我们可以看到,(0,1) 和 (1,0)皆约等于2/9,(0,0)约等于4/9,(1,1)约等于1/9,表明我们得到的所有结果是没啥大问题的,然后再看 (0,1) 和 (1,0) 的结果,可以发现是非常接近的(其实只要用第一行的值相除一下就行),因此符合题目要求。
{(0, 1): 0.22255, (1, 0): 0.22182, (0, 0): 0.44584, (1, 1): 0.10979}
{(0, 1): 0.50082138758242, (1, 0): 0.49917861241757994}
以上是我的解法,如果有更的解法请在下方评论,大家一起学习学习。