baidu

[随机数]网游垫装备及其思考

  学而无术者比不学无术者更加愚蠢       ----富兰克林

 

  玩游戏的,总归会有很多心得,网上略微搜一下,就会发现很多垫装备的言论,很多人相信垫装备有用.这是问题!!

  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

posted @ 2011-03-05 14:14  egmkang  阅读(7015)  评论(8编辑  收藏  举报