我一直在阅读有关XorShift PRNG的文章,尤其是论文here
一个人here声明
数字位于[1,2 ** 64]的范围内。请注意,它永远不会是0。
查看有意义的代码:
uint64_t x;
uint64_t next(void) {
x ^= x >> 12; // a
x ^= x << 25; // b
x ^= x >> 27; // c
return x * UINT64_C(2685821657736338717);
}
如果x
为零,则每个下一个数字也为零。但是不会让它变得不那么有用吗?如果只需要min + rand() % (max - min)
,通常的使用模式可能是int
或将64位转换为32位。但是,如果永远不会返回0
,那可能是一个严重的问题。此外,这些位不是0
或1
,但显然0
缺失的概率相同,因此零或稍差。我甚至无法在维基百科上找到任何提及,所以我错过了什么?
那么在给定范围内从XorShift64 *生成随机,均匀分布的数字的好/适当方法是什么?
答案 0 :(得分:2)
简短回答:不,它不能归零。
根据Numeric Recipes&#34;它产生一整段2^64-1
[...]缺失值为零&#34;。
本质上是仔细选择那些移位值来制作非常长的序列(完全可能的一个w / 0),因此可以确定每个数字都是产生的。零确实是这个生成器的固定点,因此它产生2个序列:零,另一个包含所有其他数字。
因此,对于足够小的范围max-min
,IMO足以使函数(next() - 1) % (max - min) + min
或甚至完全省略减法,因为模将返回零。
如果想要更好的质量平等分配,应该使用通常的&#39;使用next()
作为范围为[1, 2^64)
答案 1 :(得分:-1)
我几乎可以肯定有一个x
,xorshift操作返回0。
证明:
首先,我们有这些方程式:
a = x ^ (x >> 12);
b = a ^ (a << 25);
c = b ^ (b >> 27);
代替他们:
b = (x ^ x >> 12) ^ ((x ^ x >> 12) << 25);
c = b ^ (b >> 27) = ((x ^ x >> 12) ^ ((x ^ x >> 12) << 25)) ^ (((x ^ x >> 12) ^ ((x ^ x >> 12) << 25)) >> 27);
正如您所看到的,虽然c
是一个复杂的等式,但它完全是阿贝尔的。
这意味着,您可以将c
的位表示为x
的位的完全布尔表达式。
因此,您可以简单地为位b0
,b1
,b2
构建一个方程式系统,......所以:
(注意:系数只是示例,我没有计算它们,但看起来也是如此):
c0 = x1 ^ !x32 ^ x47 ...
c1 = x23 ^ x45 ^ !x61 ...
...
c63 = !x13 ^ ...
从那时起,你有64个方程和64个未知数。您只需使用Gauss-elimination解决问题,您将始终拥有一个独特的解决方案。
除了一些罕见的情况,即方程系统的系数的行列式为零,但这种大矩阵的大小不太可能。
即使它发生了,也意味着你在每次迭代中都有信息丢失,即你无法获得{{1}的所有2^64
个可能值只有其中一些。
现在考虑更可能的可能性,系数矩阵是非零的。在这种情况下,对于x
的所有可能2^64
值,您的所有x
值均为2^64
,这些值都不同。
因此,你可以为零。
扩展名:实际上你得零为零...对不起,证明更有用的是表明它不像第一个地方那么简单。重要的是,您可以将c
的位表示为c
的位的布尔函数。
这个随机数生成器还有另一个问题。这就是即使你以某种方式修改函数 not 也有这样的问题(例如,通过在每次迭代中添加1):
您仍然无法保证它不会因x
的任何可能值而进入短循环*。如果有值为345234523452345的5长度循环怎么办?你能证明所有可能的初始值吗?我不能。
实际上,拥有真正的伪随机迭代函数,您的系统可能会在x
次迭代后循环。它有一个几乎无足轻重的组合理由,但不幸的是,这个边距很小,不能包含它。&#34; ; - )
所以:
2^32
循环长度没问题,那么请使用从网上某处收集的经过验证的迭代函数。2^32
。这将导致大约2^128
循环长度,这并不是那么糟糕。2^64
(即,将内部状态(x>>64) ^ (x&(2^64-1))
的上半部分和下半部分对齐)。 / LI>