[随机数]网游垫装备及其思考
学而无术者比不学无术者更加愚蠢 ----富兰克林
玩游戏的,总归会有很多心得,网上略微搜一下,就会发现很多垫装备的言论,很多人相信垫装备有用.这是问题!!
OK,让我们来把问题简化一下,因为装备打造合成概率实在是繁复,所以存在必要的简化.问:
连续的抛一枚硬币,失败N次之后,第N+1次失败的概率是多少?会不会比50%高(!!!这是我们真正要搞定出的问题).
抛硬币,是随机事件.理论上讲,成功失败的概率各50%(头像朝上与否),而且任何两次随即之间完全无关.否则他就不叫随机事件了.当年概率论学的不好,但是头脑里面还有一点意识,我不相信连续的失败可以明显提高成功的概率!
但是理论学的太差,我不能证明第N+1次的概率还是那么高.....好吧,我只能写代码,看看模拟的真实情况是什么样子,来代码:
#include <stdlib.h>
#include <time.h>
#include <iostream>
#include <Windows.h>
#include <assert.h>
#pragma comment(lib, "advapi32.lib")
#define RANDOM_TIMES 1000000
#define FAIL_TIMES 5
#define FAIL_PERCENT 50
#define PERCENT_MAX 100
//#define C_RANDOM
#ifdef C_RANDOM
//nop
#else
#define RAND_MAX 65535
static HCRYPTPROV hProvider = 0;
static const DWORD dwLength = 2;
static BYTE pbBuffer[dwLength] = {};
#endif
static void
random_init()
{
#ifdef C_RANDOM
srand((int)time(0));
#else
DWORD result =::CryptAcquireContextW(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
assert(result);
#endif
}
static void
random_close()
{
#ifdef C_RANDOM
//nop
#else
::CryptReleaseContext(hProvider, 0);
#endif
}
static int
_random()
{
unsigned short _rand_value = 0;
#ifdef C_RANDOM
_rand_value = (unsigned short)rand();
#else
DWORD result = ::CryptGenRandom(hProvider, dwLength, pbBuffer);
assert(result);
_rand_value = *(unsigned short*)pbBuffer;
#endif
return _rand_value;
}
static long random_count = 0;
static int
random_result()
{
int times = 0;
while(times < FAIL_TIMES)
{
int _num = _random();
random_count++;
if((1.0f*_num/RAND_MAX) > (1.0f*FAIL_PERCENT/PERCENT_MAX))
{
times++;
}
else
{
times = 0;
}
}
random_count++;
return _random();
}
int main(int argc, char* argv[])
{
random_init();
long times_total = 0;
long times_fail = 0;
for(int times = 0; times < RANDOM_TIMES;++times)
{
int x = random_result();
if((1.0f*x/RAND_MAX) > (1.0f*FAIL_PERCENT/PERCENT_MAX))
times_fail++;
times_total++;
}
std::cout<<"total: "<<times_total<<std::endl;
std::cout<<"fail: "<<times_fail<<std::endl;
std::cout<<"random_count: "<<random_count<<std::endl;
system("pause");
random_close();
return 0;
}
这里用了两种随机数的实现,一种是标准C随机数,另外是CryptGenRandom.Windows下面没/dev/random和/dev/urandom,所以用哪个API代替.
代码我不想做过多的解释,比较重要的就那几个宏,没事干自己改变一下宏,运行一下,看看结果:-)我这边CryptGenRandom的两次结果:
total: 1000000 fail: 499497 random_count: 62905788 请按任意键继续. . . total: 1000000 fail: 499914 random_count: 62979706 请按任意键继续. . .
另外再上一次标准C随机数的运行结果:
total: 1000000 fail: 500330 random_count: 63103246 请按任意键继续. . .
可以看到,几次随即模拟的结果,差不多是相似的:连续失败N次之后,第N+1次的概率是不变的. 这才叫随机事件.:-)
但是问题还没完,这里需要架设随机数的质量非常的好,两次随机之间没有关联.事实上,标准C的随机数很难做到这一点(伪随机数生成器).伪随机数,有可能被破解,预测;真随机数不会:-D.
所以,网游在进行跟RMB相关的随机时,可以考虑一下真随机数,或者是质量稍微好一点的RNG;跟RMB无关的,libc的rand/rand_r足矣~~~~
PS:
记得我们一个策划,给怪物掉落的概率设置的太低(20%还是25%),然后打了据说有四十几个怪,一个东西都没掉....后来换成rand_r,效果好了很多.
参考:
http://msdn.microsoft.com/en-us/library/aa379942(v=vs.85).aspx