java Random() 如何找到种子?

par*_*983 3 java random random-seed

从我的研究来看,每个人似乎都说 java random 在未指定时使用系统时间(以毫秒为单位)作为其默认种子。因此,如果我们在生成随机数的那一刻有系统时间,我们应该能够知道是什么种子生成了该数字。所以,

(new Random()).nextLong;
long time = System.currentTimeMillis();
(new Random(time)).nextLong
Run Code Online (Sandbox Code Playgroud)

应该产生两个相同的数字,因为种子是相同的,对吗?事实并非如此,要么它不使用 TimeMillis 作为种子,要么我做错了其他事情。

非常感谢您的帮助,我已经搜索了几个小时,但似乎找不到一致的答案。我只想确切地知道当没有指定种子时它是如何找到种子的。我的想法是也许它确实使用了系统时间,但在进入最终种子之前它会乘以它等等。

再次感谢 :)

Jon*_*oni 8

Java曾经使用系统时间作为默认种子,最高到1.4.2。在 1.5 中,这个“错误”被修复了。比较1.4.2 API 规范

创建一个新的随机数生成器。它的种子被初始化为基于当前时间的值:

public Random() { this(System.currentTimeMillis()); }
Run Code Online (Sandbox Code Playgroud)

在同一毫秒内创建的两个 Random 对象将具有相同的随机数序列。

使用1.5 API 规范

创建一个新的随机数生成器。此构造函数将随机数生成器的种子设置为一个很可能与此构造函数的任何其他调用不同的值。

OpenJDK 中的当前实现使用静态AtomicLong,每次创建Random没有种子的新实例时都会更新该静态。如果本地没有源代码,可以在github上源代码中找到:

    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 * 1181783497276652981L;
            if (seedUniquifier.compareAndSet(current, next))
                return next;
        }
    }

    private static final AtomicLong seedUniquifier
        = new AtomicLong(8682522807148012L);
Run Code Online (Sandbox Code Playgroud)

  • 对于感兴趣的未来读者:Python 使用 [Mersenne Twister](https://en.wikipedia.org/wiki/Mersenne_Twister) 作为核心生成器。[此处](http://people.cs.pitt.edu/~jsa8/math_project.pdf) 是两者之间的有趣比较。 (2认同)