🎮
Summary:给英雄联盟制作一个版本英雄强度的榜单算法需要做什么?数据是客观的,但算法整合和分析数据的方式是主观的。当下AI如此先进,以至于最难的部分早已不是写代码,而是从思维层面上如何让算法更精确,能真实地反映出英雄强度。这篇笔记主要记录了我使用AI开发和改进算法的过程。

前言

最初的目的是为了给《英雄联盟手游》游戏内做一个版本英雄强度排行榜单的功能。这个榜单比较类似端游用户常用的op.gg数据网站,它能根据每个版本更新后的对局数据统计出每个英雄的胜率、选用率和ban率,并以此作为参考,按照一个特定的算法计算出英雄的实时强度,并将他们按照强度进行排名,从而区分出T1、T2等不同强度级别的英雄供玩家参考。
不过由于op.gg并没有公开它们这套算法,所以我们只能大体参考这个榜单并根据已有的经验复刻这套榜单的算法,并使其结果尽量更加符合手游的对抗环境。
notion image
在此之前,实际上我从未接触过算法相关的工作,也并不具备独立开发这种功能的能力。但毕竟现在已经是AI的时代,它能帮我完成很多我并不了解的任务。下面记录的就是我使用AI来帮助我开发并不断迭代完善这一套算法的过程,主要想分享的还是迭代过程中的一些思考。
使用的AI平台:CodeBuddy
使用的模型:Claude-Opus-4.8

迭代过程

最初版本

最早的版本很直接:把胜率、登场率、Ban 率这三项数据分别做 0–100 的线性归一化,再按权重加权求和得出一个英雄的强度总分。
通常来说,不同游戏环境下对于胜率、登场率和ban率的权重划分肯定是有所不同的,我们根据手游的环境有一个初步的调整:胜率占绝对权重,ban率和登场率相比,由于我们的惯性ban这种情况比较严重,ban率的权重也会更低一些。因此,也制作了一个可以随时调整权重的工具,可以根据我们的需要进行调节。
notion image
这套逻辑几乎是所有强度榜的通用做法——直到我开始用真实数据表格去验证它。会发现实际的数据导入进来后,会出现各种各样的问题,每一个都需要专门对数据进行修正或筛选,最终才能得出相对准确的结果。

问题一:小样本的极端情况

真实数据里最先跳出来的问题,是一批胜率偏向极端的英雄,甚至是100%或0%——进一步看,会发现这些英雄通常是对局数极少的特殊情况。 如果直接按胜率排序,这些"偶然"肯定会把真正有统计意义的强度信号淹没。
首先解决数据量较大时的小样本问题,这里我们只需要进行一步预筛选,把在这个分路上登场率小于0.75%的英雄直接排除在外。
但在进行这一步筛选后仍然存在问题,如果数据量本身略有不足时,即使是筛选掉了0.75%登场率,留下的数据依然可能存在某个英雄总体场数不足的情况。
解决方式是经典的 贝叶斯收缩(shrinkage):让小样本的胜率按对局数向 50% 收缩,对局越少、收缩越狠:
K 默认取 1500——对局几十场的英雄几乎被拉回 50%,对局几十万场的英雄基本保留真实胜率。
 

问题二:线性打分,低估了"极端"的分量

这里存在一个问题:胜率49%→51% 和 52%→54%,真的该涨一样多的分数吗?
线性映射有一个隐含假设:任意两个胜率区间,只要跨度相同,得分变化就该相同。但这类游戏的经验告诉我们, 从 52% 涨到 54%,显然比从 49% 涨到 51% 意义更大——越靠近强度的"天花板",每一分胜率都更稀缺、更难获得。
因此,我让AI把胜率、登场率、Ban 率的分数计算公式从线性换成了幂函数曲线:越靠近边界,曲线越陡。
notion image
同样 2% 的胜率跨度:中心区几乎不涨分,两端区间陡峭上扬
实测下来,49%→51% 只涨 1.56 分,55%→57% 却能涨 18.75 分——两端的"含金量"是中心区的十余倍,这才更接近玩家对"强弱"的直觉。
将分数转换的曲线曲率γ设置为可调的参数,可以对曲线的陡峭程度进行调整。
notion image
此外,我们还需要分别设置胜率、登场率和Ban率两端的边界值,为了方便也将这个区间设置为可调的参数。在参考了大部分数据后,目前确定的边界值如下:
notion image

问题三:被一视同仁的五个分路

真实数据导入后才发现:全分段里,下路只有 21 个英雄能稳定登场,中路却有整整 46 个。同样是 10% 的登场率, 在中路意味着"人尽皆知的核心",在下路却只是"一般般"——分母不同,数字的含金量天差地别。
更棘手的是 Ban 率:英雄联盟手游数据里的 Ban 率是"英雄级"统计,不区分分路。一个英雄如果在主分路很强、被疯狂 Ban, 换到它很少玩的副分路,也会"继承"这个高 Ban 率——但实际上没人是在针对这个副分路 Ban 它。
notion image
左:五个分路的可登场英雄数差异明显;
右:折算系数让"人少的分路"降登场、提 Ban 权重
对策分为两步:
  1. 登场率按"本分路英雄数 / 各分路平均英雄数"折算;这样可以看得出来这一英雄在自身分路上的含金量到底有多高。
    1. 因此我们就得到了登场率得分的最终计算方式:
  1. Ban 率先做"英雄级别折算"——如果英雄在当前分路的登场率明显低于其在其他分路的登场率,就按比例把 Ban 率"打折",避免副分路蹭到主分路的热度。
    1. 当前分路登场率 < 该英雄其他分路登场率之和 × 25% 时,f1 = 当前分路登场率 / 全分路登场率,否则 f1 = 1。
      再做分路上的折算,按照"本分路ban率总和 / 各分路平均ban率总和"。
      但这引出了一个问题:《英雄联盟手游》的环境中由于惯性ban等问题的存在,会出现Ban率极端高的情况,例如一个 76% Ban 率的英雄,会将整条分路的平均Ban率陡然拉高。(举例来说,下路射手通常是玩家不会主动去ban的位置,但在某一段时间由于斯莫德的强度超标,导致这个英雄的ban率极高,如果根据目前这个算法,就会影响到这个分路的ban率计算。)
      这里最容易想到的办法是"剔除极端值",但直接删除意味着丢弃真实信息。更合理的方式是统计学里的缩尾(Winsorize):只在"计算分路基准"这一步,把单个英雄计入的 Ban 率封顶在 30%,但该英雄自己的得分仍然用真实 Ban 率计算——限制的是"代表性",不是"真实强度"。
      notion image
      中路的 Ban 率总和被三个极端个例拉高了 59%,缩尾之后基准线恢复正常,个例自身分数毫发无损
      因此我们就得到了Ban率得分的最终计算方式:

问题四:50% 未必是所有分路"及格线"的统一标准

同样 52% 的胜率,在不同分路该给一样的分吗?
此前所有的胜率曲线都以固定的 50% 为中心。但如果某条分路里的强势英雄格外集中(加权平均胜率天然偏高),那么在这条分路里拿到 52% 胜率的含金量,其实要比在平均水平更低的分路里拿到 52% 更低。
于是把曲线的中心,从写死的 50%,换成了"该分路登场率加权后的胜率均值"——中心一旦偏移,曲线两侧的"陡峭程度"就会不对称,形成统计学意义上的偏度(skewness)
notion image
中心右移后,同样的胜率在"整体偏强"的分路里得分反而更低——因为及格线本身更高了
不过在进行这一步计算过后,我们发现实际上对胜率得分的影响并不是很大,这也可以侧面说明目前我们游戏的分路并没有胜率天然偏高或偏低的分路,这是好事。
因此我们可以得到胜率分数的完整计算公式:
 

问题五:Ban率或登场率极端值导致的分数超标

靠登场率和 Ban 率堆分,堆得进 OP 榜吗?
权重调整到后期,出现了一类边界案例:某英雄凭借极高的登场率和 Ban 率,把综合分堆进了 OP 梯度, 但它的实际胜率并不出众,甚至低于 52%。这类英雄更像是"版本热度高"而非"真正超模"。 于是在评分之上加了一层胜率硬性门槛:不满足门槛,逐级降级,直到胜率达标为止。
梯度
胜率硬性下限(需 ≥)
OP
52%
T1
49.5%
T2
48%
T3 / T4
无限制
这一层规则很朴素,却是整个算法里"最不容妥协"的一条——分数可以体现热度,但高级别梯度必须守住"真的能赢"这条底线。

完整算法

经过以上所有修正,最终的评分计算链路:
备注:win=实际胜率%(banpick 用修复胜率);pick=登场率%;ban=Ban率%;games=对局数;γ=曲率(默认2)。clamp(x,a,b) 将 x 限制在 [a,b]。
  • 预筛选:剔除登场率过低的噪声数据
    • 样本收缩:小样本胜率向 50% 收缩
      • 登场率分数计算(分路折算)
        • Ban率分数计算(分路折算+单英雄极端值缩尾)
          • 胜率分数计算(根据分路折算出偏度曲线
            • 加权求和 → 得到综合评分
              • 按阈值定级,再按胜率硬性门槛做最终校验
              notion image
              最终呈现出健康的金字塔分布:越往顶端梯度越稀缺
               
              开放世界的驱动力与关卡设计攻守一体|立阵绘卷|宿敌之战——浅谈只狼的战斗设计如何塑造沉浸感
              Loading...