一、总论
1.伪随机机制的意义
1.1 什么是伪随机机制
日常生活中大家都喜欢玩单机或者网络游戏,而在这些游戏中常常会存在随机,比如暴击率20%或者被动击晕15%等等,而在这些随机事件中,我们往往被告知这些随机事件也能分成两类:一类是固定的概率,常被称为“真随机”,另一类是根据游戏行为有变化的概率,则叫做“伪随机”。我们都知道计算机只能产生伪随机数,那么这里的“真随机”和“伪随机”也不是我们通常对于计算机生成随机数的随机性质的定义,而是关于这个可见概率的具体实施方法。
1.2 为什么要使用伪随机机制
所谓的“真随机”在足够大的样本下,其中某一段的连续样本会距离整体期望产生较大的偏差,简单地说,就是“脸黑,怎么一直不暴击”或者“怎么脸那么好,一直在爆击”。而“伪随机”则在具体的游戏进程中对概率进行了及时的修正,以图实现足够稳定的样本子序列。举个简单的例子的话,就是当你打不出爆击时会增加下一次的概率,而你连续打出爆击时则会降低概率,虽然这对于某些运气好的玩家来说可能是一个噩耗,但是总体上来说,这挽救了某些运气实在太糟糕的朋友。
- 一个合理的伪随机机制的数据
2.1 以一个被动击晕技能为模版
假设这个技能来自某款知名ARPG游戏,并且这个技能在某个前版本里被修正成了伪随机机制
被动击晕:概率25%
显然这个数据的大小是和我们玩的各类游戏中的被动都十分接近的,所以这个样本有其代表性
2.2 伪随机机制的实现数据
首先需要感谢前辈们为我们分析出了WAR3这款经典的RTS游戏内的伪随机实现过程,我们可直接套用这个关系来实现我们的伪随机机制。

P是游戏中显示的概率,c是实际操作概率,maxN是第N次连续不触发后必定触发的临界,Pcount为大样本测试下的实际概率
而具体实现流程则是
1.第一次攻击,概率为C,如果暴击返回1,未爆击进入2
2.第二次攻击,概率为2C,如果暴击返回1,未爆击进入3
……
N.第N次攻击,必定爆击,返回1
那么我们的代码也会依照着这样一个明确的流程来实现。
二、实现
//伪随机实现过程 #include<iostream> #include<cstdlib> #include<cstring> using namespace std; #define P 200 //P和C均扩大若干倍以方便计算 #define C 84.75 // #define maxN 11 //参照表格 #define Num 100 //实际测试组数 int main() { bool a[1001]; double cUsing=C; int p,count=0; int i,j; srand(time(NULL)); for(i=1;i<=Num;i++){ memset(a,false,sizeof(a)); for(j=0;j<=(int)cUsing;j++){ a[j]=true; } p=(int)1000*rand()/RAND_MAX; //根据数组的值判断是否生效 if(a[p]){ cout<<cUsing<<" Y"<<endl; count++; cUsing=C; } else{ cout<<"N"<<endl; cUsing+=C; } } cout<<1.0*count/Num<<endl; return 0; }
Num | 实际样本概率(理论值0.25) | 误差[(理论-实际)/理论] |
100 | 0.235 | -0.06 |
1000 | 0.255 | 0.02 |
100000 | 0.24989 | 0.00044 |
100000 | 0.25048 | 0.00192 |
可以料想到随着样本的足够大,我们的确可以实现较为稳定的输出结果:25%的暴击概率
三、总结
随着网络游戏的不断发展,概率触发这个增进游戏不确定度的机制在游戏中愈发多的被使用,如炉石传说中的食人魔一族、DOTA2中各类击晕和爆击,甚至是整个RPG游戏族内的装备爆率,都要利用到概率这一利器,而其中有一些是固定概率的“真随机”,而另一部分则是不定概率“伪随机”,这些各有特色的机制让我们的游戏过程更为随机,让我们的游戏结果更为的出乎意料,也让我们获得了更多快乐。
而这个应用则让我们实际操作了一次伪随机机制的运行过程,让我们对游戏背后的运行原理加深了理解,希望能够帮助到未来有志于从事游戏开发的朋友。