[随机数]网游垫装备及其思考
学而无术者比不学无术者更加愚蠢 ----富兰克林
玩游戏的,总归会有很多心得,网上略微搜一下,就会发现很多垫装备的言论,很多人相信垫装备有用.这是问题!!
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