我正在尝试在C#中创建一个伪随机数生成器,在给定相同的输入集的情况下生成相同的输出。它需要快速,接受64位数字,特别重要的是允许任意一组输入。输出应该是[0和1}之间的两倍。
输入,大部分将是在应用程序生命周期内保持不变的种子值,以及一个或多个其他数值,例如x,y,z和w坐标,它们本身可能是输入int32,int64或double。
我已经尝试调整我发现的其他一些随机数生成器,但是我一直遇到大量的丛生问题,这主要是因为我不知道围绕这个主题的理论,或素数的数学属性,这似乎在大多数情况下都起作用。
我想使用此函数生成随机噪声,这些噪声将在许多其他算法内部使用,包括我自己的算法,以及Perlin噪声等标准算法。
我应该怎么做?
答案 0 :(得分:1)
这仍然是一个非常慢的rng,但比基于Murmur3的速度快大约10倍。对每个生成的数字进行重新种类都需要花费成本,因此需要大量种子才能对结果产生非系统性影响。
更新:确实没有任何理由允许弱位,这个版本应该没有明显的模式。
class Prng
{
const double shift3 = .125;
const double shift9 = shift3 * shift3 * shift3;
const double shift27 = shift9 * shift9 * shift9;
const double shift53 = shift27 * shift27 * 2.0;
public ulong rndlong(ulong a, ulong b, ulong c, ulong d){
ulong e = ((a ^ (b >> 14 | b << 50)) + ((c >> 31 | c << 33) ^ (d >> 18 | d << 46)))*1911413418482053185;
ulong f = (((a >> 30 | a << 34) ^ c) + ((b >> 32 | b << 32) ^ (d >> 50 | d << 14)))*1139072524405308145;
ulong g = (((a >> 49 | a << 15) ^ (d >> 33 | d << 31)) + (b ^ (c >> 48 | c << 16)))*8792993707439626365;
ulong h = (((a >> 17 | a << 47) ^ (b >> 47 | b << 17)) + ((c >> 15 | c << 49) ^ d))*1089642907432013597;
return (e ^ f ^ (g >> 21 | g << 43) ^ (h >> 44 | h << 20)) * 2550117894111961111 +
((e >> 20 | e << 44) ^ (f >> 41 | f << 23) ^ (g >> 42 | g << 22) ^ h) * 8786584852613159497 +
((e >> 43 | e << 21) ^ (f >> 22 | f << 42) ^ g ^ (h >> 23 | h << 41)) * 3971056679291618767;
}
public double rnddouble(ulong a, ulong b, ulong c, ulong d)
{
return (double)(rndlong(a, b, c, d) >> 11) * shift53;
}
}
答案 1 :(得分:0)
我找到了一个解决方案,我会用,直到有人说服我为什么不这样做。
以下代码是我班级的有用摘录。第一个Generate
函数可以根据您的需要进行任意数量的重载。您将输入转换为字节数组并将它们传递给私有的Generate方法,后者执行其余操作。请注意,在内部有一个对_seed
的引用,它只是一个字节数组本身,是通过构造函数提供的种子值生成的。
此外,代码依赖于this MurMurHash3 algorithm,这非常快。
我已经运行了大量的迭代来检查分布,它似乎分布非常均匀,没有明显的人类明显的聚集给定的值。我在英特尔酷睿i7上大约在720毫秒内产生了一百万个值,这足以满足我的需求。我还测试了它在纹理上产生2D白噪声,噪点看起来很随机。
public double Generate(double x, double y, double z, double w)
{
return Generate(
_seed,
BitConverter.GetBytes(x),
BitConverter.GetBytes(y),
BitConverter.GetBytes(z),
BitConverter.GetBytes(w)
);
}
private double Generate(params byte[][] inputs)
{
var len = 0;
int i;
for(i = 0; i < inputs.Length; i++)
len += inputs[i].Length;
var buffer = new byte[len];
var offset = 0;
for(i = 0; i < inputs.Length; i++)
{
var bytes = inputs[i];
Buffer.BlockCopy(bytes, 0, buffer, offset, bytes.Length);
offset += bytes.Length;
}
return Hash(buffer);
}
private double Hash(byte[] bytes)
{
var hash = new Murmur3().ComputeHash(bytes);
var buffer = new byte[8];
for(var i = 0; i < hash.Length; i++)
buffer[i%8] ^= hash[i];
var n = BitConverter.ToInt64(buffer, 0);
if(n < 0) n = -n;
if(n == long.MaxValue) n--;
return n / (double)long.MaxValue;
}