Skip to content

Java随机数random seed

摘抄 Java:Random函数及其种子的作用 - 一只敲码的猫 - 博客园

伪随机(preundorandom):通过算法产生的随机数都是伪随机!!

只有通过真实的随机事件产生的随机数才是真随机!!比如,通过机器的硬件噪声产生随机数、通过大气噪声产生随机数**

Random生成的随机数都是伪随机数!!!

是由可确定的函数(常用线性同余),通过一个种子(常用时钟),产生的伪随机数。这意味着:如果知道了种子,或者已经产生的随机数,都可能获得接下来随机数序列的信息(可预测性)

Random类拥有两个构造方法,用于实现随机数生成器:

Random( )构造一个随机数生成器 、Random(long seed)用种子seed构造一个随机数生成器

一、无参构造方法(不设置种子)

虽然表面上看我们未设置种子,但 Random 构造方法里有一套自己的种子生成机制,源码如下:

java
/**
 * 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.
 */
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;
	}
}

private static final AtomicLong seedUniquifier
	= new AtomicLong(8682522807148012L);
/**
 * 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.
 */
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;
	}
}

private static final AtomicLong seedUniquifier
	= new AtomicLong(8682522807148012L);

生成种子过程:(参考解密随机数生成器(二)——从java源码看线性同余算法

1、获得一个长整形数作为“初始种子”(系统默认的是8682522807148012L)

2、不断与一个变态的数——181783497276652981L相乘(天知道这些数是不是工程师随便滚键盘滚出来的-.-)得到一个不能预测的值,直到 能把这个不能事先预期的值 赋给Random对象的静态常量seedUniquifier 。因为多线程环境下赋值操作可能失败,就for(;;)来保证一定要赋值成功

3、与系统随机出来的nanotime值作异或运算,得到最终的种子

nanotime算是一个随机性比较强的参数,用于描述代码的执行时间。源码中关于nanotime的描述(部分):

/**
     * Returns the current value of the running Java Virtual Machine's
     * high-resolution time source, in nanoseconds.
     *
     * <p>This method can only be used to measure elapsed time and is
     * not related to any other notion of system or wall-clock time.
/**
     * Returns the current value of the running Java Virtual Machine's
     * high-resolution time source, in nanoseconds.
     *
     * <p>This method can only be used to measure elapsed time and is
     * not related to any other notion of system or wall-clock time.

二、有参构造方法(设置种子)*

java
/**
 * Creates a new random number generator using a single {@code long} seed.
 * The seed is the initial value of the internal state of the pseudorandom
 * number generator which is maintained by method {@link #next}.
 *
 * <p>The invocation {@code new Random(seed)} is equivalent to:
 *  <pre> {@code
 * Random rnd = new Random();
 * rnd.setSeed(seed);}</pre>
 *
 * @param seed the initial seed
 * @see   #setSeed(long)
 */
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;
}
/**
 * Creates a new random number generator using a single {@code long} seed.
 * The seed is the initial value of the internal state of the pseudorandom
 * number generator which is maintained by method {@link #next}.
 *
 * <p>The invocation {@code new Random(seed)} is equivalent to:
 *  <pre> {@code
 * Random rnd = new Random();
 * rnd.setSeed(seed);}</pre>
 *
 * @param seed the initial seed
 * @see   #setSeed(long)
 */
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;
}

其中的multiplier和mask都是定值:

java
private static final long multiplier = 0x5DEECE66DL;

private static final long mask = (1L << 48) - 1;
private static final long multiplier = 0x5DEECE66DL;

private static final long mask = (1L << 48) - 1;

结论:

**虽然二者都是伪随机,但是,**无参数构造方法(不设置种子)具有更强的随机性,能够满足一般统计上的随机数要求。使用有参的构造方法(设置种子)无论你生成多少次,每次生成的随机序列都相同,名副其实的伪随机!!