参考:概率补偿算法:从PRD算法的缺点谈起

一、PRD算法简介

PRD(Pseudo Random Distribution)算法,即伪随机分布算法,最早起源于游戏 War3 ,用于调整暴击分布,公式如下:

P(N) = C * N ,N = 1,2,3,······,T
T = ceil(1/C),ceil 为向上取整,如 ceil(1.2) = 2

N表示当前攻击的次数,P(N)表示当前攻击的暴击率,C为概率增量以及初始概率,T表示N的最大值,即暴击率的循环周期;如果某一次攻击产生了暴击,则需要将 N 重置为 1;如果这次攻击没有产生暴击,则 N + 1。

在 DotA2 的官方 wiki 中,列出了各暴击率对应的 C 值,而 C 值的计算代码并没有给出,本文提供计算 C 值的二分法。

二、由 C 值计算暴击率 p

逻辑推演:

设当前召唤师暴击率为 p
P(N) = C * N, N = 1,2,3,...,T
T = ceil(1/C),最大连续不暴击次数
p = 1/E(P(N))
E(P(N)) 为暴击的平 A 次数期望,如 暴击率 50% 时,期望为两下平 A 必出暴击

// 由 C 值计算暴击率 pfunc EstimateCriticalChanceByC(c float64) float64 {// 当前平 A 暴击率,与平 A 次数相关currentAdCriticalChance := 0.0// 当前累计的暴击率currentSumCriticalChance := 0.0// 平 A 次数的期望ePn := 0.0// 第 t 下必暴击t := math.Ceil(1 / c)// 计算平 A 次数期望for i := float64(1); i <= t; i++ {currentAdCriticalChance = i * c * (1 - currentSumCriticalChance)//log.Println("currentAdCriticalChance => ", currentAdCriticalChance)currentSumCriticalChance += currentAdCriticalChance//log.Println("currentSumCriticalChance => ", currentSumCriticalChance)ePn += i * currentAdCriticalChance//log.Println("ePn => ", ePn)}// 返回估算的暴击率return 1 / ePn
}

三、暴力计算

// 暴力计算
// 在测试时可以看到该方法的弊端
// 当计算精度不够时,则没有办法取出最优的近似解func CalculateCByTraverse(expectCriticalChance float64) (float64, float64) {log.Printf("Expect Critical Chance => %.2f %%", expectCriticalChance*100)c := 0.01estimateCriticalChance := 0.0for {estimateCriticalChance = EstimateCriticalChanceByC(c)if expectCriticalChance-estimateCriticalChance >= 0.000001 && expectCriticalChance-estimateCriticalChance < 0.000010 {log.Println("Actually Critical Chance => ", estimateCriticalChance)log.Println("C => ", c)break}c += 0.000001// 出现精度无法匹配的情况if c >= 1.0 {log.Println("Actually Critical Chance => [null]")log.Println("C => [null]")c = 0.0break}}return c, estimateCriticalChance
}

四、二分查找法

计算 C 值的基本思路为二分查找法,从 0~p 之间查找 C 值,使得 P(N) 的平均概率近似等于 p 。

// 二分查找法func CalculateCByBinarySearch(expectCriticalChance float64) (float64, float64) {left := 0.0right := expectCriticalChancemid := 0.0last := 1.0estimateCriticalChance := 0.0log.Printf("Expect Critical Chance => %.2f %%", expectCriticalChance*100)for {// 用 mid 作为 C 不断逼近正确值mid = (left + right) / 2.0// 用 C 估算处理的当期暴击率 estimateCriticalChanceestimateCriticalChance = EstimateCriticalChanceByC(mid)// 前后两次计算结果相同,说明到了逼近极限,不与 expectCriticalChance 比较是因为有误差,可能永远无法再逼近// 一开始差值 X 是最大的,然后逐渐变小,最终趋于 0if math.Abs(estimateCriticalChance-last) <= 0.0 {log.Println("Actually Critical Chance => ", estimateCriticalChance)break}// 如果估算概率大于预期概率,则往左边查找,否则往右边查找if estimateCriticalChance > expectCriticalChance {right = mid} else {left = mid}// 将本次估算的暴击率作为下次逼近的标准,若前后两次计算结果相同,left == right ,说明到了逼近极限last = estimateCriticalChance}log.Println("C => ", mid)return mid, estimateCriticalChance}

五、输出结果保存为CSV文件

package utilsimport ("encoding/csv""log""os"
)func SaveAsCsv(csvFileName string, header []string, content [][]string) {// 创建 csv 文件file, err := os.Create(csvFileName)if err != nil {log.Fatalln(err.Error())}defer file.Close()// 写入UTF-8 BOM,防止中文乱码file.WriteString("\xEF\xBB\xBF")writer := csv.NewWriter(file)defer writer.Flush()// Write CSV headerwriter.Write(header)for i := 0; i < len(content); i++ {writer.Write(content[i])}
}

六、测试

package testimport ("demo/utils""fmt""testing"
)func TestCalculateCByTraverse(t *testing.T) {var res [][]stringfor i, j := float64(5), 0; i < 100; i, j = i+5, j+1 {c, estimateCriticalChance := utils.CalculateCByTraverse(i / 100)res = append(res, []string{fmt.Sprintf("%f", c), fmt.Sprintf("%f", i/100), fmt.Sprintf("%f", estimateCriticalChance)})}//fmt.Println(res)utils.SaveAsCsv("TestCalculateCByTraverse.csv",[]string{"C", "Nominal Chance", "Approximate C"},res)
}func TestCalculateCByBinarySearch(t *testing.T) {var res [][]stringfor i, j := float64(5), 0; i < 100; i, j = i+5, j+1 {c, estimateCriticalChance := utils.CalculateCByBinarySearch(i / 100)res = append(res, []string{fmt.Sprintf("%f", c), fmt.Sprintf("%f", i/100), fmt.Sprintf("%f", estimateCriticalChance)})}//fmt.Println(res)utils.SaveAsCsv("TestCalculateCByBinarySearch.csv",[]string{"C", "Nominal Chance", "Approximate C"},res)
}--- PASS: TestCalculateCByTraverse (0.15s)
--- PASS: TestCalculateCByBinarySearch (0.01s)

对比 Dota2 官方数据 :

PRD 算法 Golang 实现相关推荐

  1. 获取用户列表为空_数据结构和算法(Golang实现)(15)常见数据结构-列表

    列表 一.列表 List 我们又经常听到 列表 List 数据结构,其实这只是更宏观的统称,表示存放数据的队列. 列表 List:存放数据,数据按顺序排列,可以依次入队和出队,有序号关系,可以取出某序 ...

  2. vrp 节约算法 c++_数据结构和算法(Golang实现)(8.1)基础知识-前言

    基础知识 学习数据结构和算法.我们要知道一些基础的知识. 一.什么是算法 算法(英文algorithm)这个词在中文里面博大精深,表示算账的方法,也可以表示运筹帷幄的计谋等.在计算机科技里,它表示什么 ...

  3. TF-IDF算法-golang实现

    1.TF-IDF算法介绍 TF-IDF(term frequency–inverse document frequency,词频-逆向文件频率)是一种用于信息检索(information retrie ...

  4. 微信小程序获取手机号 -加密数据解密算法-golang版本

    微信小程序端主动获取用户手机号时需要后端把加密的数据解密才能得到用户手机号 需要注意的点: 1:key  密钥  前端每获取一次用户openid就会刷新一次.所以在解密数据前要保证密钥为最新的. 微信 ...

  5. 雪花算法id长度_分布式全局ID生成器(雪花算法golang无锁版)

    //github地址:https://github.com/chenjie199234/Corelib/tree/master/id var offset uint64 = uint64(time.D ...

  6. 游戏中常用的伪随机算法之PRD暴击算法

    游戏中常用的伪随机算法之PRD暴击算法 PRD伪随机算法常用于游戏中的暴击算法,因此本文的标题将其称为 PRD暴击算法. 诞生与应用 PRD算法诞生与<魔兽争霸3>,可以说其诞生就是为了解 ...

  7. Golang基础 一揽子方案

    1. go mod tidy 自动下载所有依赖 2. 安装ptotobuf https://github.com/protocolbuffers/protobuf/releases go instal ...

  8. 这坑爹的抽卡机制,一晚上没睡,游戏的保底算法

    妈的,玩了<三国志~战略版>快2年了,抽五星将总是一种乘兴而来,败兴而归的,总是一周才能保底一次,我的心态已经够佛系了,于是就开始企图玄学了,比如 零点抽卡运气会变好! 先抽几发友情抽,再 ...

  9. DLang vs GoLang

    这次我打算好好写,  因为最近有点闲. 首先介绍两种语言: DLang 如果你不介意阅读英文, 那么DLang的官网有一份非常好的介绍, 简单概括下就是 C太古老了, 缺乏很多新的特性. C/C++太 ...

  10. sql2012服务器桌面图标,Windows Server 2012 修改桌面图标

    NOSQL场景梳理 Redis 场景:缓存,Session,消息发布订阅,产品属性分析,订单购买等强事务,计数等   Memcached 场景:读密集,写一般的缓存,Session   MongoDB ...

最新文章

  1. android 通知取消折叠_三星折叠屏手机发布会取消背后-折叠屏真的是用户想要的吗...
  2. OC基础 代理和协议
  3. windows卸载程序提示“请等待当前程序完成卸载或更改“问题解决方法,windows卸载卡进程问题解决方法
  4. Android --- 无法预览xml布局视图的解决办法
  5. nginx、Apache、IIS服务器解决 413 Request Entity Too Large问题方法汇总
  6. pycharm中python的默认安装路径_mac PyCharm添加Python解释器及添加package路径的方法...
  7. XenServer部署系列之05——虚拟机的创建及复制
  8. python基础篇–变量和简单的数据类型(下)
  9. Linux 数据重定向
  10. 新型人肉搜索Ark将完胜谷歌?
  11. echarts 广州地图入门案例
  12. 微信公众号授权H5页面
  13. 字节跳动Java实习面试题目大全
  14. expect巡检服务器_linux 巡检报告
  15. Jetpack Compose UI组件入门教程
  16. 106短信发送失败的原因
  17. Floyd 算法+例题
  18. 计算机专业单招考试网上面试范文,2分钟单招面试自我介绍
  19. for循环语句求数组当中的最大元素
  20. html如何制作正方体手工图,立体图形手工模型(怎样用卡纸制作正方体、长方体)...

热门文章

  1. 北京市参加汽车摇号条件
  2. w7计算机文件夹打开怎么设置密码,文件夹怎么设置密码,win7文件怎么加密-
  3. 浅析Servlet中的四大作用域
  4. 设置MobaXterm复制粘贴快捷键
  5. plot画图颜色设置
  6. jquery与ajax实现增删改查
  7. gwipr70驱动天空_win7 kmplayer plus设置_gwi pr2 pluswin7驱动_kmplayerplus官方下载
  8. 马未都说收藏:陶瓷篇(6、7)宋代八大民窑
  9. H3C认证安全技术高级工程师
  10. 阿里云云原生数据湖体系全解读——数据湖构建 数据导入