随机长,可以连续两次相同的数字

时间:2014-02-07 17:47:47

标签: java random

我想知道当前java 1.7的Random类实现,下面的代码是否有可能生成相同随机长度的两倍?

Random rand = new Random((long) "some seed".hashCode());
while(rand.nextLong() != rand.nextLong()){

}

System.out.println("Will this text ever be on the console?");

nextLong()和next();

的Java源代码
public long nextLong(){
    return ((long) next(32) << 32) + next(32);
}

protected synchronized int next(int bits){
    seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
    return (int) (seed >>> (48 - bits));
}

我会用false回答这个问题,因为我认为java使用的随机方法在2 ^ 48周期内不重复相同的数字,所以它永远不会在一行中生成两个相同的数字。这是对的吗?

4 个答案:

答案 0 :(得分:15)

要想出一个比我之前更长的答案:

您已经将实现链接起来,它看起来像:

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种子会导致 到SAME返回值 - 除此之外你的CPU坏了。

所以,总的来说:我们只需关注这一行

seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);

如果这将导致SAME种子,四次,则生成2个随机数, 这是平等的。

注意:可以排除类似a,b,a,b的序列以产生相同的结果。帖子足够长,我跳过那部分。) < / p>


首先,让我们消除<< 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.

这就是说,等式({1}}的位置大于48的位置将被忽略

第49位等于(seed * 10111011110111011001110011001101101 + 1011)2^49 - 意味着&amp; 562949953421312 decimal基本上只是说 MAXIMUM 结果可以是(0111111111111111111111111111111111111111111111111)。 因此,而不是结果562949953421312 - 1 - 它将再次产生0,562949953421312将产生1,依此类推。

我上面写的所有内容都可以轻松验证:

以下代码将生成随机种子 * 11 *:

562949953421313

可以对种子进行逆向工程,并使用数字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)); } 从非0种子中获取种子11。

562949953421312L

所以,你看,种子562949953421312等于种子0

更容易证明:

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));
}

它当然是连续的:

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"); } )很重要?

假设我们从种子0开始。

第一个新种子将是:562949953421312L

下一个种子将是:0 * 10111011110111011001110011001101101 + 1011 = 1011 (dec: 11)

下一个种子(电话3)将是:1011 * 10111011110111011001110011001101101 + 1011 = 100000010010100001011011110011010111010 (dec: 277363943098)

因此,超出了100000010010100001011011110011010111010 * 10111011110111011001110011001101101 + 1011 = 10000100101000000010101010100001010100010011100101100100111101 (dec 2389171320405252413)的最大数量,这将导致随机数小于上述计算值。

此外,添加562949953421312L将导致结果在奇数和偶数之间交替。 (不确定真正的含义 - 添加1可能也有效,imho)

因此,生成2个种子(非随机数)可确保它们不相等,因为已选择了特定的“溢出”点 - 并且添加MAXIMUM值(562949953421312L)不足以在2中达到相同的数字代。

当2次相同的种子不可能时,4次也是不可能的,这意味着,nextLong()函数永远不能为n和n + 1代返回相同的值。

我不得不说,我想证明相反的情况。从统计的角度来看,相同数量的2倍是可能的 - 但也许这就是为什么它被称为伪随机性:)

答案 1 :(得分:4)

不,使用此算法连续获得两个相同的长度是不可能的。

当人们写关于数学和其他巫术的长篇文章时,我去了代码猴子路线,而野蛮人在周末强迫了2 ^ 48种可能的种子。对于任何种子,连续产生的两个多头都不相同。


long int seed = 0;

int next(int bits){
    seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
    return (int) (seed >> (48 - bits));
}
long int nextLong(){
    return ((long int) next(32) << 32) + next(32);
}

int main(int argc, char** argv) {
  long int step = atoi(argv[1]);
  long int i = step << 32;
  long int end = (step+1) << 32;
  while(i < end) {
    seed = i;
    if(nextLong() == nextLong()) {
      printf("Found seed %ld\n", i);
      return 0;
    }
    ++i;
  }
  printf("No seed in %ld\n", step);
  return 1;
}

然后

echo {0..65535} | xargs -n 1 -P 12 ./executable

答案 2 :(得分:2)

这取决于你问的问题。

正如其他人所说:伪随机数生成器的标准公式在重复之前充分探索它们的价值空间。

但是:在大多数应用中,我们不使用PRNG的全部输出。我们通过将其划分或截断到与我们试图解决的问题相匹配的范围来减少它。事实上,我们这样做的大部分方式都会产生一系列数字,包括即时重复。

即使您只是在基础公式使用更多位进行计算时使用整数随机数,情况也是如此。

所以:理论上,如果你直接看PRNG的输出,答案是“可能不是”。实际上,答案是“不要指望它。”

答案 3 :(得分:1)

我不这么认为。我相信生成器使用下一个数字作为后续数字的种子。因此,如果您获得一次值,并且如果它重复,则您的数字生成器将陷入循环。

然而,许多应用程序正在寻找某个范围内的数字,这使得重复成为可能,因为后续数字可能具有与该范围相同的相同值。

编辑:由于您添加了next的源代码,您可以看到,如果它返回了相同的数字,它将始终返回相同的数字。因此,你会被困在一个值的循环中。