繁体   English   中英

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

[英]What is a good random number generator for a game?

在 C++ 中用于游戏的好的随机数生成器是什么?

我的考虑是:

  1. 需要很多随机数,所以速度很好。
  2. 玩家总是会抱怨随机数,但我希望能够向他们指出一个参考资料,说明我确实做到了我的工作。
  3. 由于这是一个我没有太多时间进行的商业项目,如果算法 a) 相对容易实现或 b) 有一个很好的非 GPL 实现可用,那就太好了。
  4. 我已经在很多地方使用了rand() ,所以任何其他生成器最好能证明它需要的所有更改是合理的。

我对这个主题了解不多,所以我能想到的唯一选择是Mersenne Twister 它是否满足所有这些要求? 还有什么更好的吗?

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

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

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

编辑 4:请注意:我打算在今天 UTC 午夜之前接受一个答案(以避免弄乱某人的代表上限)。 所以如果你想回答,不要等到最后一分钟!
另外,我喜欢 Marsaglia 的 XORshift 生成器的外观。 有没有人对他们有任何意见?

有时游戏开发者不想要真正的随机性, 洗牌包更合适。

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

编辑: rand()通常实现为线性同余生成器 如果您对它是否足以满足您的目的做出明智的选择,这可能是最好的。

现在有比 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 年后创建,并产生比梅森更好的数字。 你怎么会弄错? :)

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

George Marsaglia开发了一些目前可用的最好和最快的 RNG ,这是一个值得注意的均匀分布的乘法

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

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

=== 更新 2021-02-23 ===

在 .NET 6(目前为预览版)中,System.Random 的实现已更改为使用 xoshiro256**,但仅适用于无参数构造函数。 采用种子的构造函数使用旧的 PRNG 以保持向后兼容性。 有关更多信息,请参阅改进随机(性能、API 等)

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

在大多数游戏应用程序中,速度确实是关键因素,因为玩家会抱怨低帧率,而不是抱怨这样一个事实,即在 7、2、和 9 按此顺序。

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

买一个便宜的网络摄像头,一个电离烟雾探测器。 拆开它们,烟雾探测器几乎不含放射性物质 - 伽马波的来源 - 这会导致在您的网络摄像头上发射光子。 那是你真正随机性的来源:)

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

WELL 随机算法被设计为对 Mersenne Twister 的改进。 游戏宝石7 有更多信息。 在上面,如果你可以借用它或拥有它。

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

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

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

在实时游戏中,玩家无法确定“好”生成器和“坏”生成器之间的区别。 在回合制游戏中,您是对的——少数狂热者会抱怨。 他们甚至会详细地告诉你你是如何用一个糟糕的随机数生成器毁了他们生活的故事。

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

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

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 可能会干扰它的确定性行为(如果您的游戏依赖于此)。 至少我建议你切换到 rand_r。

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

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

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

然而,需要注意的一点是将 MT 正确地播种到通过随机性测试的状态。 通常像 drand48() 这样的 LCG 用于此目的。

我会说 MT 满足您设定的所有要求(可证明),而选择 MWCG imo 之类的东西是一种矫枉过正。

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

啊哈!

这才是你真正的需求!

没有人会因为在此应用程序中使用 Mersenne Twister 而责备您。

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

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

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

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

我只是不想为了得到一个随机数而费心打字! :P

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM