python根据指定概率生成随机数

一、前言

        今天在上信息论与编码这门课的时候,看到书上有个题目需要求:单符号离散无记忆二信源,需要用指定概率来生成随机数,书上是用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}')

        我们知道random.random() 可以随机生成一个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 

  • 24
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值