简介Random类

主要介绍

  1. Math.random( )方法
  2. Random类
  3. 随机的基本原理

1. Math.random( )方法

最基本的随机莫过于Math类中的random( )方法,它生成一个0~1之间的随机数,类型为double,包括0而不包括1。
那么,Math.random( )中的随机是如何实现的呢?

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

private static final class RandomNumberGeneratorHolder {
        static final Random randomNumberGenerator = new Random();

        private RandomNumberGeneratorHolder() {
        }
    }

可以看出,它内部其实是产生了一个Random类型的randomNumber-Generator。也就是说,调用Math中的random( )方法,实际上是调用了Random类的nextDouble( )方法。
那么Random类是怎么产生随机数的呢?

2. Random类

Random类提供了产生各种类型随机数的方法,但是由于其中的方法并非静态,所以想要使用这些方法,必须要创建一个Random实例。
如:

Random random = new Random();
random.nextInt();
random.nextInt(100);

其中,前者产生一个随机的int,可能为正数,也可能为负数
而后者产生一个介于0~100的正数,包括0,不包括100。
当然,除了nextInt( )方法,还有如下方法:

// 生成随机字节,并将其置于给出的字节数组
public void nextBytes(byte[] var1);
// 随机生成一个long
public long nextLong();
// 随机生成一个boolean
public boolean nextBoolean();
// 0~1范围内的随机浮点数,含0不含1
public float nextFloat();
// 0~1范围内的随机浮点数,含0不含1
public double nextDoble();

补充:
关于nextBytes方法
针对源代码进行分析,可得,每次先产生一个32位的int类型的数据(通过nextInt()方法),如果要产生的数组长度大于4,那就将其右移3次,即可得三个数字,填充数组,如果数组中数字数目不够就继续产生int类型的数据;反之,如果数组长度小于4,那么只需将int类型的数据右移小于等于3的次数,即可得数组。

再浅看一眼Random的构造方法

public Random() {
        this(seedUniquifier() ^ System.nanoTime());
    }
    
public Random(long var1) {
        this.haveNextNextGaussian = false;
        if (this.getClass() == Random.class) {
            this.seed = new AtomicLong(initialScramble(var1));
        } else {
            this.seed = new AtomicLong();
            this.setSeed(var1);
        }
    }

关于第一个构造方法,会在下面提到。
那么第二个构造方法,可以接受一个long类型的种子。
种子决定了随机产生的序列,种子相同,产生的随机数序列就相同。
那至于为什么要指定种子,当然是,为了随机的复现。

关于随机的原理,下面进行简单的介绍。

随机的原理

Random产生的随机数被称为伪随机数的原因是,那并不是真正意义上的随机数。
伪随机数都是基于一个种子数的,然后每需要一个随机数,就对当前的种子进行一些数学运算,得到一个属,基于这个数得到需要的随机数和新的种子。
因为数学运算是固定的式子,所以只要种子确定了,那么产生的随机数序列就是确定的,确定的随机数当然不算是真正的随机数。不过呢,种子不同,序列就不同,而且每个序列中的数字分布随机而均匀,故而称作是伪随机数。

public Random() {
        this(seedUniquifier() ^ System.nanoTime());
    }

    private static long seedUniquifier() {
        long var0;
        long var2;
        do {
            var0 = seedUniquifier.get();
            var2 = var0 * 181783497276652981L;
        } while(!seedUniquifier.compareAndSet(var0, var2));

        return var2;
    }

上即为Random类默认的无参构造方法,不难看出,在未给出种子的情况下,Random类会自动生成一个种子,而这个种子,相较而言,是随机的。
种子是seedUniquifier( ) 和System.nanoTime( )异或的结果,其中seedUniquifier( )是返回当前seedUniquifier和181783497276652981L这个long数据的乘积,然后CAS操作尝试设置旧值var0为更新后的值var2,其中CAS是为了保证在多线程情况下,不会产生两个相同的随机值,即确保随机性。

next( )方法

其实上述常用的产生随机数的方法,都有一个共同的特点,它们都无一例外地调用了next( )方法。

protected int next(int var1) {
        AtomicLong var6 = this.seed;

        long var2;
        long var4;
        do {
            var2 = var6.get();
            // 关键
            var4 = var2 * 25214903917L + 11L & 281474976710655L;
        } while(!var6.compareAndSet(var2, var4));

        return (int)(var4 >>> 48 - var1);
    }

实际上,该代码就是一个数学公式:
var4 = var2 * 25214903917L + 11L & 281474976710655L;
这个公式,称作线性同余随机数生成器(建议去看看《计算机程序设计艺术》,详细了解一下…)。
那,
总而言之,言而总之,随机数的基本原理就是,以所得随机数种子为基准,按照特定的数学公式,依次生成随机数序列。
或者说,随机数基于一个种子,种子固定,随机数序列就固定。

本人实在才疏学浅,本文就结束啦。如有错误,劳烦联系我哦。

3. 参考

《Java编程的逻辑》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值