Java 种子

Java 种子

拭心

Java 种子
于 2016-10-30 10:58:51 发布
Java 种子
16358
Java 种子
收藏 12

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

Random 通常用来作为随机数生成器,它有两个构造方法:

        Random random = new Random();
        Random random2 = new Random(50);

1.不含参构造方法:

public Random() {
    setSeed(System.nanoTime() + seedBase);
    ++seedBase;
}

2.含参构造方法:

public Random(long seed) {
    setSeed(seed);
}

都调用的 setSeed 方法:

public synchronized void setSeed(long seed) {
    this.seed = (seed ^ multiplier) & ((1L << 48) - 1);
    haveNextNextGaussian = false;
}

可以看到,不含参构造方法每次都使用当前时间作为种子,而含参构造方法是以一个固定值作为种子

什么是种子 seed 呢?

seed 是 Random 生成随机数时使用的参数:

Random 中最重要的就是 next(int) 方法,使用 seed 进行计算:

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

其他 nextXXX 方法都是调用的 next()。

比如 nextInt(int):

public int nextInt(int n) {
    if (n <= 0) {
        throw new IllegalArgumentException("n <= 0: " + n);
    }
    if ((n & -n) == n) {
        //调用 next()
        return (int) ((n * (long) next(31)) >> 31);
    }
    int bits, val;
    do {
        bits = next(31);
        val = bits % n;
    } while (bits - val + (n - 1) < 0);
    return val;
}

再比如 nextBoolean():

//也是调用的 next()
public boolean nextBoolean() {
    return next(1) != 0;
}

举个栗子:

@Test
public void testRandomParameter(){
    System.out.println("Random 不含参构造方法:");
    for (int i = 0; i < 5; i++) {
        Random random = new Random();
        for (int j = 0; j < 8; j++) {
            System.out.print(" " + random.nextInt(100) + ", ");
        }

        System.out.println("");
    }

    System.out.println("");

    System.out.println("Random 含参构造方法:");
    for (int i = 0; i < 5; i++) {
        Random random = new Random(50);
        for (int j = 0; j < 8; j++) {
            System.out.print(" " + random.nextInt(100) + ", ");
        }
        System.out.println("");
    }
}

分别用含参构造方法和不含参构造方法创建 5 个随机生成器对象,每个随机生成器再生产 8 个随机数,对比下结果:

再运行一次:

总结:

通过上述例子可以发现:

随机数是种子经过计算生成的

  • 不含参的构造函数每次都使用当前时间作为种子,随机性更强
  • 而含参的构造函数其实是伪随机,更有可预见性

Java 种子

转载请注明出处——https://blog.csdn.net/chy555chy

一、概述
(1)java.util.Random.Random()

public Random() {
    this(seedUniquifier() ^ System.nanoTime());
}
Creates a new random number generator. This constructor sets the seed of the random number generator to a value very likely to be distinct from any other invocation of this constructor.

创建一个新的随机数生成器。该构造函数设置随机数种子的方式非常不同于其他的构造函数。

(2)java.util.Random.Random(long seed)

Creates a new random number generator using a single long seed. The seed is the initial value of the internal state of the pseudorandom number generator which is maintained by method next. 

使用一个随机数种子,创建一个新的随机数生成器。该种子是由next函数维护的伪随机数生成器的内部状态的初始值。

(3)int java.util.Random.nextInt(int bound)

Returns a pseudorandom, uniformly distributed int value between 0 (inclusive) and the specified value (exclusive), drawn from this random number generator's sequence. The general contract of nextInt is that one int value in the specified range is pseudorandomly generated and returned. All bound possible int values are produced with (approximately) equal probability. 

返回一个从随机数生成序列提取的均匀分布在0(包括)和bound指定的值(不包括)的伪随机数。nextInt函数约定了在指定范围内的一个整数被伪随机的产生并返回。所有区域范围内的值都有一个(大致)相同的生成概率。

二、代码实战

package com.demo.test;

import java.util.Random;

public class RandomDemo {

    public static void main(String[] args) {
        int bound = 10;
        Random random = new Random();
        for(int i=0;i<10;i++) {
            int value = random.nextInt(bound);
            System.out.println(value+"");
        }
        System.out.println("====================");

        //不用currentTimeMillis的原因是:当多线程调用时,由于CPU速率很快,因此currentTimeMillis很可能相等,使得随机数结果也会相等
//      long seed1 = System.currentTimeMillis();
//nanoTime()返回最准确的可用系统计时器的当前值,以毫微秒为单位。此方法只能用于测量已过的时间,与系统或钟表时间的其他任何时间概念无关。
        long seed1 = System.nanoTime();
        Random seedRandom1 = new Random(seed1);
        for(int i=0;i<10;i++) {
            /*
             产生一个[0,bound)之间的随机数
             设a<b, 要产生一个[a,b)之间的随机数的公式为:
             int value = a + random.nextInt(b-a);
             */
            int value = seedRandom1.nextInt(bound);
            System.out.println(value+"");
        }
        System.out.println("====================");

        long seed2 = 10;
        //当种子一样的时候,虽然每次nextXXX的方法会返回不同的结果,但是由于每次new Random(相同的seed)创建的“随机数生成器”都相同,因此之后产生的随机数序列也就都是一样的。所以每次调用该函数生成随机数都会产生相同的结果。
        Random seedRandom2 = new Random(seed2);
        for(int i=0;i<10;i++) {
            int value = seedRandom2.nextInt(bound);
            System.out.println(value+"");
        }
    }

}

三、运行截图(这里我运行了3次)

Java 种子

从运行结果可以看出当“随机数种子”相同的时候,每次调用该函数产生的结果都一样,所以在实际运行环境中,我们应该避免使用相同的随机数种子,常用的方法就是使用System.nanoTime来为作为种子。