代码改变世界

单次遍历,带权随机选取问题

  youxin  阅读(1311)  评论(0编辑  收藏  举报

在以前的链表单次遍历,随机选取问题中,我们采用水塘抽象方法解决了问题。问题结点带权呢?

问题描述:有一组数量未知的数据,每个元素有非负权重。要求只遍历一次,随机选取其中的一个元素,任何一个元素被选到的概率与其权重成正比。

设元素总数为n,当然在遍历结束前n是未知的。设第i(1 <= i <= n)个元素的权重为wi(> 0),则权重总和为w=\sum_{i=1}^{n}{w_i},也是在遍历结束时才知道的。根据题目要求,第i个元素被选取的概率应该等于p_i=\frac{w_i}{w}

 

 

虽然加了个权重,但解法依旧非常简单,在单次遍历,等概率随机选取问题中的RandomSelect函数上稍作修改就得到本问题的解法,依旧是O(n)时间,O(1)辅助空间:

复制代码
from random import Random

def WeightedRandomSelect(rand=None):
  selection = None
  totalweight = 0.0
  if rand is None:
    rand = Random()
  while True:
    # Outputs the current selection and gets next item
    (item, weight) = yield selection
    totalweight += weight
    if rand.random() * totalweight < weight:
      selection = item
复制代码

其中Python的random.random()返回[0, 1)之间的随机小数。

把rand.random()<weight/totalWeigth,则会有好理解一点。(同等概率选取类似,random(1,i)等于1就替换.i分之一,只不过不同概率用权重占比来表示了。)

 

 

 

算法很简单:对于任意的i(1 <= i <= n),按照如下方法给第i个元素分配一个键值key(其中ri是一个0到1之间等概率分布的随机数):

 

之后,如果要随机选取一个元素,就去key最大的那个;如果要选取m个元素,就取key最大的m个。

真不知道是怎么想出来的这样的方法,不过还是先来关注一下证明的过程。

算法的核心,计算每个元素的随机权重,python版:

key = rand.random() ** (1.0 / weight)
 
选择key最大的m个,就是结果

更多:

http://www.gocalf.com/blog/weighted-random-selection.html

http://www.gocalf.com/blog/weighted-random-selection-2.html

http://www.cnblogs.com/SuperBrothers/archive/2012/11/13/2768788.html

编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
历史上的今天:
2012-10-05 jquery 关于ajax的操作
点击右上角即可分享
微信分享提示