概率PK直觉 如何平衡概率设计与玩家感受
一、关于概率的一些误解
在游戏中,经常会有一些概率上的设计。由于是概率,导致玩家对游戏的感受也是千差万别的,玩家经常会基于经验和直觉理解概率,从而对游戏产生很多误解。
纵观大多数游戏中的设定,有两种典型的利用概率的设计,一种是“真概率”,一种是“伪概率”。
真概率,就是两次操作之间完全没有关系,每次操作都是一次随机。比如游戏中常见的装备升星。
伪概率,就是预先固定好序列中的值,然后打乱这个序列,只是这个序列足够长,给玩家的一种是随机的感觉。
给玩家带来困惑、带来乐趣的,往往是“真概率”:
1)在游戏的论坛上,经常有玩家反馈说,对于某个玩法,自己试了几十次了,怎么一次都没成功?你们这个概率是骗人的吧?
2)又有些玩家说,在某个特定地图操作,概率会高,我在那里,连续两次都成功了呢。
3)策划同学还有时跑过来说,这个地方,是不是有bug?
下面就来说道说道这个概率的问题。
二、概率和直觉
10%的概率意思是操作10次必然有一次成功吗?
假设成功的概率是10%,那么抽取10次,至少抽中一次的概率是多少?100%?
这其实是一个经典概率问题,我们套用概率公式计算一下,操作10次至少成功一次的概率:
P = 1 -(1 - 0.1)^10 = 0.6513
也就是说,有大约35%的倒霉蛋,抽取了10次其实一次都没成功的。那么,概率10%,如果想成功的概率大约90%,玩家抽取的期望次数是多少呢?设次数为n,上面的公式可以写成:
1 - (1 - 0.1)^ n > 0.9
0.9^n < 0.1
n > log_(0.9)(0.1)
查对数表可以知道n > 21.8543,也就是说,10%的概率,成功概率大于90%的期望次数差不多是22次。所以,那些在想:10%的概率,我都操作了10次了成功的想法真的是:图样图森破。
但是,有倒霉蛋,必然就有幸运儿。
大神的诞生
10%的概率,连续两次成功的概率是多少?
一个人连续两次都成功的概率是1%,抽取10次,这种情况可以简化为:概率为1%,抽取10次,成功的概率是多少?
p = 1 - (1 - 0.01) ^ 10 = 0.0956
也就是说大约有10%的玩家会出现连续成功的情况
连续三次都成功的概率呢?
p = 1 - ( 1 - 0.001)^10 = 0.009955
也就是说,出现超神的概率大约1%。
总结:以10%的概率计算,操作10次,有35%的倒霉蛋一次都没成功,有10%的神仙连续成功了2次,有1%的超级大神连续成功了3次,这不是bug,这就是概率的神奇作用。
所以,那些看着概率觉得有黑幕的同学,不要怀疑,你真的就是运气差。
我们一个服务器以在线为3w来算,出现神仙、超神的概率,那是必然的。也就是说,每天服务器上都有大量的幸运儿诞生,也有更多的倒霉蛋诞生。倒霉蛋上论坛上说游戏是个坑,幸运的大神们在论坛上总结攻略,把自己前后关系的操作理解成因果关系,诞生出各种操作流,虽然荒谬,但是,这就是乐趣是不是?
三、概率和次数
一个问题:以10%的概率抽取10次和以20%的概率抽取5次,哪个中奖的概率比较高?100%的概率一次和50%的概率两次呢?
这个问题看起来很复杂,似乎难以取舍,但是我们只要推导出概率和次数之间的关系,就简化了问题,设我们至少中奖一次的概率是y,基础概率是p,抽取次数是n,则:
y=1-(1-p)^n;
因为p和n都是未知的,所以这个函数是比较复杂的。不过还是可以看出,随着p或n的增大,y都是在增大。而且根据p、n的取值范围(p∈(0,1],n∈N*),可以确定:
y∈(0,1];且p=1时,y=1;
而这个问题中,还有一个已知条件:p、n的积是常量。所以可以进一步分析:
设:k=p·n;则y可以分别转化为p或n的一元函数:
1 y是p的函数:
y=1-(1-p)^(k/p);
2 y是n的函数:
y=1-(1-k/n)^n;
可以看出:函数①是一个增函数;而函数②(在n的有效范围内)是一个减函数。所以,我们的结论是:在p、n乘积固定的情况下:
总中奖概率y,总是随着单次中奖率p的增大而增大;随着抽奖次数n的增大而减小——抽奖次数增加,说明单次中奖率降低,那么总中奖率自然也就降低了。
四、概率的计算
在开发过程中,检查一个随机的结果是成功还是失败,经常看到这样的代码:
int iResult = random() % 100;
if (iResult <= p)
success
else
fail
我们知道random的结果是从0 到 RAND_MAX一个序列,而RAND_MAX并不是100的正数倍,所以这个方式计算概率是不准确的。一个更好的方法是把random的结果除以RAND_MAX,得到一个小数,这个就是概率。
除了linux下的random函数,boost库也提供了30个随机数生成器和分布方式的模版。详细介绍见boost随机数库介绍
这里分别列出了每个生成器对内存和cpu的消耗,可以按需选择使用,如果不知道选择哪个,就选择mt19937
因为上面真随机的方式给玩家的感觉不那么随机,运气好的和运气差的对游戏的体验差别很大,有些系统为了提升玩家体验,采用“伪概率”的方式,就是事先预生成一个序列,这个序列每个玩家都一样,然后打乱这个序列的顺序,这样如果是10%的概率,100次必出10次。
五、基于经验的随机数的检测
随机数的测试总是个难题,因为要测试概率需要大量的样本,而测试的时候产生大量的样本费时费力,往往为了一个小小的功能点就消耗的大量的时间和精力。尤其是在游戏测试中,大部分都是手工测试,概率的问题往往得不到充分的测试。这里其实可以提供一个检测思路:
本福德法则法则
一组随机发生的数字,各个数字的首位存在一定规律,越小的数字出现的比率越高,既0出现的概率是100%(实际上首位不可能是0,因此我们可以认为其出现的概率是100%),1出现的概率是31%,2出现的概率是18%,依次类推,9出现的概率只有不到5%。
在b进位制中,以数n起头的数出现的机率为logb(n + 1) − logb(n) .本福特定律不但适用于个位数字,连多位的数也可用。在十进制首位数字的出现机率(%,小数点后一个位):
用这个方法,可以检测随机算法是否真的是随机的
详见百度百科:http://baike.baidu.com/view/1405011.htm
六、靠谱的随机数检验方式
NIST(国家标准与技术研究所)测试程序是一个统计包,包括16种测试手段。这些测试手段可测试由用作保密随机或者伪随机数发生器的硬件和软件产生的任意长的2进制序列的随机性。这些测试手段主要致力于判定可能存在于序列中的多种多样的非随机性。其中一些测试又可以分解成多种子测验。这16种测试手段是:
1. 频率检验,
2. 块内频数检验,
3. 游程检验,
4. 块内最长游程检验,
5. 二元矩阵秩检验,
6. 离散傅里叶变换检验,
7. 非重叠模块匹配检验,
8. 重叠模块匹配检验,
9. Maurer的通用统计检验,
10. Lempel-Ziv压缩检验,
11. 线性复杂度检验,
12. 序列检验,
13. 近似熵检验,
14. 累加和检验,
15. 随机游动检验,
16. 随机游动状态频数检验
详细介绍见:随机数测试方法介绍