使用Java的随机数种子时出现奇怪行为

4
以下代码应该创建两个具有相同种子的随机对象:
System.out.println("System time before: " + System.currentTimeMillis());
Random r1 = new Random();
Random r2 = new Random(System.currentTimeMillis());
System.out.println("System time after: " + System.currentTimeMillis());

System.out.println("r1: " + r1.nextInt());
System.out.println("r2: " + r2.nextInt());

种子应该是相同的,因为在创建这两个对象之前和之后,System.currentTimeMillis() 没有发生变化,如下所示:
System time before: 1331889186449
System time after: 1331889186449
r1: -1836225474
r2: 2070673752

从文档中可以看出,不带任何参数的构造函数是这样的:

public Random() { this(System.currentTimeMillis()); }

那么问题出在哪里呢?有人能解释一下为什么这两个生成器返回不同的输出,而它们应该具有相同的种子吗?

在大多数集成开发环境中,您可以使用 <ctrl>+<click> 来查看方法的源代码。 - Peter Lawrey
这并不保证是正确的,因为System.currentTimeMillis()在创建这两个对象前后并没有改变。 - Jesper
3个回答

7
如果您正在使用java.util.Random,我看到的是默认的无参构造函数 - 现在可能取决于您使用的JDK版本(此代码似乎至少用于sun JDK 6和7):
public Random() {
    this(seedUniquifier() ^ System.nanoTime());
}

private static long seedUniquifier() {
    // L'Ecuyer, "Tables of Linear Congruential Generators of
    // Different Sizes and Good Lattice Structure", 1999
    for (;;) {
        long current = seedUniquifier.get();
        long next = current * 181783497276652981L;
        if (seedUniquifier.compareAndSet(current, next))
            return next;
    }
}

为了确认,这里有一段代码可以检查种子是否相同:

public static void main(String args[]) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
    System.out.println("System time before: " + System.currentTimeMillis());
    Random r1 = new Random();
    Random r2 = new Random(System.currentTimeMillis());
    System.out.println("System time after: " + System.currentTimeMillis());

    Field seed = Random.class.getDeclaredField("seed");
    seed.setAccessible(true);
    AtomicLong seed1 = (AtomicLong) seed.get(r1);
    AtomicLong seed2 = (AtomicLong) seed.get(r2);

    System.out.println("seed1 = " + seed1);
    System.out.println("seed2 = " + seed2);
}

我的快速谷歌搜索找到了这个 - 你在哪里找到的? - Flash
@Andrew,那是来自Java 1.4.2的。 - artbristol
1
从中得出的教训是永远不要依赖类或方法的内部定义来进行自己的逻辑。 - Perception
教我不要使用我的IDE...所以Eclipse给出了public Random() { this(++seedUniquifier + System.nanoTime()); }这是@Roman在下面发的。我想知道为什么这与你的答案不同? - Flash
@Andrew 因为你可能在使用不同的JDK...他在Max OS X上,我在Windows上,不确定差异是什么。 - assylias

2

我认为默认构造函数并不像你所说的那样(即public Random { this(System.currentTimeMillis()); })。Java文档只是说它使用一个可能在每次调用时都不同的值来初始化类 这里。在我的实现(Mac OS X)中查看Random的头文件。

public Random() { this(++seedUniquifier + System.nanoTime()); }

0
两个生成器在应该具有相同种子时返回不同的输出?
是吗?在我看来,只有一个生成器获得了毫秒种子...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接