如何从一个已经加权重的集合中随机返回一条数据
一.背景
每周的周一早上都是摸鱼的,今天下午的时候我又在摸鱼中(学习),当我摸的正得劲的时候,突然背后站了一个人,然后来了一句,小明有个需求需要你来做一下,然后我肯定是细心听讲,大概的事情是这样的,我司有一个python做的爬虫系统专门用来爬取数据然后回推给内部的业务集群,调用方式是通过http接口的方式调用的,有两种方式,一种是直接访问域名,还有一种是通过ip访问,通过ip访问,这里主要说的是通过ip访问,原先系统会从配置中读取一个集合,集合中存放的是ip,然后通过随机集合长度的方式来进行负载均衡,说实话这种方式比较low,现在的场景是这样的,原先我们公司的服务都是部署在自己云服务器上的,最近在和友商合作,准备部署一些服务到友商的服务中,但是如果全部部署过去的话又怕他妈的机器扛不住影响到我们,所以就准备做一个类似权重的分流,说的简单点,就是切一部分流量到友商的机器上,然而更狗血的是ip的格式需要配置成这种格式 ip-云服务商-权重值,问了下为啥不用json,得到的回答是不想改另外一个系统。那就只能硬着头皮做了。
二.过程
首先就是把这个问题转换一下,就是从一个已经绑定权重的集合中随机根据权重获取一条数据,然后当然就是google了,google了好一会都没有查到有用的结果,然后就转换查询条件,然后查出了一段代码
import random
def weighted_choice(weights):
totals = []
running_total = 0
for w in weights:
running_total += w
totals.append(running_total)
rnd = random.random() * running_total
for i, total in enumerate(totals):
if rnd < total:
return i
if __name__ == '__main__':
for i in range(500):
print(weighted_choice([2, 3, 5, 5, 5]))
上述方法主要做的是什么呢,首先传入权重的集合,遍历集合中的元素,对权重进行相加后加入到一个列表中,然后调用随机数函数在这个权重的总和之间随机生成一个处于最大权重范围内的权重值,然后遍历原先相加的权重获取到下标索引,其实我理解的是,本身列表中的数据是无序的,先按列表中的数据进行排序,然后取每个数据的权重值相加存放到另外一个列表中,此时就多了一个权重列表的数据和你原生数据的一个映射,本身权重列表中的数据的最大值使我们的取值范围,通过随机函数生成后,遍历权重列表进行比较可以得到一个下标,这个下标和我们原生的数据的下标是映射起来的,这样就能获取到我们想要的数据了。其实就相当于我们从一个列表中找出一个处于指定范围的数据的下标。上述代码其实比较的时候可以改成二分查找,可以提升效率。
import bisect
import random
from itertools import accumulate
class WeightedRandomGenerator(object):
def __init__(self, weights):
self.totals = list(accumulate(weights))
def next(self):
rnd = random.random() * self.totals[-1]
return bisect.bisect_right(self.totals, rnd)
三.应用到实际场景
这里就简单讲一下怎么将这个工具类使用到我的实际场景中,首先我会从配置中读取到数据然后对数据进行切割,然后以每个ip后面的权重按从小到大排序,然后创建一个WeghtedRandomGenerator对象,然后传入后获取到下标值,然后获取原生列表中的ip发起请求。大概思路是这样的,具体业务场景具体分析。
四.总结
简单记录,不会就谷歌,每天进步一点,一年就不知道进步多少了。睡觉。
参考
https://eli.thegreenplace.net/2010/01/22/weighted-random-generation-in-python/
|