什么是游戏的好随机数生成器?

时间:2009-06-25 23:34:43

标签: c++ performance random

用于C ++游戏的优秀随机数生成器是什么?

我的考虑是:

  1. 需要很多随机数,因此速度很快。
  2. 玩家总会抱怨随机数字,但我希望能够将它们指向一个解释我真正完成工作的参考。
  3. 由于这是一个我没有太多时间的商业项目,如果算法a)相对容易实现或b)具有良好的非GPL实现,那将是很好的。
  4. 我已经在很多地方使用rand(),所以任何其他生成器最好能够证明它需要的所有更改。
  5. 我对这个主题知之甚少,所以我能想出的唯一选择是Mersenne Twister;它满足所有这些要求吗?还有什么比这更好的吗?

    编辑: Mersenne Twister似乎是共识的选择。但是第4点呢?它真的比rand()好多了吗?

    编辑2:让我在第2点稍微清楚一点:通过了解随机数,玩家无法作弊。期。我希望它足够随意,人们(至少那些了解随机性的人)不能抱怨它,但我并不担心预测。 这就是为什么我把速度作为首要考虑因素。

    编辑3:我现在倾向于Marsaglia RNG,但我仍然想要更多输入。因此,我正在设立赏金。

    编辑4:只是一个注释:我打算在今天午夜UTC之前接受一个答案(以避免弄乱别人的代表上限)。所以如果你想回答,不要等到最后一分钟! 此外,我喜欢Marsaglia的XORshift发电机的外观。有人对此有任何意见吗?

16 个答案:

答案 0 :(得分:42)

Sometimes游戏开发者不希望获得真正的随机性,shuffle bag更合适。

如果你想要随机性,梅森捻线机满足你的要求。它速度快,统计随机,周期长,而且有很多实现。

修改:rand()通常以linear congruential generator的形式实施。如果您明智地选择它是否足以满足您的目的,那么这可能是最好的。

答案 1 :(得分:38)

现在有比Mersenne Twister更好的选择。这是一款名为WELL512的RNG,由Mersenne的设计师设计,10年后开发,是游戏的更好选择。该代码由Chris Lomont博士提交公共领域。他声称这种实现比Mersenne快40%,当状态包含许多0位时,不会受到扩散和陷阱的影响,并且显然是很简单的代码。它的周期为2 ^ 512; PC需要10到100年的时间来循环通过状态,因此足够大。

这是一篇关于PRNG的文章,在那里我发现了WELL512的实现。 http://www.lomont.org/Math/Papers/2008/Lomont_PRNG_2008.pdf

所以 - 更快,更简单,10年后由相同的设计师创造,并产生比Mersenne更好的数字。你怎么会弄错? :)

更新(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];
   }

答案 2 :(得分:26)

George Marsaglia开发了一些目前可用的最好和最快的RNG Multiply-with-carry是一个值得注意的统一分布。

===更新2018-09-12 ===

对于我自己的工作,我现在正在使用Xoshiro256**,这是Marsaglia的XorShift的一种进化/更新。

答案 3 :(得分:10)

Mersenne Twister是行业中的典型代表,特别是因为它非常适合SIMD并且可以超快速地制造。 Knuth也很受欢迎(谢谢David)。

在大多数游戏应用程序中,速度确实是关键因素,因为玩家会抱怨低帧率比他们抱怨的事实是,只要前面有7,就会产生轻微的偏差。 ,2和9按此顺序。

当然例外是赌钱,但您的相关许可机构会专门列出您可以使用的算法。

答案 4 :(得分:9)

购买便宜的webcamera,电离式烟雾探测器。拆卸它们两者,烟雾探测器含有很少的放射性物质 - 伽马波源 - 这将导致你的摄像头发射光子。这是你真正随机性的来源:)

答案 5 :(得分:6)

Mersenne Twister非常好,而且速度也很快。我在游戏中使用它并且实现或使用并不困难。

WELL random algorithm被设计为对Mersenne Twister的改进。 Game Gems 7有更多信息。在它上面,如果你可以借用它或拥有它。

在我链接到的WELL页面上,数字是算法的周期。也就是说,你需要在需要重新播种之前获得2 ^ N - 1个数字,其中N是:512,1024,19937或44497.Mersenne Twister的周期为N = 19937,或2 ^ 19937 - 1.你将会看到这是非常大的数字:)

我可以指出的另一件事是boost有一个random library,您应该会发现它很有用。

响应你的编辑,是的,Twister或WELL比rand()要好得多。此外,旧模数技巧会损害数字的分布。更有理由使用boost:)

答案 6 :(得分:4)

在实时游戏中,玩家无法确定“好”发生器和“坏”发生器之间的区别。在回合制游戏中,你是对的 - 一些少数狂热者会抱怨。他们甚至会以极其痛苦的细节告诉你如何用糟糕的随机数发生器破坏他们的生活。

如果你需要一堆真正的随机数(而且你是一个在线游戏),你可以在Random.org获得一些。将它们用于回合制游戏,或作为实时游戏的种子。

答案 7 :(得分:4)

我是Isaac的粉丝,与mersense twister不同,它的密码学安全(你*不能通过观察卷来破解这段时间)

IBAA(rc4?)也是一个used by blizzard来阻止人们预测用于战利品卷的随机数。我想像你玩的时候用暗黑破坏神II做类似的事情离战网服务器。

*不能在任何合理的时间范围内(几个世纪?)

答案 8 :(得分:3)

基于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()实施更高的熵
  • 易于理解

答案 9 :(得分:2)

您应该考虑的其他标准是线程安全性。 (而且你应该在今天的多核环境中使用线程。)只是从多个线程调用rand就会弄乱它的确定性行为(如果你的游戏依赖于它)。至少我建议你切换到rand_r。

答案 10 :(得分:1)

我也投票支持Mersenne Twister。实现广泛可用,它具有非常大的2 ^ 19937 -1的周期,相当快并且通过大多数随机性测试,包括Marsaglia开发的Diehard测试。作为LCG的rand()和Co.产生较低质量的偏差,并且可以很容易地推断出它们的连续值。

然而,值得注意的是,将MT适当地播种到通过随机性测试的状态。通常像drand48()这样的LCG用于此目的。

我会说MT满足你设定的所有要求(可证明),而且像MWCG imo这样的东西也是一种过度杀伤。

答案 11 :(得分:1)

GameRand实现此处发布的算法 http://www.flipcode.com/archives/07-15-2002.shtml

这是我最初在80年代末开发的东西。 它在数值质量方面很容易击败rand(),并且可能成为最快的随机算法。

答案 12 :(得分:0)

  

我希望它足够随意(至少那些了解随机性的人)   我不能抱怨它,但我并不担心预测。

A-公顷!

这是你真正的要求!

在这个应用程序中使用Mersenne Twister没有人会指责你。

答案 13 :(得分:0)

根据目标操作系统,您可以使用/ dev / random。它并不需要任何实现,并且在Linux(可能还有其他一些操作系统)上它是真正随机的。读取阻塞直到有足够的熵可用,因此您可能希望读取文件并将其存储在缓冲区或其他线程中。如果您不能使用阻塞读取调用,则可以使用/ dev / urandom。它几乎和/ dev / random一样生成随机数据,但它会重用一些随机数据来立即提供输出。它不那么安全,但它可以正常工作,具体取决于你打算用它做什么。

答案 14 :(得分:0)

显然(我忘记了我读到它的地方,就像我忘了我读到咖喱在防止altzheimas方面的好处),采用新生成的GUID的校验和的绝对值非常随机。这是一个很大的数字,您可以使用它的模数来缩小它。

所以在SQL(我的区域)中,这是ABS(CHECKSUM(NEWID()))%1000

罗布

答案 15 :(得分:0)

你知道吗?如果你认为这个答案完全糟透了,请原谅我......但是我(因为上帝只知道是什么原因......)使用DateTime.Now.Milliseconds作为获取随机数的方法。我知道这不是完全随机的,但似乎是......

我只是无法打字这么多只是为了得到一个随机数! :P