想出一个比我以前“更长”的答案:
你已经链接了实现,它看起来像:
public long nextLong(){
return ((long) next(32) << 32) + next(32);
}
所以,很明显,一个随机数调用了 2 次 next(32)。
这意味着,如果next(32) 结果,2 个随机数将相等
相同数字的 4 倍,因为函数的其余部分是“硬编码”的。
查看next()函数,我们可以看到如下:
protected synchronized int next(int bits){
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
return (int) (seed >>> (48 - bits));
}
可以简单地忽略返回部分,因为同样:SAME 种子会导致
到相同的返回值 - 否则你的 CPU 坏了。
所以,总的来说:我们只需要专注于这条线
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
如果这将导致相同的种子,四次,已生成 2 个随机数,
是相等的。
(注意:可以排除 a,b,a,b 之类的序列以产生相同的结果。帖子足够长,我跳过那部分。)
首先,让我们消除<< 48 部分。那是什么意思?给定的数字 (1) 将左移
48 次。所以二进制0...01 将变成1000000000000000000000000000000000000000000000000(48 个零)
然后,减去一个,所以你会得到0111111111111111111111111111111111111111111111111(47个)
让我们看一下该等式的第一部分:
(seed * 0x5DEECE66D[L] + 0xB[L])
注意,结尾的 [L] 只会导致它是一个长值而不是整数。
所以,用二进制的话来说,这意味着:
seed * 10111011110111011001110011001101101 + 1011
毕竟函数看起来像
seed = (seed * 10111011110111011001110011001101101 + 1011) & (0111111111111111111111111111111111111111111111111)
(我省略了第一个值的前导零)
那么,& (0111111111111111111111111111111111111111111111111) 做了什么?
按位和运算符,基本上比较两个二进制数的每个位置。并且只有当它们都为“1”时,生成的二进制数中的位置才会为 1。
这就是说,等式(seed * 10111011110111011001110011001101101 + 1011) 中距离右侧大于 48 的每一位都将被忽略。
第 49 位等于 2^49 或 562949953421312 decimal - 这意味着 & (0111111111111111111111111111111111111111111111111) 基本上只是说
MAXIMUM 结果可以是562949953421312 - 1。
因此,562949953421312 将再次产生 0,而不是结果 562949953421313 将产生 1,依此类推。
我上面写的所有东西都可以很容易地验证:
虽然下面的代码会产生随机的种子 *11*:
private Long seed = 0L;
protected synchronized int next(int bits){
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
System.out.println(seed);
return (int) (seed >>> (48 - bits));
}
可以对种子进行逆向工程,并且还可以使用数字 562949953421312L 从非 0 种子中获取种子 11。
private Long seed = 562949953421312L - 0xBL / 0x5DEECE66DL;
protected synchronized int next(int bits){
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
System.out.println(seed);
return (int) (seed >>> (48 - bits));
}
所以,您会看到:种子 562949953421312 等于种子 0。
更容易证明:
Random r = new Random(0L);
Random r2 = new Random(562949953421312L);
if (r.nextLong()==r2.nextLong()){
System.out.println("Equal"); //You WILL get this!
}
当然是连续的:
Random r3 = new Random(1L);
Random r4 = new Random(562949953421313L);
if (r3.nextLong()==r4.nextLong()){
System.out.println("Equal");
}
为什么这个“神奇数字”(562949953421312L)很重要?
假设,我们从种子 0 开始。
第一个新种子将是:0 * 10111011110111011001110011001101101 + 1011 = 1011 (dec: 11)
下一个种子是:1011 * 10111011110111011001110011001101101 + 1011 = 100000010010100001011011110011010111010 (dec: 277363943098)
下一个种子(调用 3)将是:100000010010100001011011110011010111010 * 10111011110111011001110011001101101 + 1011 = 10000100101000000010101010100001010100010011100101100100111101 (dec 2389171320405252413)
所以,超出了562949953421312L的最大数量,这将导致随机数小于上述计算值。
此外,添加1011 将导致结果在奇数和偶数之间交替。 (不确定 real 的含义 - 加 1 也可以,恕我直言)
因此,生成 2 个种子(不是随机数)可确保它们不相等,因为已选择特定的“溢出”点 - 并且添加 MAXIMUM 值 (562949953421312L) 不足以在 2 内达到相同的数字几代人。
当 2 次相同的种子是不可能的时,4 次也是不可能的,这意味着 nextLong() 函数永远不会在 n 和 n+1 代中返回相同的值。
我不得不说,我想证明相反的情况。从统计的角度来看,相同数字的 2 倍是可能的 - 但也许这就是它被称为伪随机性的原因:)