游戏开发论坛

 找回密码
 立即注册
搜索
查看: 12906|回复: 2

[原创] 10 连抽保底的概率模型:保底概率的设计

[复制链接]

1万

主题

1万

帖子

3万

积分

论坛元老

Rank: 8Rank: 8

积分
36572
发表于 2017-1-23 15:18:47 | 显示全部楼层 |阅读模式
5eb6978a8b9d329474d222a07c94b54920407594.jpg


  文/云风

  网游里有很多抽卡、开箱子之类的赌性玩法,在最开始,游戏设计者实现的时候,仅仅给这些抽取概率简单的设置了一个值。比如抽卡抽出橙卡的概率是10%,那么就是说,玩家每次抽一张卡,有90%的可能是白卡,10%的可能是橙卡。

  但大R玩家是大爷,需要小心伺候。如果感受不好,人跑了就亏大了。概率这个东西靠改进伪随机数发生器是解决不了体验问题的,大爷要是连抽20张都出不来橙卡,那是要怒删游戏的。

  连抽20张10%概率橙卡一张都抽不到的机会多不?一张抽不中的概率是0.9,20张都抽不中的概率是0.9^20=12.2%。这可不算小数字啊。平均8个大R就会碰到一次,一下子赶跑了1/8的金主,这个责任小策划可担当不起。

  所以、一般网游都会用各种规则来避免玩家出现连抽不中的尴尬。例如,我忘记是谁发明的10连抽规则:如果你购买一个大包连抽10次,我在规则上就保证里面一定至少有一张橙卡。实现它应该并不困难,按常规概率生成10张的卡包,如果里面没有橙卡,那么我加一张即可。

  但如果我想把10抽保底的规则惠及日常抽卡的玩家该怎么做呢?

  就是说,我希望任何玩家任何时候,接连抽了10张卡,我都想保证这10张卡里至少有一张橙卡。

  首先,要说明的一点:如果你同时想保证橙卡掉落率是10%,也就是在极大范围内,比如系统投放了一万张卡片中,其中要有一千张橙卡。那么同时保证每10张卡里有至少一张橙卡的话,结果一定是完全不随机的,也就是必须每抽9张白卡,必出一张橙卡。

  所以,如果即想要随机(出橙卡的概率稳定),又想有10张出一张的保底,那么橙卡投放量是一定超过1/10的。

  我们之前的游戏用了个很粗暴的方案:记录玩家已经连续几次没有抽中,如果连续次数超过9,就必给他一张橙卡。为什么我说这个方案粗暴,因为它其实破坏了抽卡的自然体验。虽然玩家的确更高兴了,但是概率却很不自然。不自然的方案(其实是生硬的打了个补丁)实现起来还容易出错,我们前段时间就因为实现bug多发放了很多稀有物品,这个bug就不展开说了。

  下面来看看,为什么这么做不自然。

  假设橙卡的掉率是10%,那么你在获得一张橙卡后,再抽下一张橙卡的概率就是0.1。下一张是白卡,再下一张是橙卡的概率是0.9*0.1,下两张是白卡,第三张是橙卡的概率是0.9^2*0.1……

  后续有10张及10张以上的概率总共有多少呢?我算了一下,大约是35%左右。

  我们把抽到两张橙卡之间会抽取到的白卡张数排成一个数列的话,这个数列的值的范围是0到正无穷。是的,非洲酋长可能永远抽不到橙卡。当然这只是理论值。

  如果你读过大学,学的是理工科,没有逃课的话,就应该知道,这个数列是大致符合指数分布的。指数分布正是用来表示独立随机事件发生的时间间隔的。

  当我们把这个数列中大于9的数字都强行改成9,那么9的出现频率就陡然跳变,这是极不自然的。(分布不平滑)

  从一致分布的随机数,转换为指数分布的随机数非常简单。如果你懒得自己推导,那么可以在爆栈网上找到公式。

  让我们回答前面的问题,如果我希望获得一个大约每10张卡里出一张橙卡的随机数列,除了每次random一个[0,10)的整数,判断证书是不是0以外,还有一个方法。那就是每次抽到一个橙卡后,都从一个指数分布的随机数列中取一个值出来,作为接下来会抽取到白卡的张数。按这个张数去发放白卡,等计数器减到0,就发一张橙卡给玩家。这个白卡张数的数值范围是[0,inf)。

  用lua实现的话,大概是这样的:

  1. math.floor(math.log(1-math.random()) * (-rate)) 其中 rate = 10 。
复制代码

  好了,如果我们想加上 10 张保底,又想让间隔大致符合指数分布怎么办?简单:

  1. function erand(rate)
  2.     while true do
  3.         local p = math.floor(math.log(1-math.random()) * (-rate))
  4.         if p < rate then
  5.             return p
  6.         end
  7.     end
  8. end
复制代码

  让产生出来的数字小于 10 的时候重来一次就好了。如果你担心这里死循环(实际并不会),也可以加上循环上限:

  1. function erand(rate)
  2.     for i = 1, 100 do  -- 100 可以随便写
  3.         local p = math.floor(math.log(1-math.random()) * (-rate))
  4.         if p < rate then
  5.             return p
  6.         end
  7.     end
  8.     return rate-1
  9. end
复制代码

  当然,一旦加上了 10 张保底,单张出橙卡的概率就大大增加了,增加到多少呢?大约是 21%。如果你希望保持 10% 左右的投放率,那么保底张数大约应该设置在 23 张左右。

0

主题

9

帖子

60

积分

注册会员

Rank: 2

积分
60
发表于 2017-1-23 16:12:58 | 显示全部楼层
你这个整复杂了~~就是默认设计10为保底位,然后其他9个还是纯随机出卡,这样RP好的,可以出多个橙卡,RP差,就1橙保底,照你那样整,就真的只是保底了~~~话说这应该是火星贴~~

0

主题

13

帖子

123

积分

注册会员

Rank: 2

积分
123
发表于 2017-1-23 18:25:05 | 显示全部楼层
阴阳师不就是没有保底么,玩家也不见少。保底无非就是两种做法,一种死规则,每抽几次必出,这种就不说了,另一种就是类似暴雪开橙装的做法,最简单的实现办法是权重,随着抽不种的次数增加,把橙卡的权重一次次往上提就可以了,先ROLL品质,再在品质内ROLLID,两层结构就足够了。一般来说,尤其是这种和付费相关的概率问题,要百分之百按照数值策划的设计来开发,程序是不能擅自改变的,否则万一实现和设计有误差,爆率出现了影响,这个锅谁也背不起。当然,这种关键功能也是需要大量测试数据的,不会拍拍脑门就上线。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2024-4-26 11:05

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表