Java如何生成随机数?

时间:2015-11-29 03:10:39

标签: java random

我知道您可以使用Java中的Math.random()生成从0.0到1.0的随机双精度,但计算机如何选择一个数字?计算机跟随模拟随机性的代码是什么?

4 个答案:

答案 0 :(得分:5)

通过查看文档很容易找到,因为很高兴,这种行为在Java中得到了很好的定义。来自Math, it says

  

首次调用此方法时,它会创建一个新的伪随机数生成器,就像表达式

一样      

new java.util.Random()

可悲的是,它没有明确说明它调用的Random上的哪种方法。但是,由于其返回值与Random.nextDouble的范围相同,因此它也会链接到"另请参阅"部分,应该是一个安全的假设,即使用的是什么。

查看Random.nextDouble, you can see

return (((long)next(26) << 27) + next(27))
  / (double)(1L << 53);

next is implemented as

  

以原子方式将种子更新为

     

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

     

并返回

     

(int)(seed >>> (48 - bits))

所以最终的随机值计算为

seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)
next1 = (int)(seed >>> (48 - 26))
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)
next2 = (int)(seed >>> (48 - 27))
randomValue = (((long)next1 << 27) + next2) / (double)(1L << 53);

其中

  

随机数生成器的种子[初始化为]一个非常可能与此构造函数的任何其他调用不同的值。

as stated in the Random docs

通过观察这一点,应该非常清楚的是,这个数字实际上并不是&#34;随机的&#34;因为大多数人会理解它。它是一种完全可预测的数学模式,称为pseudorandom,它可以用于多种目的。但是,可能值得研究SecureRandom(产生不可预测的,加密强的随机值),具体取决于您希望结果的不可预测性。

答案 1 :(得分:1)

关于随机数的真相是它们并非真正随机。它们只是用当前时间以mili / nano(甚至更少) - 秒来计算...因为时间总是变化而且永远不会重复这个值不能是相同的值,因为“相同的原因”是什么使得随机编号一个随机数...

查看@FranciscoSpaeth回答:

https://stackoverflow.com/a/11317929/3180983

答案 2 :(得分:0)

您可以在Java类声明中对其进行跟踪。这个来自Math.class:

public static double random() {
    return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}

在Eclipse中很容易完成。焦点&amp; F3。

答案 3 :(得分:0)

通过查看Java中Random类的源代码,您会发现这取决于是否使用种子-即调用public Random()或调用public Random(long seed)。

来自Java Random类的源代码:

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

您会看到,如果未指定种子,则Random类将使用AtomicLong种子,并使用System.nanoTime()执行按位XOR操作(^)

但是,如果您指定种子,则无需调用System.nanoTime()即可调用另一部分代码。

(请注意,如果您在没有种子的情况下进行调用,则使用种子的构造函数仍会被调用,但其种子包含System.nanoTime(),从而保证每次调用时都具有不同的种子)

public Random(long seed) {
    if (getClass() == Random.class)
        this.seed = new AtomicLong(initialScramble(seed));
    else {
        // subclass might have overriden setSeed
        this.seed = new AtomicLong();
        setSeed(seed);
    }
}

private static long initialScramble(long seed) {
    return (seed ^ multiplier) & mask;
}

首先将通过调用initialScramble(long seed)方法对种子进行加扰

使用nextBytes,nextLong,nextInt等从代码中进行调用。将调用next()。其实现如下:

 protected int next(int bits) {
    long oldseed, nextseed;
    AtomicLong seed = this.seed;
    do {
    oldseed = seed.get();
    nextseed = (oldseed * multiplier + addend) & mask;
    } while (!seed.compareAndSet(oldseed, nextseed));
    return (int)(nextseed >>> (48 - bits));
}

它基本上在行中包含“线性同余生成器”

nextseed = (oldseed * multiplier + addend) & mask;

您可以测试有无种子的呼叫之间的差异。如果提供种子并仅调用一次方法nextInt,nextBytes等,则每次将获得相同的值。 但是,如果不指定种子,则即使仅调用一次nextInt,nextBytes等方法,也会获得不同的值。