当前位置:   article > 正文

随机的原理及其应用_随机原理

随机原理

来源:Java编程的逻辑

1 Math.random

Java中,对随机最基本的支持是Math类中的静态方法random,它生成一个0到1的随机数,类型为double,包括0但不包括1

Math.random()实现相关代码:

private static Random randomNumberGenerator;

private static synchronized Random initRNG() {
   
    Random rnd = randomNumberGenerator;
    return (rnd == null) ? (randomNumberGenerator = new Random()) : rnd;
}

public static double random() {
   
    Random rnd = randomNumberGenerator;
    if (rnd == null) rnd = initRNG();
    return rnd.nextDouble();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

内部它使用了一个Random类型的静态变量randomNumberGenerator,调用random()就是调用该变量的nextDouble()方法,这个Random变量只有在第一次使用的时候才创建。

2 Random

2.1 设置种子

除了默认构造方法,Random类还有一个构造方法,可以接受一个long类型的种子参数:

private final AtomicLong seed;

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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

种子决定了随机产生的序列,种子相同,产生的随机数序列就是相同的。

除了在构造方法中指定种子,Random类还有一个setter实例方法:

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

synchronized public void setSeed(long seed) {
   
    this.seed.set(initialScramble(seed));
    haveNextNextGaussian = false;
}
private static long initialScramble(long seed) {
   
    return (seed ^ multiplier) & mask;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

其效果与在构造方法中指定种子是一样的。

指定种子的原因:为了实现可重复的随机。比如用于模拟测试程序中,模拟要求随机,但测试要求可重复。

2.2 随机的基本原理

Random产生的随机数不是真正的随机数,相反,它产生的随机数一般称之为伪随机数,真正的随机数比较难以产生,计算机程序中的随机数一般都是伪随机数。

伪随机数都是基于一个种子数的

过程:
每需要一个随机数,都是对当前种子进行一些数学运算,得到一个数,基于这个数得到需要的随机数和新的种子。

数学运算是固定的,所以种子确定后,产生的随机数序列就是确定的,确定的数字序列当然不是真正的随机数,但种子不同,序列就不同,每个序列中数字的分布也都是比较随机和均匀的,所以称之为伪随机数。

Random的默认构造方法中没有传递种子,它会自动生成一个种子,这个种子数是一个真正的随机数

生成种子数代码如下:

private static final AtomicLong seedUniquifier = new AtomicLong(8682522807148012L);
//每一次默认创建Random对象,旧种子都会重新计算    
public Random() {
   
    this(seedUniquifier() ^ System.nanoTime());
}

private static long seedUniquifier() {
   
    for (;;) {
   
        long current = seedUniquifier.get();
        long next = current * 181783497276652981L;
        if (seedUniquifier.compareAndSet(current, next))
            return next;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

种子是seedUniquifier() 与System.nanoTime()按位异或的结果;
System.nanoTime()返回一个更高精度(纳秒)的当前时间;
简单的说,就是返回当前seedUniquifier(current)与一个常数181783497276652981L相乘的结果(next),然后,将seedUniquifier设置为next,使用循环和compareAndSet都是为了确保在多线程的环境下不会有两次调用返回相同的值,保证随机性。

有了种子数之后,其他数是怎么生成的呢?我们来看一些代码:

public int nextInt() {
   
    return next(32);
  • 1
  • 2
  • 3
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号