我说我为什么抽不到SSR,原来是这段代码在作祟...

news/2024/4/20 13:24:42/文章来源:https://blog.csdn.net/BTnode/article/details/129160384

本文是龚国玮所写,熊哥有所新增修改删减,原文见文末。

我说我为什么抽不到SSR,原来是加权随机算法在作祟

阅读本文需要做好心理准备,建议带着深究到底的决心和毅力进行学习!

灵魂拷问

为什么有 50% 的几率获得金币?

为什么有 40% 的几率获得钻石?

为什么只有 9% 的几率获得装备?

为什么才有 1% 的几率获得极品装备?

是人性的扭曲,还是道德的沦丧,请和我一起走进今日说法

介绍

元素被选中的机会并不相等,而是由相对“权重”(或概率)被选中的,是偏心的,这就是加权随机。

举个栗子,假如现在有一个权重数组 w = {1, 2, 4, 8},它们代表如下规则。

  • $ \frac{1}{(1+2+4+8)} = \frac{1}{15} \approx 6.6$ % 几率选中第1个

  • $ \frac{2}{(1+2+4+8)} = \frac{2}{15} \approx 13.3$ % 几率选中第2个

  • $ \frac{3}{(1+2+4+8)} = \frac{4}{15} \approx 26.6$ % 几率选中第3个

  • $ \frac{1}{(1+2+4+8)} = \frac{8}{15} \approx 53.3$ % 几率选中第4个

一个小小的概率问题,居然引出了大大的思考。

方案一、笨笨的办法

所以要设计一个加权算法的程序,你会怎么写呢?

第一个方法把权重所在的位置展开,然后从该列表中随机选择。

假设现在有权重列表 {1, 2, 4, 8}

那我们得到的候选列表将是

{0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3}

然后通过 rand.Intn() ,获取一个随机数,就完成了,代码如下。

func weightedRandomS1(weights []int) int {if len(weights) == 0 {return 0}var indexList []intfor i, weight := range weights {cnt := 0for weight > cnt {indexList = append(indexList, i)cnt++}}rand.Seed(time.Now().UnixNano())return indexList[rand.Intn(len(indexList))]
}

当权重特别大的时候,这种方案费时费力,又占空间。先别急往下看,你能想到更好的办法吗?

方案二、略显聪明

由于总权重为 15(1+2+4+8),我们可以生成一个 [0,15) 的随机整数,然后根据这个数字返回索引。代码如下。

func weightedRandomS2() int {rand.Seed(time.Now().UnixNano())r := rand.Intn(15)if r <= 1 {return 0} else if 1 < r && r <= 3 {return 1} else if 3 < r && r <= 7 {return 2} else {return 3}
}

妙不妙!!但你以为这就是效率最高的办法吗?

写那么多if else不痛苦吗我的宝贝。

方案三、神之一手

何必将随机数和所有的范围进行比较呢?直接遍历随机数减去权重,如果结果小于等于零,不就是我们要的结果下标吗?

func weightedRandomS3(weights []int) int {rand.Seed(time.Now().UnixNano())r := rand.Intn(len(weights))for i, v := range weights {r = r - vif r <= 0 {return i}}return len(weights) - 1
}

这种方法叫放弃临时名单。想不明白就评论问!

方案四、小小优化

对于方案三,怎么有效的减少遍历次数呢?

r小于等于 0 的速度越快,算法越高效。那我们就让 r 到达 0 更快。先排序这样就能先减去权重大的,减少遍历次数。

func weightedRandomS4(weights []int) int {sort.Sort(sort.Reverse(sort.IntSlice(weights)))....

有人就不服了,排序不是更浪费时间?

是的!虽然看起来减少遍历次数!但排序本身就要遍历就是更浪费时间。。。

但是一次排序,反复使用,还是能提高效率的!

方案五、不可思议!

有没有办法不用排序,而让原数组有序呢?

有人就说了,你这不是扯么?

如果每次遍历都加上上一个权重,那整个数字就是递增的!

再用二分就能加快速度了,时间复杂度从 $ O(n) $ 直接变为 $ O(log(n)) $。

func weightedRandomS5(weights []int) int {rand.Seed(time.Now().UnixNano())sum := 0var sumWeight []intfor _, v := range weights {sum += vsumWeight = append(sumWeight, sum)}r := rand.Intn(sum)idx := sort.SearchInts(sumWeight, r)return weights[idx]
}

读到这里,对源码没有信心学习的朋友,可以直接撤了。 真正的高阶优化要来了。

方案六、不死不休

到目前的位置,我们的解决方案已经足够好了,但是仍然有改进的余地。

方案五中,我们使用了 Go 标准库的二分查找算法 sort.SearchInts() ,是封装了通用的 sort.Search() 函数,如下。

sort.SearchInts

sort.Search() 的函数参数需要一个闭包函数,并且这个闭包函数是在 for 循环中使用的,如下。

sort.Search

闭包函数反复调用,在编译期会产生额外的开销。因为会产生更多的跳转,跳转会引起压栈(函数参数都是会压栈的)。

我们手动提出取函数,就可以减少编译器的内联(文末会解释)。

func weightedRandomS5(weights []int) int {
...
idx := sort.SearchInts(sumWeight, r)return weights[idx]
}
func searchInts(a []int, x int) int {i, j := 0, len(a)for i < j {h := int(uint(i+j) >> 1)if a[h] < x {i = h + 1} else {j = h}}return i
}

通过基准测试可以看到吞吐量提升了 33% 以上。对于大型数据集,优势越明显。

优化前

优化后

方案七,“偷鸡”取巧--轮盘赌

目前为止我们所有的方案都有一个共同点 —— 生成一个介于 0 和“权重之和”之间的随机数,并找出它属于哪个“切片”。

还有一种不同的方法。

func weightedRandomS7(weights []float64) int {var sum float64var winner intrand.Seed(time.Now().UnixNano())for i, v := range weights {sum += vf := rand.Float64()if f*sum < v {winner = i}}return winner
}
  • 从命中的角度去考虑。
  • 权重越大,命中率自然越大了。
  • 既然是随机,多次随机和单次随机而言都是随机的。

这个算法的一个有趣的特性是你不需要提前知道权重的数量就可以使用它。所以说,它或许可以用于某种流。

尽管这种方案很酷,但它比其他方案慢得多。相对于方案一,它也快了 25%

小结

  • 下标直接展开到列表里,随机长度取值。
  • if else 取值。
  • 遍历随机数减去权重,结果小于等于零时。
  • 先排序,再用方法三。
  • 免排序,直接加和,再二分。
  • 优化源码中的二分法。
  • 轮盘赌算法,每次都去赌。

内联:编译器的一个名词。我们的代码最终都是经过编译系统转换成可执行二进制文件。汇编阶段读取的是词法、语法单元输出的结果。而内联是编译器对词法、语法分析器对源代码做出的分析,然后产生二进制代码这个过程叫内联。

源代码

https://github.com/guowei-gong/weighted-random

原文:加权随机设计与实现

一起进步

你好,我是小熊,是一个爱技术但是更爱钱的程序员。上进且佛系自律的人。喜欢发小秘密/臭屁又爱炫耀。

奋斗的大学,激情的现在。赚了钱买了房,写了书出了名。当过面试官,带过徒弟搬过转。

大厂外来务工人员。是我,我是小熊,是不一样的烟火欢迎围观。

我的博客 机智的程序员小熊 欢迎收藏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_72506.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

同一局域网的不同主机使用共享文件夹通信(仅限于不同Windows主机之间的通信)

1、新建共享文件夹 我们新建一个文件夹 Server-Share&#xff0c;右键点击“ 属性 ” 选择“everyone”&#xff0c;即允许当前局域网下的所有用户访问这个共享文件夹 此时该文件夹面向当前局域网是公开的。 2、服务器访问共享文件夹 (1) 查看当前电脑的IP IP地址可以唯一标…

企业为什么需要数据可视化报表

数据可视化报表是在商业环境、市场环境已经改变之后&#xff0c;发展出来为当前企业提供替代解决办法的重要方案。而且信息化、数字化时代&#xff0c;很多企业已经进行了初步的信息化建设&#xff0c;沉淀了大量业务数据&#xff0c;这些数据作为企业的资产&#xff0c;是需要…

园区数字化转型必不可少的助推器:快鲸智慧园区系统

数字化浪潮下&#xff0c;园区数字化转型已成必然趋势。可大多数人在讨论智慧园区的时候&#xff0c;更多聚焦在技术上&#xff0c;却忽略了一个关键点&#xff0c;就是打造智慧园区最终的结果导向是提高业务信息化水平&#xff0c;进而达到集约高效、提质增效、节能降耗的可持…

干货复试详细教程——从联系导师→自我介绍的复试教程

文章目录联系导师联系之前的准备联系导师注意自我介绍教育技术领域通用的复试准备其他补充联系导师 确定出分和自己能进复试以后联系。 分两类 科研技能型 低调&#xff0c;如实介绍&#xff0c;不吹不水。就算你很牛啥都会手握核心期刊论文也不太狂 学霸高分型 不要自卑&…

STM32-CAN配置与库函数解析,实现环回模式通信

STM32-CAN配置与库函数解析 CAN总线介绍&#xff1a;https://blog.csdn.net/weixin_46251230/article/details/129147612 STM32-CAN控制器介绍&#xff1a;https://blog.csdn.net/weixin_46251230/article/details/129150872 STM32CubeMx配置 因为bxCAN是挂载在APB1总线上的…

【学习总结】相机与IMU标定一:Kalibr论文

论文&#xff1a;2013IROS论文&#xff0c;Unified Temporal and Spatial Calibration for Multi-Sensor Systems&#xff0c;是Kalibr工具的参考论文之一。介绍了如何进行IMU与相机标定。 参考的一篇资料&#xff1a;知乎&#xff1a;超全汇总&#xff01;多传感器离线/在线时…

新建微服务模块Maven子工程

gitegg-cloud是微服务框架&#xff0c;整体功能是非业务相关的基础功能&#xff0c;在实际业务开发过程中需要新建微服务的业务模块&#xff0c;根据业务的整体规划&#xff0c;设计新建Maven子工程。   下面以常用的电商项目举例新建Maven子工程&#xff0c;电商项目一般包含…

VIIRS-NPP夜间灯光遥感数据下载和预处理

VIIRS-NPP夜间灯光遥感数据下载和预处理 月和年合成产品下载网站 日数据下载网站 一、下载shp掩膜文件 下载好月合成产品后&#xff0c;在这个网站上下载矢量地图&#xff0c; 点击复制按钮&#xff0c;来到这个网站&#xff0c;ctrl v粘贴 点击右上角Export&#xff0c;…

搞懂事件——C# 的event的机制深度理解

📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:无尽的折腾后,终于又回到了起点,工控,我来了 !1. 前言 为什么忽然对Event感兴趣了? 因为进入Web时代以后,很少使用它了,忽然想起这个知识点,…

九龙证券|市场化转融资业务试点上线首日平稳运行

2月21日&#xff0c;中国证券金融股份有限公司&#xff08;下称“中证金融”&#xff09;商场化转融资事务试点迎来首个买卖日。全天该事务试点平稳运转&#xff0c;商场化转融资规模合计10亿元。 业内人士以为&#xff0c;商场化转融资事务形式下&#xff0c;证券公司参加转融…

使用51单片机和DS1302时钟芯片做一个简易的电子时钟

简易的电子时钟实验一、前言二、DS1302模块介绍三、驱动DS1302的代码3.1 初始化DS1302时钟芯片3.2 读取DS1302时钟芯片的时间3.3 设置DS1302时钟芯片的时间3.4 读取DS1302时钟芯片的RAM四、读取DS1302时钟芯片的RAM4.1 发送读取RAM的命令4.2 读取RAM的内容4.3 读取部分单独代码…

[飞桨paddle]1. conda安装paddle环境.模型转换,pytorch->paddlepaddle

“一生费城七六人”1. conda装paddle环境1.1 验证是否装好2. x2paddle2.1 介绍2.2 安装3 模型转换3.1 pt -> onnx3.2 onnx > .pdparams3.2.1 会出现的错误情况3-1. 第一种情况3-2. 第二种情况4. 查看结果问题阐述&#xff1a;将yoloV5项目移至paddle框架下执行时&#xf…

中央一号文件首提“即时零售”,县域掀起消费业态新风潮

经过几年的探索&#xff0c;即时零售已经逐步走向成熟&#xff0c;并开始向三四线城市以及乡镇城市渗透。 过去一年&#xff0c;京东、美团、阿里争先布局即时零售市场&#xff0c;完善即时配送网络、培养用户消费习惯&#xff0c;即时零售订单迎来了骤增。2022年下半年&#…

C/C++每日一练(20230222)

目录 1. 部分复制字符串(★) 2. 按字典顺序排列问题(★★) 3. 地下城游戏(★★★) 附录 动态规划 1. 部分复制字符串 将字符串2小写字母复制到字符串1&#xff1a;编写程序,输入字符串s2,将其中所有小写字母复制到字符串数组strl中。例如&#xff1a;aal1bb22cc33de4AA55…

Java实现多线程有几种方式(满分回答)

目录JDK8 创建的线程的两种方式orcle文档解释方式一&#xff1a;继承Thread类方式二&#xff1a;实现Runnable接口同时用两种的情况其他间接创建方式Callable接口线程池JDK8 创建的线程的两种方式 orcle文档解释 orcle文档&#xff1a;https://docs.oracle.com/javase/8/docs…

九龙证券|银行资本管理办法迎“大修” 信用风险权重法调整优化

1年期AAA中债商业银行同业存单到期收益率 日前迎来“大修”的商业银行本钱办理方法&#xff0c;在债券商场激起“涟漪”——债券商场一改此前平静态势&#xff0c;连续两日跌落。 2月21日&#xff0c;10年期国债收益率较上星期五上行2.9个基点&#xff0c;至2.919%&#xff1b…

Redis学习【10】之Redis主从集群(1)

文章目录一 Redis主从集群搭建1 伪集群搭建与配置1.1.1 分级管理1.1.2 容灾冷处理1.2 主从复制原理1.2.1 主从复制过程1.2.2 数据同步演变过程1.3 哨兵机制实现1.3.1 哨兵机制简介1.3.2 Redis 高可用集群搭建1.3.3 Redis 高可用集群的启动1.3.4 Sentinel 优化配置1.4 哨兵机制原…

Spring中自定义Session管理,Spring Session源码解析

系列文章&#xff1a;Spring Boot学习大纲&#xff0c;可以留言自己想了解的技术点 目录 系列文章&#xff1a;Spring Boot学习大纲&#xff0c;可以留言自己想了解的技术点 1、session是什么&#xff1f; 1>session在哪里&#xff1f; 2>服务器怎么知道每次说话的是…

H5盲盒抽奖系统源码

盲盒抽奖系统4.0&#xff0c;带推广二维码防洪炮灰功能和教程。 支持微信无限回调登录 标价就是源码价格&#xff0c;vuetp5框架编写&#xff0c;H5网页&#xff0c;前后端分离 此源码为正规开发&#xff0c;正版产品已申请软著。 开源无加密无授权&#xff0c;可以二开使用…

霍尔元件的应用

霍尔传感器有3个pin&#xff0c;分别是 正极 负极和输出pin。 输出pin接电阻和发光二极管。电阻起限流作用。 电源接5.5v直流电。当拿一个磁铁的N极靠近霍尔元件时&#xff0c;二极管越来越亮。当拿S极靠近霍尔元件时&#xff0c;二极管越来越暗。 N极磁场强度定义为正的磁场强…