使用连续数字设定java.util.Random种子

使用连续数字设定java.util.Random种子,java,random,Java,Random,我将遇到的一个bug简化为以下代码行: int[] vals = new int[8]; for (int i = 0; i < 1500; i++) vals[new Random(i).nextInt(8)]++; System.out.println(Arrays.toString(vals)); int[]vals=newint[8]; 对于(int i=0;i

我将遇到的一个bug简化为以下代码行:

    int[] vals = new int[8];
    for (int i = 0; i < 1500; i++)
        vals[new Random(i).nextInt(8)]++;
    System.out.println(Arrays.toString(vals));
int[]vals=newint[8];
对于(int i=0;i<1500;i++)
VAL[新随机(i).nextInt(8)]+;
System.out.println(Arrays.toString(vals));
输出为:[0,0,0,0,01310190,0]

这仅仅是一个选择连续数字作为随机种子,然后使用2次方的nextInt的人工制品吗?如果是这样的话,我是否应该意识到其他类似的陷阱,如果不是,我做错了什么?(我不是在寻找上述问题的解决方案,只是对其他可能出现的问题有所了解)


丹,写得很好的分析。由于javadoc非常明确地说明了数字是如何计算的,所以发生这种情况的原因并不神秘,就像有其他类似的异常需要注意一样——我没有看到任何关于连续种子的文档,我希望具有java.util.Random经验的人能够指出其他常见的陷阱


至于代码,需要几个并行代理具有可重复的随机行为,这些代理恰好从enum 8元素中选择作为第一步。一旦我发现了这种行为,种子都来自一个由已知种子创建的主随机对象。在程序的前一个(顺序种子)版本中,在第一次调用nextInt之后,所有行为都很快发生了分歧,因此我花了相当长的时间才将程序的行为缩小到RNG库,我希望在将来避免这种情况。

RNG的种子本身应该是随机的。你正在使用的种子只会在一到两位上有所不同

很少有好的理由在一个程序中创建两个单独的RNG。您的代码不是有意义的情况之一

只要创建一个RNG并重用它,就不会有这个问题

回应来自的评论:

你知道java.util.Random吗 这足以解释为什么它选择5 在这种情况下是6

答案在java.util.Random的源代码中,它是一个。在构造函数中指定种子时,将按如下方式对其进行操作

seed = (seed ^ 0x5DEECE66DL) & mask;
其中掩码仅保留较低的48位并丢弃其他位

在生成实际随机位时,此种子按如下方式操作:

randomBits = (seed * 0x5DEECE66DL + 0xBL) & mask;

现在,如果你认为所使用的种子是顺序的(0—1499),然后使用一次,然后丢弃,前四个种子产生以下四组随机位:

101110110010000010110100011000000000101001110100
101110110001101011010101011100110010010000000111
101110110010110001110010001110011101011101001110
101110110010011010010011010011001111000011100001
请注意,在每种情况下,前10位都是相同的。这是一个问题,因为他只想生成0-7范围内的值(只需要几个位),而RNG实现通过将高位向右移位并丢弃低位来实现这一点。这是因为在一般情况下,高位比低位更随机。在这种情况下,它们不是因为种子数据很差

最后,要查看这些位如何转换为我们得到的十进制值,您需要知道java.util.Random在n是2的幂时有一个特例。它请求31个随机位(上面48位中的前31位),将该值乘以n,然后向右移动31位

乘以8(本例中的n值)与向左移动3个位置相同。因此,这个过程的最终效果是将31位28位向右移位。在上述4个示例中的每一个示例中,这将保留位模式101(或十进制中的5)

如果我们不在一个值之后丢弃RNG,我们会看到序列发散。虽然上述四个序列都以5开头,但每个序列的第二个值分别为6、0、2和4。最初种子的微小差异开始产生影响

在回答更新的问题时:是线程安全的,您可以跨多个线程共享一个实例,因此仍然不需要有多个实例。如果您确实必须有多个RNG实例,请确保它们完全独立于彼此播种,否则您无法相信输出是独立的

至于为什么会得到这种效果,java.util.Random不是最好的RNG。它很简单,速度很快,如果你不仔细看的话,它是相当随机的。但是,如果您在其输出上运行一些,您将看到它有缺陷。你可以从视觉上看到这一点

如果需要更随机的RNG,可以使用。速度稍微慢一点,但工作正常。但有一件事对你来说可能是个问题,那就是它是不可重复的。具有相同种子的两个SecureRandom实例不会给出相同的输出。这是故意的

那么还有什么其他选择呢?这是我的插头。它包括3个可重复的伪RNG,它们比SecureRandom快,比java.util.random更随机。它们不是我发明的,我只是从最初的C版本移植过来的。它们都是线程安全的


我实现这些RNG是因为我需要更好的。与我最初的简短回答一致,这段代码是多线程的,但它只使用一个RNG实例,在所有线程之间共享。

反复播种生成器有什么用?同意jjnguy。为什么不在循环外执行Random r=new Random(x),然后反复调用r.nextInt()?看起来你正在做一个数字0-7出现频率的柱状图,你希望它相对相等……只要使用r=new Random(),我相信它将从当前时间开始播种。这是真的,但现在他让我很好奇。我知道你和RNGs一起工作过;您是否对java.util.Random非常了解,足以解释为什么在本例中选择5和6?