我说我为什么抽不到SSR,原来是这段代码在作祟...
本文是龚国玮所写,熊哥有所新增修改删减,原文见文末。
我说我为什么抽不到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.Search()
的函数参数需要一个闭包函数,并且这个闭包函数是在 for
循环中使用的,如下。
闭包函数反复调用,在编译期会产生额外的开销。因为会产生更多的跳转,跳转会引起压栈(函数参数都是会压栈的)。
我们手动提出取函数,就可以减少编译器的内联(文末会解释)。
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
原文:加权随机设计与实现
一起进步
你好,我是小熊,是一个爱技术但是更爱钱的程序员。上进且佛系自律的人。喜欢发小秘密/臭屁又爱炫耀。
奋斗的大学,激情的现在。赚了钱买了房,写了书出了名。当过面试官,带过徒弟搬过转。
大厂外来务工人员。是我,我是小熊,是不一样的烟火欢迎围观。
我的博客 机智的程序员小熊 欢迎收藏
相关文章:

我说我为什么抽不到SSR,原来是这段代码在作祟...
本文是龚国玮所写,熊哥有所新增修改删减,原文见文末。 我说我为什么抽不到SSR,原来是加权随机算法在作祟 阅读本文需要做好心理准备,建议带着深究到底的决心和毅力进行学习! 灵魂拷问 为什么有 50% 的几率获得金币&a…...

MySQL MGR 集群新增节点
前言 服务器规划现状(CentOS7.x) IP地址主机名部署角色192.168.x.101mysql01mysql192.168.x.102mysql02mysql192.168.x.103mysql03mysql192.168.x.104proxysql01proxysql、keepalived192.168.x.105proxysql02proxysql、keepalived 新增服务器IP&#x…...

【单目标优化算法】蜣螂优化算法(Dung beetle optimizer,DBO)(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

【C++】类和对象入门必知
面向过程和面向对象的初步认识类的引入类的定义类的访问限定符封装类的作用域类的实例化类对象模型this指针C语言和C实现Stack的对比面向过程和面向对象的初步认识 C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解…...

day38 动态规划 | 509、斐波那契数 70、爬楼梯 746、使用最小花费爬楼梯
题目 509、斐波那契数 斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) 0,F(1) 1 F(n) F(n - 1) F(n - 2),其…...

2023年备考软考必须知道的6件事
不知不觉,距离2023年上半年软考也只有不到100天的时间了,报名入口也将在3月13日正式开通,你是正在犹豫是否参加考试? 还是已经开始着手准备复习? 关于软考考试你还有哪些疑问? 2023年备考软考必须知道的6件事,建议收藏…...

GLOG如何控制输出的小数点位数
1 问题 在小白的蹩脚翻译演绎型博文《GLOG从入门到入门》中,有位热心读者提问说:在保存日志时,浮点型变量的小数位数如何设置? 首先感谢这位“嘻嘻哈哈的地球人”赏光阅读了小白这不太通顺的博客文章,并提出了一个很…...

2022年全国职业院校技能大赛(中职组)网络安全竞赛试题A(6)
目录 模块A 基础设施设置与安全加固 一、项目和任务描述: 二、服务器环境说明 三、具体任务(每个任务得分以电子答题卡为准) A-1任务一:登录安全加固(Windows) 1.密码策略 a.密码策略必须同时满足大小…...

Safety-Gym环境配置与安
官网: https://github.com/openai/safety-gym https://github.com/openai/safety-starter-agents 一、安装依赖环境配置 建议使用python 3.7及以下环境,因为官方的safety-rl是基于tensorflow1.13.1实现,而tensorflow1.13.1只能支持python…...

3月再不跳槽,就晚了
从时间节点上来看,3月、4月是每年跳槽的黄金季! 以 BAT 为代表的互联网大厂,无论是薪资待遇、还是平台和福利,都一直是求职者眼中的香饽饽,“大厂经历” 在国内就业环境中无异于一块金子招牌。在这金三银四的时间里&a…...

HTTP cookie格式与约束
cookie是前端编程当中经常要使用到的概念,我们可以使用cookie利用浏览器来存放用户的状态信息保存用户做了一些什么事情。session是服务器端维护的状态。session又是如何和cookie关联起来。后面介绍cookie和session的使用。Cookie 是什么?RFC6265, HTTP …...

docker基础
docker基础 docker概述 docker的出现?docker解决思想docker历史docker链接docker能干什么?开发-运维 docker安装 镜像(image)容器(container)仓库(repository)底层原理 docker命令 帮助命令镜像命令 docker-images查看所有本地主机上的镜像docker-searc…...

【微信小程序】--JSON 配置文件作用(三)
💌 所属专栏:【微信小程序开发教程】 😀 作 者:我是夜阑的狗🐶 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询! &#…...

EDA-课设
EDA-课程设计-电子闹钟 一、实验目的 1.掌握多层电路在 QuartusII 集成开发环境中的实现; 2.熟练掌握基于 QuartusII 集成开发环境的组合逻辑电路设计流程; 3.掌握基于 QuartusII 集成开发环境的时序逻辑电路设计流程; 4.理解有限状态机设计…...

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

Java API 文档搜索引擎
1. 认识搜索引擎:在搜狗搜索的搜索结果页中, 包含了若干条结果, 每一个结果包含了图标, 标题, 描述, 展示URL等搜索引擎的本质:输入一个查询词, 得到若干个搜索结果, 每个搜索结果包含了标题, 描述, 展示URL和点击URL2. 搜索引擎思路:2.1 搜索的核心思路:当前我们有很多的网页(…...

2023美赛C题Wordle二三问分布预测和难度分类预测
文章目录前言题目介绍人数分布预测首先建立字母词典,加上时间特征数据预处理训练和预测函数保存模型函数位置编码模型及其参数设置模型训练以及训练曲线可视化预测人数分布难度分类预测总结前言 2023美赛选了C题,应该很多人会选,一看就好做&…...

gdb的简单练习
题目来自《ctf安全竞赛入门》1.用vim写代码vim gdb.c#include "stdio.h" #include "stdlib.h" void main() {int i 100;int j 101;if (i j){printf("bingooooooooo.");system("/bin/sh");}elseprintf("error............&quo…...

如何使用python AI快速比对两张人脸图像?
本篇文章的代码块的实现主要是为了能够快速的通过python第三方非标准库对比出两张人脸是否一样。 实现过程比较简单,但是第三方python依赖的安装过程较为曲折,下面是通过实践对比总结出来的能够支持的几个版本,避免大家踩坑。 python版本&a…...

(2)C#传智:变量基础(第二天)
一、注释符 不写注释是流氓,名字瞎起是扯蛋。 注释作用:解释与注销 命名: 以字母、_、开头,里面只能有_与特殊符,其它不得出现如%*&^等。 不能与关键字重复。区分大小写,Num…...

02-mysql高级-
文章目录mysql高级1,约束1.1 概念1.2 分类1.3 非空约束1.4 唯一约束1.5 主键约束1.6 默认约束1.7 约束练习1.8 外键约束1.8.1 概述1.8.2 语法1.8.3 练习2,数据库设计2.1 数据库设计简介2.2 表关系(一对多)mysql高级 今日目标 掌握约束的使用 掌握表关系…...

windows 使用everything 查看文件(夹)存储空间占用
起因 总是那个原因,C: D: E:全都红了,下的游戏太多了,然后就这样了,之前也有过不少这种情况.几年前,就在智能手机上见过类似的功能. 大概就是遍历文件系统,统计每个文件的大小,然后父节点记录所有子节点的和,然后可以显示占用百分比之类的. 经过 在windows 上我最开始使用ex…...

2023该好好赚钱了,推荐三个下班就能做的副业
在过去的两年里,越来越多的同事选择辞职创业。许多人通过互联网红利赚到了他们的第一桶金。随着短视频的兴起,越来越多的人吹嘘自己年收入百万,导致很多刚进入职场的年轻人逐渐迷失自我,认为钱特别容易赚。但事实上,80…...

vue3如何进行数据监听watch/watchEffect
我们都知道监听器的作用是在每次响应式状态发生变化时触发,在组合式 API 中,我们可以使用 watch()函数和watchEffect()函数, 当你更改了响应式状态,它可能会同时触发 Vue 组件更新和侦听器回调。 默认情况下,用户创建的侦听器回…...

Wgcloud安装和使用(性能监控)
一、Wgcloud说明 官网:https://www.wgstart.com/ WGCLOUD支持主机各种指标监测(cpu使用率,cpu温度,内存使用率,磁盘容量,磁盘IO,硬盘SMART健康状态,系统负载,连接数量&…...

前端如何实现本地图片上传?
前端如何实现本地图片上传? 摘要 对于学习前端的小伙伴都有一个困惑,就是平常想上手小项目,但碍于不想购买服务器,实践受到了限制。 一般我选择node.js搭建服务器,毕竟基于JavaScript语言,简直不是一家人…...

【基础算法】差分的应用(一维差分和二维差分)
🌹作者:云小逸 📝个人主页:云小逸的主页 📝Github:云小逸的Github 🤟motto:要敢于一个人默默的面对自己,强大自己才是核心。不要等到什么都没有了,才下定决心去做。种一颗树,最好的时间是十年前…...

第49章 API统一集中管理
1 关于统一集中管理API的一些思考 1、统一集中管理是保证工程性项目得保质、保量、成功实施,并对后期维护提供数据支撑的最有效,最节省资源和时间的技能和做法,软件做为一种特殊的工程性项目,也符合上述特性。 2、由于在前台实现中…...

carla0.9.13-UE4添加4轮车模型(Linux系统)
前期准备建模工具:blender:v3.4.1;可以在Ubuntu Software商店直接下载虚拟引擎:carla-UE4 (carla v0.9.13),无需额外安装UE4,carla中自带插件编译carla参照官方文档:https://carla.readthedocs.io/en/0.9.1…...
对比yolov4和yolov3
目录 1. 网络结构的不同 1.1 Backbone 1.1.1 Darknet53 1.1.2 CSPDarknet53 1.2 Neck 1.2.1 FPN 1.2.2 PAN 1.2.3 SPP 1.3 Head 2. 数据增强 2.1 CutMix 2.2 Mosaic 3. 激活函数 4. 损失函数 5. 正则化方法 知识点 记录备忘。 总体而言&…...