热点新闻
一文讲完random:python中的随机模块
2023-07-10 17:17  浏览:3498  搜索引擎搜索“手机低淘网”
温馨提示:信息一旦丢失不一定找得到,请务必收藏信息以备急用!本站所有信息均是注册会员发布如遇到侵权请联系文章中的联系方式或客服删除!
联系我时,请说明是在手机低淘网看到的信息,谢谢。
展会发布 展会网站大全 报名观展合作 软文发布

我们在python工程和数据分析中经常用到随机的操作,比如随机生成某个值,对一串数据进行随机排序等等。random是python一个很强的第三方库,可以实现常用的随机算法。

安装:pip install random

一:生成随机的数字

0~1之间的随机小数(float):random.random()
a~b之间的随机小数(float):random.uniform(a, b)
[a, b)之间的随机整数(int):random.randint(a, b)

import random print(f''' random.random():0~1之间抽取随机小数: {random.random()} ''') print(f''' random.uniform():a~b之间抽取随机小数: {random.uniform(1,10)} ''') print(f''' random.randint():[a, b)之间抽取随机整数: {random.randint(3,8)} ''') # 运行结果: random.random():0~1之间抽取随机小数: 0.75106286590147 random.uniform():a~b之间抽取随机小数: 1.1154946513637323 random.randint():[a, b)之间抽取随机整数: 6


二:对非空序列(元组、列表、字符串)操作随机拿取

随机地取一个值(等权):random.choice(lis)

这个很简单,从对象中随机取出一个值,每个值取到的概率相同。

import random lis = (1,2,3,4,5,'cat') print(random.choice(lis)) # 运行结果: 4

随机地取k个值(可分配权重):random.choices(lis, weights=[], cum_weights=[], k=n)

这里重点讲一下几个参数:

  • 参数1:population=lis,即样本集。
  • 参数2:设置权重weights或cum_weights,传入一个权重列表,为每个样本元素分别设置权重(权重列表个数要与样本列表个数一致)。
    weights(相对权重)和cum_weights(累加权重)都是权重参数,设置方式不同而已,使用时选择其一
  • 参数3:k=n,即从样本中取几个元素。

重点来了:
要了解weights(相对权重)和cum_weghts(累加权重),我们看两个例子:
weights相对权重:把每个权重要素加一起,其总和为总权重。每个权重要素占总权重的比例,即为对应样本的概率。

import random lis = (1,2,3,4,5,'cat') print(random.choices(population=lis, weights=[1,2,1,1,3,4], k=3)) ''' weights=[1,2,1,1,3,4],权重总和为:1+2+1+1+3+4 =12 即: 第一个元素1抽到的概率为:1/12,约8.3% 第二个元素2抽到的概率为:2/12,约16.7% ... 第五个元素5抽到的概率为:3/12,25% 第六个元素‘cat’抽到的概率为:4/12,约33.3% ''' # 运行结果: [2, 'cat', 5]

cum_weights累加权重:网上的教程对累加权重解释的都很模糊,我在这里详细说一下。

  • 累加权重是“分蛋糕”的概念,样本按照所设权重的总份数(总蛋糕),从第一个开始依次分。分完即止,后面没分到权重的概率为0。
  • 列表的最后一项为权重的总份数(共有多少份蛋糕)。
  • 列表的每一项代表截至到此,累计拿了几份权重(拿走了多少份蛋糕)。

下面看几个例子。为了便于直观看到概率是多少,我们写个函数test_cumweights(),专门统计样本出现的概率。

def test_cumweights(lis, cw, m, n): ''' 【输入】lis:样本集,cw:累加权重,m:抽多少次,n:每次抽几个 【输出】每个样本出现的概率 ''' result = {} # 用于记录样本出现的次数,{样本:次数} for i in lis: result[i] = 0 # 初始化,出现次数均为0 num = 0 while num < m: # 抽 m 次 res = random.choices(population=lis, cum_weights=cw, k=n) # 抽n个的结果res for i in res: result[i] += 1 # 将本次的结果保存到字典 num += 1 times = num * n # 一共抽了多少次 for k,v in result.items(): # 打印结果 print(f'共抽了{times}次,其中【{k}】出现了{v}次,概率约为:{round((v/times),3)}')

例一:cum_weights=[1,2,2,3,7],总权重为7份。

第一个元素:累计拿了一份,即1/7为14.3%;
第二个元素:累计拿了两份,即第二个元素拿了一份权重,1/7为14.3%;
第三个元素:累计拿了两份,说明第三个元素并未拿权重,即0%;
第四个元素:累计拿了三份,即第四个元素也拿了一份权重,1/7为14.3%;
第五个元素:到此累计拿了七份,即第五个元素拿了四份权重,4/7为57.1%。

lis = ['张飞','马超','荀彧','颜良文丑','毒苹果'] cw = [1,2,2,3,7] m = 10000 n = 3 test_cumweights(lis=lis,cw=cw, m=m, n=n) # 运行结果: 共抽了30000次,其中【张飞】出现了4284次,概率约为:0.143 共抽了30000次,其中【马超】出现了4366次,概率约为:0.146 共抽了30000次,其中【荀彧】出现了0次,概率约为:0.0 共抽了30000次,其中【颜良文丑】出现了4322次,概率约为:0.144 共抽了30000次,其中【毒苹果】出现了17028次,概率约为:0.568

是不是很好理解?别急,下面还有难的:

例二:cum_weights=[2,3,1,2,7],总权重为7份。

第一个元素:累计拿了两份,即2/7为28.6%;
第二个元素:累计拿了三份,即第二个元素拿了一份权重,即1/7为14.3%;
第三个元素:累计拿了一份,权重分配到此,说明又收回了两份,只分了一份出去。谁拿了呢?显然是第一个,因为第一个是2,是最先分配的(如果是0呢?)。所以第一个元素变成了1/7为14.3%,第二个元素没分到为0%,第三个元素也没拿,为0%
第四个元素:累计拿了两份,即第四个元素拿了一份权重,1/7为14.3%;
第五个元素:到此累计拿了七份,即第五个元素拿了五份权重,5/7为71.4%。

lis = ['张飞','马超','荀彧','颜良文丑','毒苹果'] cw = [2,3,1,2,7] m = 10000 n = 3 test_cumweights(lis=lis,cw=cw, m=m, n=n) # 运行结果: 共抽了30000次,其中【张飞】出现了4281次,概率约为:0.143 共抽了30000次,其中【马超】出现了0次,概率约为:0.0 共抽了30000次,其中【荀彧】出现了0次,概率约为:0.0 共抽了30000次,其中【颜良文丑】出现了4425次,概率约为:0.147 共抽了30000次,其中【毒苹果】出现了21294次,概率约为:0.71

例二中,如果cw第一个是0呢?显然,第一个就没要份额,后面重新分配时就会把权重分给第二个,即cw=[0, 3, 1, 2, 7]:
元素一:0%
元素二:14.3%
元素三:0%
元素四:14.3%
元素五:71.4%

lis = ['张飞','马超','荀彧','颜良文丑','毒苹果'] cw = [0,3,1,2,7] m = 10000 n = 3 test_cumweights(lis=lis,cw=cw, m=m, n=n) # 运行结果: 共抽了30000次,其中【张飞】出现了0次,概率约为:0.0 共抽了30000次,其中【马超】出现了4281次,概率约为:0.143 共抽了30000次,其中【荀彧】出现了0次,概率约为:0.0 共抽了30000次,其中【颜良文丑】出现了4292次,概率约为:0.143 共抽了30000次,其中【毒苹果】出现了21427次,概率约为:0.714

累加权重cum_weights支持整数和小数分配,逻辑是一样的。

累加权重的概念到此讲完,着实很抽象。

要知道,random.choices()的运算逻辑就是把相对权重weights先转为累加权重cum_weights,再执行随机算法。因此使用cum_weights参数的运算效率要高一些

如果不是追求极致的算法,使用weights就能满足日常需要。但我还是建议你掌握cum_weights的逻辑,以便日后装杯用。


三、随机打乱一个可迭代对象

random.shuffle(lis)

该用法是对lis的原数据执行打乱操作,本身无返回值,lis将被随机打散,原数据再也回不来了。

lis = ['张飞','马超','荀彧','颜良文丑','毒苹果'] random.shuffle(lis) print(random.shuffle(lis)) print(lis) # 运行结果: None ['颜良文丑', '荀彧', '毒苹果', '张飞', '马超']

因此shuffle()使用起来会有诸多不便。我们可以构建一个自定义的shuffle函数,结合在numpy系列里讲的copy用法,实现对传入对象的打散,且不改变原对象的数据。

import random import copy def my_shuffle(lis): copy_lis = copy.deepcopy(lis) # 深拷贝一个副本对象 try: random.shuffle(copy_lis) # 对副本对象操作打散 except: return None return copy_lis lis = ['张飞','马超','荀彧','颜良文丑','毒苹果'] result = my_shuffle(lis=lis) print(f'执行打散后的数据:{result}') print(f'原数据:{lis}') # 运行结果: 执行打散后的数据:['马超', '颜良文丑', '毒苹果', '荀彧', '张飞'] 原数据:['张飞', '马超', '荀彧', '颜良文丑', '毒苹果']


四、固定某个随机结果

使用random.seed(n)可以将接下来的随机操作结果固定下来,如果后面的代码调用该seed,将复现本次的随机操作,使两次随机结果相同。
参数n可以为整型、浮点型、甚至为字符串。其本身没有任何意义,只作区分本次标记用。
因此要注意,每个seed值对应的随机结果是唯一的。

我们看个抽牌的小游戏,随机从一叠牌中抽取一张,如果抽到了JOKER,则使用seed模仿赌神,再抽一次JOKER出来。

res = ['10', 'J', 'Q', 'K', 'A', 'JOKER'] # 定义几张牌 num = 0 while num < 10: # 抽10次 set_seed = random.random() # 定义一个随机seed, 用于标记本次随机的记录 random.seed(set_seed) get_it = random.choice(res) # 抽牌 print(f'本次抽到了{get_it}。') if get_it == 'JOKER': random.seed(set_seed) # 如果为JOKER, 则使用本次标记的seed再抽一张JOKER出来 get_again = random.choice(res) # 执行第二次抽取 print(f'触发第二次,本次抽到了{get_again}') else: pass num += 1 print('- ' * 20) # 运行结果: 本次抽到了A。 - - - - - - - - - - - - - - - - - - - - 本次抽到了Q。 - - - - - - - - - - - - - - - - - - - - 本次抽到了K。 - - - - - - - - - - - - - - - - - - - - 本次抽到了Q。 - - - - - - - - - - - - - - - - - - - - 本次抽到了JOKER。 触发第二次,本次抽到了JOKER - - - - - - - - - - - - - - - - - - - - 本次抽到了Q。 - - - - - - - - - - - - - - - - - - - - 本次抽到了JOKER。 触发第二次,本次抽到了JOKER - - - - - - - - - - - - - - - - - - - - 本次抽到了Q。 - - - - - - - - - - - - - - - - - - - - 本次抽到了A。 - - - - - - - - - - - - - - - - - - - - 本次抽到了A。 - - - - - - - - - - - - - - - - - - - -


五、生成固定bit大小的随机整数:

random.getrandbits(s)

关于比特位bit和字节Byte的知识:

  • 比特位bit指二进制的机器码(0和1),1011 代表4 bit;
  • 1 Byte = 8 bit。

用最后一个例子结束random的讲解。生成一个随机bit位的随机数,执行十次看看结果如何:

num = 0 while num < 10: # 试验十次 size = random.randint(1,20) # 定义一个随机大小的bit位(1-19之间) result = random.getrandbits(size) # 生成size大小的随机整数 result_b = bin(result) # 转成二进制用做验证 print(f''' 第{num+1}次试验: 生成结果为:{result},其二进制为:{result_b} 该数字大小为{size}bit,{round(size/8,2)}byte ''') num += 1 print('- ' * 20) # 运行结果: 第1次试验: 生成结果为:3,其二进制为:0b11 该数字大小为4bit,0.5byte - - - - - - - - - - - - - - - - - - - - 第2次试验: 生成结果为:211,其二进制为:0b11010011 该数字大小为8bit,1.0byte - - - - - - - - - - - - - - - - - - - - 第3次试验: 生成结果为:435,其二进制为:0b110110011 该数字大小为9bit,1.12byte - - - - - - - - - - - - - - - - - - - - 第4次试验: 生成结果为:784,其二进制为:0b1100010000 该数字大小为14bit,1.75byte - - - - - - - - - - - - - - - - - - - - 第5次试验: 生成结果为:33159,其二进制为:0b1000000110000111 该数字大小为19bit,2.38byte - - - - - - - - - - - - - - - - - - - - 第6次试验: 生成结果为:3,其二进制为:0b11 该数字大小为4bit,0.5byte - - - - - - - - - - - - - - - - - - - - 第7次试验: 生成结果为:56,其二进制为:0b111000 该数字大小为7bit,0.88byte - - - - - - - - - - - - - - - - - - - - 第8次试验: 生成结果为:0,其二进制为:0b0 该数字大小为4bit,0.5byte - - - - - - - - - - - - - - - - - - - - 第9次试验: 生成结果为:1,其二进制为:0b1 该数字大小为2bit,0.25byte - - - - - - - - - - - - - - - - - - - - 第10次试验: 生成结果为:26,其二进制为:0b11010 该数字大小为5bit,0.62byte - - - - - - - - - - - - - - - - - - - -

值得注意的是,我们以2举例,其二进制既可以为10,也可以为0010或00010。

其对应的值相同,只是占的bit大小不同(2bit、4bit、5bit)。

发布人:1cf3****    IP:117.173.23.***     举报/删稿
展会推荐
让朕来说2句
评论
收藏
点赞
转发