视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
详解python的random模块及加权随机算法和实现方法
2020-11-27 14:16:07 责编:小采
文档
 random是用于生成随机数的,我们可以利用它随机生成数字或者选择字符串。
?random.seed(x)改变随机数生成器的种子seed。
一般不必特别去设定seed,Python会自动选择seed。
?random.random() 用于生成一个随机浮点数n,0 <= n < 1
?random.uniform(a,b) 用于生成一个指定范围内的随机浮点数,生成的随机整数a<=n<=b;
?random.randint(a,b) 用于生成一个指定范围内的整数,a为下限,b为上限,生成的随机整数a<=n<=b;若a=b,则n=a;若a>b,报错
?random.randrange([start], stop [,step]) 从指定范围[start,stop)内,按指定基数递增的集合中获取一个随机数,基数缺省值为1
?random.choice(sequence) 从序列中获取一个随机元素,参数sequence表示一个有序类型,并不是一种特定类型,泛指list,tuple,字符串等
?random.shuffle(x[,random]) 用于将一个列表中的元素打乱 (洗牌),会改变原始列表
?random.sample(sequence,k) 从指定序列中随机获取k个元素作为一个片段返回,不会改变原有序列
那么现在基础知识有了,我们来实现一个加权随机算法:
加权随机算法一般应用在以下场景:有一个集合S,里面比如有A,B,C,D这四项。这时我们想随机从中抽取一项,但是抽取的概率不同,比如我们希望抽到A的概率是50%,抽到B和C的概率是20%,D的概率是10%。一般来说,我们可以给各项附一个权重,抽取的概率正比于这个权重。那么上述集合就成了:
{A:5,B:2,C:2,D:1}
方法一:
最简单的方法可以这样:
把序列按权重值扩展成:lists=[A,A,A,A,A,B,B,C,C,D],然后random.choice(lists)随机选一个就行。虽然这样选取的时间复杂度是O(1),但是数据量一大,空间消耗就太大了。

# coding:utf-8
import random
def weight_choice(list, weight):
 """
 :param list: 待选取序列
 :param weight: list对应的权重序列
 :return:选取的值
 """
 new_list = []
 for i, val in enumerate(list):
 new_list.extend(val * weight[i])
 return random.choice(new_list)
if name == "main":
 print(weight_choice(['A', 'B', 'C', 'D'], [5, 2, 2, 1]))


方法二:
比较常用的方法是这样:
计算权重总和sum,然后在1到sum之间随机选择一个数R,之后遍历整个集合,统计遍历的项的权重之和,如果大于等于R,就停止遍历,选择遇到的项。
还是以上面的集合为例,sum等于10,如果随机到1-5,则会在遍历第一个数字的时候就退出遍历。符合所选取的概率。
选取的时候要遍历集合,它的时间复杂度是O(n)。

# coding:utf-8
import random
list = ['A', 'B', 'C', 'D']
def weight_choice(weight):
 """
 :param weight: list对应的权重序列
 :return:选取的值在原列表里的索引
 """
 t = random.randint(0, sum(weight) - 1)
 for i, val in enumerate(weight):
 t -= val
 if t < 0:
 return i
if name == "main":
 print(list[weight_choice([5, 2, 2, 1])])


方法三:
可以先对原始序列按照权重排序。这样遍历的时候,概率高的项可以很快遇到,减少遍历的项。(因为rnd递减的速度最快(先减去最大的数))
比较{A:5,B:2,C:2,D:1}和{B:2,C:2,A:5,D:1}
前者遍历步数的期望是5/10*1+2/10*2+2/10*3+1/10*4=19/10而后者是2/10*1+2/10*2+5/10*3+1/10*4=25/10。
这样提高了平均选取速度,但是原序列排序也需要时间。
先搞一个权重值的前缀和序列,然后在生成一个随机数t后,可以用二分法来从这个前缀和序列里找,那么选取的时间复杂度就是O(logn)了。

# coding:utf-8
import random
import bisect
list = ['A', 'B', 'C', 'D']
def weight_choice(weight):
 """
 :param weight: list对应的权重序列
 :return:选取的值在原列表里的索引
 """
 weight_sum = []
 sum = 0
 for a in weight:
 sum += a
 weight_sum.append(sum)
 t = random.randint(0, sum - 1)
 return bisect.bisect_right(weight_sum, t)
if name == "main":
 print(list[weight_choice([5, 2, 2, 1])])

-->

下载本文
显示全文
专题