什么是一个好的游戏随机数发生器?
什么是一个好的随机数发生器用于在C + +的游戏?
我的考虑是:
- 很多随机数字是需要的,所以速度是好的。
- 玩家总是会抱怨随机数字,但是我希望能够给他们提供一个解释我真的做了我的工作的参考。
- 由于这是一个我没有太多时间的商业项目,所以如果algorithma)相对容易实现,或者b)具有良好的非GPL实现,那将是非常好的。
- 我已经在很多地方使用了
rand()
,所以任何其他的生成器都可以更好地certificate它需要的所有更改。
我对这个题目不太了解,所以我唯一可以提出的就是梅森扭转者(Mersenne Twister) 。 它是否满足所有这些要求? 还有什么更好的吗?
编辑:梅森扭转者似乎是共识的select。 但是关于第四点呢? 它真的比rand()
好多了吗?
编辑2:让我在第二点更清楚一点:玩家无法通过随机数字来作弊。 期。 我希望它足够随便,人们(至less懂得随机性的人)不能抱怨,但我并不担心预测。 这就是为什么我把速度作为首要考虑因素。
编辑3:我现在倾向于Marsaglia RNGs,但我仍然喜欢更多的input。 因此,我正在设立一个赏金。
编辑4:只是一个说明:我打算在今天午夜之前接受答案(以避免与某人的代表帽混淆)。 所以如果你想回答,不要等到最后一刻!
另外,我喜欢Marsaglia的XORshift发电机的外观。 有没有人有任何关于他们的意见?
George Marsaglia已经开发了一些目前可用的最好和最快的RNG。“ 随身携带”(Multiply-with-carry)是统一分发的一个显着特点。
有时候游戏开发者不想要真正的随机性, 洗牌袋是更合适的。
如果你想要随机性,梅森扭转者满足你的要求。 这是快速,统计随机,有一个很长的时期,有很多的实现。
编辑: rand()
通常被实现为线性同余生成器 。 如果您根据自己的目的做出明智的select,这可能是最好的select。
现在有更好的select比梅森扭转者。 这是一个名为WELL512的RNG,由Mersenne的devise师devise,10年后开发,为游戏提供了一个更好的select。 该代码由Chris Lomont博士进入公共领域。 他声称这个实现比Mersenne快40%,当状态包含很多0位时不会受到不良扩散和陷阱的影响,而且显然是更简单的代码。 它有一个2 ^ 512的时期; 个人电脑需要10多年的时间才能在各州之间循环,所以它足够大。
这里是一篇关于PRNGs的文章,我发现了WELL512的实现。 http://www.lomont.org/Math/Papers/2008/Lomont_PRNG_2008.pdf
所以 – 更快,更简单,10年后由同样的devise师创造出来,产生的数量比梅森更好。 你怎么会弄错? 🙂
更新(11-18-14) :修正错误(如上文链接所述,将0xDA442D20UL更改为0xDA442D24UL)。
/* initialize state to random bits */ static unsigned long state[16]; /* init should also reset this to 0 */ static unsigned int index = 0; /* return 32 bit random number */ unsigned long WELLRNG512(void) { unsigned long a, b, c, d; a = state[index]; c = state[(index+13)&15]; b = a^c^(a<<16)^(c<<15); c = state[(index+9)&15]; c ^= (c>>11); a = state[index] = b^c; d = a^((a<<5)&0xDA442D24UL); index = (index + 15)&15; a = state[index]; state[index] = a^b^d^(a<<2)^(b<<18)^(c<<28); return state[index]; }
Mersenne Twister在这个行业是典型的,特别是因为它可以很好地适用于SIMD,并且可以做得非常快。 克努特也很受欢迎(谢谢大卫)。
在大多数游戏应用程序中,速度确实是关键因素,因为玩家会抱怨比较低的帧速率比他们会抱怨的事实是,在产生3时有轻微的偏见,和9顺序。
当然是赌钱,但是在那里你的相关授权机构将会特别devise你可以使用的algorithm。
买一个便宜的摄像机,一个电离烟雾探测器。 拆卸它们两个,烟雾探测器包含一些放射性物质 – 伽马波源 – 这将导致在您的webcamera发射光子。 这是你的真正的随机性:)
Mersenne Twister非常好,速度也很快。 我在游戏中使用它,实现或使用并不困难。
WELL随机algorithm被devise为对Mersenne Twister的改进。 游戏gem 7有更多的信息。 就可以了,如果你可以借用或拥有它。
在我连接到的WELL页面上,数字就是algorithm的周期。 也就是说,你可以得到2 ^ N – 1的数字,在它需要重新加载之前,其中N是:512,1024,19937或44497. Mersenne Twister的周期为N = 19937,或者2 ^ 19937-1。看到这是一个非常大的数字 🙂
我唯一可以指出的是boost有一个随机库 ,你应该find有用的。
为了回应你的编辑,是的,Twister或WELL比rand()要好的多。 另外,旧的模数技巧会损害数字的分布。 更有理由使用boost 🙂
在实时游戏中,玩家无法确定“好”发电机和“坏”发电机之间的差异。 在回合制游戏中,你是对的 – 一些狂热分子会抱怨。 他们甚至会用令人费解的细节告诉你,如何用坏的随机数发生器毁了他们的生活。
如果你需要一些真正的随机数(你是一个在线游戏),你可以在Random.org上find一些。 使用它们进行回合制游戏,或作为实时游戏的种子。
我是艾萨克的粉丝,不像梅森扭转者,它是密码保密的(你*无法通过观察卷来破解这段时间)
IBAA (rc4?)也是一个被暴雪用来防止人们预测用于战利品卷的随机数的游戏..我想象一下类似的事情在暗黑破坏神II的时候是在战网服务器上玩的。
*不能在任何合理的时间内(几个世纪?)
基于Ian C. Bullard的随机数发生器:
// utils.hpp namespace utils { void srand(unsigned int seed); void srand(); unsigned int rand(); } // utils.cpp #include "utils.hpp" #include <time.h> namespace { static unsigned int s_rand_high = 1; static unsigned int s_rand_low = 1 ^ 0x49616E42; } void utils::srand(unsigned int seed) { s_rand_high = seed; s_rand_low = seed ^ 0x49616E42; } void utils::srand() { utils::srand(static_cast<unsigned int>(time(0))); } unsigned int utils::rand() { static const int shift = sizeof(int) / 2; s_rand_high = (s_rand_high >> shift) + (s_rand_high << shift); s_rand_high += s_rand_low; s_rand_low += s_rand_high; return s_rand_high; }
为什么?
- 非常,非常快
- 比大多数标准的
rand()
实现更高的熵 - 容易明白
你应该考虑一个额外的标准是线程安全。 (而且你应该在今天的多核心环境中使用线程。)从多个线程调用rand可能会混淆它的确定性行为(如果你的游戏依赖于它的话)。 至less我build议你切换到rand_r。
我也会投票给Mersenne Twister。 实现广泛可用,它具有非常大的2 ^ 19937-1的周期,是相当快的,通过了大部分随机性testing,包括由Marsaglia开发的Diehardtesting。 兰特()和公司,是LCGs,产生较低的质量偏差,他们的连续值可以很容易推断。
但是,需要注意的一点是将MT正确地种入通过随机性testing的状态。 通常像drand48()这样的LCG用于这个目的。
我会说MT满足你所设定的所有要求(可以certificate),对于像MWCG这样的事情来说,这会是一个矫枉过正的事情。
你知道吗? 原谅我,如果你认为这个答案完全糟糕…但我已经(因为上帝只知道什么原因…)使用DateTime.Now.Milliseconds
作为一种方式来获得一个随机数。 我知道这不是完全随机的,但似乎是…
我只是不能打扰这么多只是得到一个随机数字! :P
GameRand实现这里发布的algorithmhttp://www.flipcode.com/archives/07-15-2002.shtml
这是我最初在80年代后期开发的。 它在数值质量方面很容易击败rand(),并且作为可能的最快的随机algorithm的副作用。
我希望它足够随便,人们(至less懂得随机性的人)不能抱怨,但我并不担心预测。
A-HA!
有你真正的要求!
没有人会因为在这个应用程序中使用Mersenne Twister而造成错误。
根据目标操作系统,您可能可以使用/ dev / random。 它并不需要任何实现,在Linux(也可能是其他一些操作系统)上,它确实是随机的。 读取块直到有足够的熵可用,所以你可能想要读取文件并将其存储在缓冲区或使用另一个线程的东西。 如果你不能使用阻塞读取调用,你可以使用/ dev / urandom。 它产生的随机数据几乎和/ dev / random一样,但是它重用了一些随机数据来立即给出输出。 这不是安全的,但它可以正常工作,取决于你打算如何处理它。
很显然(我忘记了我读的地方,就像我忘记了咖喱在预防altzheimas的地方一样),拿一个新生成的GUID的校验和的绝对值是很好的随机的。 这是一个很大的数字,你可以使用模数来缩小它。
所以在SQL(我的区域),这是ABS(CHECKSUM(NEWID()))%1000
抢