一、前言
今天在上信息论与编码这门课的时候,看到书上有个题目需要求:单符号离散无记忆二信源,需要用指定概率来生成随机数,书上是用matlab来实现的,matlab中的randsrc函数可以传入元素 和 元素对应的概率 来生成随机数,我就在想这是如何实现的,用python怎么做,查了一圈发现并没有找到想要的信息(后来发现random.choice()可以实现这个功能),其他博客上都写的支支吾吾的,于是我开始自己研究,最后终于想出办法解决问题。
二、代码及思路
举个例子来说明一下:
比如我现在有个数组[1, 2, 3, 4]
我想要他们对应的随机概率为[0.1, 0.2, 0.3, 0.4]
代码
import random
def randomNum_for_probability(array: list, probability_array: list):
"""
按照指定概率生成随机数
:param array: 随机数列表
:param probability_array: 对应每个元素生成的概率
:return: 随机生成的数字
"""
rand_num = random.random() # 随机生成一个0到1之间的浮点数
Sn = 0 # 前n个数生成的概率和
for index, p in enumerate(probability_array):
Sn += p
if rand_num <= Sn:
return array[index]
N = 100000
my_list = [1, 2, 3, 4]
probability_array = [0.1, 0.2, 0.3, 0.4]
count1 = 0
count2 = 0
count3 = 0
count4 = 0
for i in range(N):
n = randomNum_for_probability(my_list, probability_array)
if n == 1:
count1 += 1
elif n == 2:
count2 += 1
elif n == 3:
count3 += 1
else:
count4 += 1
print(f'1生成的概率为: {count1 / N}')
print(f'2生成的概率为: {count2 / N}')
print(f'3生成的概率为: {count3 / N}')
print(f'4生成的概率为: {count4 / N}')
随机生成一个0到1之间的浮点数,我们定义前n个元素概率和为Sn,比如第一次循环时,Sn = 0+0.1 = 0.1, 如果随机生成的rand_num<=Sn就意味着,rand_num落在[0,0.1]这一块了,而落在这一块的概率是0.1/1,所以生成1的概率变成了0.1
如果进入第二次循环,就说明了rand_num>0.1此时,Sn = 0.1+0.2 = 0.3 ,如果rand_num<=Sn就说明了rand_num的范围是(0.1, 0.3]这一块区域的长度就说0.3-0.1=0.2,占总区域面积的0.2/1,所以生成2的概率就是0.2了
以此类推,生成3的概率是0.3,生成4的概率是0.4,如果满足rand_num<Sn,就返回arry[index],其中index为对应概率元素的下标,arry就是传入元素的数组。
为了测试函数的准确性,就做了如下实验:传入元素和对应生成的概率,循环十万次,并且统计每个元素出现的次数,随后求出其占总体的百分比,最终实验结果为:
1生成的概率为: 0.10051
2生成的概率为: 0.20111
3生成的概率为: 0.30092
4生成的概率为: 0.39746
发现结果与对应的 概率数组 里的概率基本吻合。
三、优化拓展
为了避免有刁民乱传参数使概率数组之和不为1,应该先对概率数组进行归一化操作,根据如上函数,可以编写出如下功能函数
def randomArray_for_probability(array: list, probability_array: list, N: int):
"""
传入指定元素列表,和每个元素生成概率的列表 ,以及想生成列表的长度,按照指定概率生成数组
:param array: 随机数列表
:param probability_array: 对应每个元素生成的概率
:param N: 生成随机数组的元素个数
:return: 按照指定概率生成的数组
"""
# 归一化probability_array
prob_array_sum = sum(probability_array)
prob_array = [x/prob_array_sum for x in probability_array]
ans = [] # 按照指定概率生成的数组
for _ in range(N):
Sn = 0
rand_num = random.random() # 随机生成一个0到1之间的浮点数
for index, p in enumerate(prob_array):
Sn += p
if rand_num <= Sn:
ans.append(array[index])
break
return ans
N = 100000
my_list = [1, 2, 3, 4]
probability_array = [0.1, 0.2, 0.3, 0.4]
ans_array = randomArray_for_probability(my_list, probability_array, N)
count1 = ans_array.count(1)
count2 = ans_array.count(2)
count3 = ans_array.count(3)
count4 = ans_array.count(4)
print(f'1生成的概率为: {count1/N}')
print(f'2生成的概率为: {count2/N}')
print(f'3生成的概率为: {count3/N}')
print(f'4生成的概率为: {count4/N}')
运行结果如下:
1生成的概率为: 0.1004
2生成的概率为: 0.19928
3生成的概率为: 0.29939
4生成的概率为: 0.40093