用于程序内容的快速伪随机数生成器
我正在寻找一个伪随机数生成器,当它在生成每个数字之前得到种子时,它将专门用于快速工作。到目前为止,我看到的大多数生成器都假设我们设置一次种子,然后生成一长串数字。到目前为止,看起来与我唯一相似的是Perlin Noise,但是对于相似的输入,它生成的数据太"平滑",因此往往会产生相似的结果。
生成器的声明应类似于:
int RandomNumber1(int seed);
或者:
int RandomNumber3(int seedX, int seedY, int seedZ);
我认为拥有良好的RandomNumber1就足够了,因为可以通过散列其输入并将结果传递给RandomNumber1来实现RandomNumber3,但是我写了第二个原型,以防某些实现可以使用独立的输入。
此生成器的预期用途是将其用于过程内容生成器,例如通过将树木放置在网格中并确定每个位置的随机树木种类和随机空间偏移来生成森林。
生成器必须非常高效(低于500个CPU周期),因为在渲染过程中会实时大量创建程序内容。
解决方案
似乎我们要的是散列函数而不是PRNG。谷歌搜索"快速哈希函数"会产生一些有希望的结果。
例如:
uint32_t hash( uint32_t a) a = (a ^ 61) ^ (a >> 16); a = a + (a << 3); a = a ^ (a >> 4); a = a * 0x27d4eb2d; a = a ^ (a >> 15); return a; }
编辑:是的,某些哈希函数肯定比其他哈希函数更合适。
为了目的,充分了解该功能并检查输入中的一位更改是否会传播到很多输出位就足够了。
是的,我们正在寻找一种快速的整数哈希算法,而不是PRNG。
该页面包含几种算法,如果我们知道正确的搜索词,我相信我们会发现更多的算法。
编辑:原始页面已被删除,实时版本可以在GitHub上找到。
请参阅std :: tr1 :: ranlux3
或者其他TR1向标准C ++库添加的随机数生成器。我最初建议使用mt19937,但随后看到注释,它需要非常快。 TR1应该在Microsoft VC ++和GCC上可用,也可以在支持更多编译器的boost库中找到。
从boost文档改编的示例:
#include <random> #include <iostream> #include <iterator> #include <functional> #include <algorithm> #include <ctime> using namespace std; using namespace std::tr1; int main(){ random_device trueRand; ranlux3 rng(trueRand); // produces randomness out of thin air // see pseudo-random number generators uniform_int<> six(1,6); // distribution that maps to 1..6 // see random number distributions variate_generator<ranlux3&, uniform_int<> > die(rng, six); // glues randomness with mapping // simulate rolling a die generate_n( ostream_iterator<int>(cout, " "), 10, ref(die)); }
示例输出:
2 4 4 2 4 5 4 3 6 2
任何TR1随机数生成器都可以播种任何其他随机数生成器。如果需要更高质量的结果,请考虑将mt19937的输出(速度较慢,但质量较高)输入到生成器更快的minstd_rand或者randlux3中。
如果内存并不是真正的问题,并且速度至关重要,那么我们可以预构建大量随机数,然后在运行时对其进行遍历。例如,有一个单独的程序生成100,000个随机数,并将其保存为自己的文件,例如
unsigned int randarray [] = {1,2,3,....}
然后将该文件包含到编译器中,并且在运行时,随机数函数仅需要从该数组中提取数字,并在到达末尾时循环回到起始位置。
这是由乔治·玛格萨利亚(George Marsaglia)开发的小型随机数生成器。他是该领域的专家,因此我们可以放心生成器具有良好的统计属性。
v = 36969*(v & 65535) + (v >> 16); u = 18000*(u & 65535) + (u >> 16); return (v << 16) + u;
在这里,u和v是无符号的整数。将它们初始化为任何非零值。每次生成随机数时,请将u和v存储在某个位置。我们可以将其包装在一个函数中以匹配上面的签名(除非int是无符号的。)
我在Java随机数库中使用以下代码,这对我来说效果很好。我还将它用于生成程序内容。
/** * State for random number generation */ private static volatile long state=xorShift64(System.nanoTime()|0xCAFEBABE); /** * Gets a long random value * @return Random long value based on static state */ public static long nextLong() { long a=state; state = xorShift64(a); return a; } /** * XORShift algorithm - credit to George Marsaglia! * @param a initial state * @return new state */ public static final long xorShift64(long a) { a ^= (a << 21); a ^= (a >>> 35); a ^= (a << 4); return a; }