Java Random类源码分析

来源:互联网 发布:2016年进出口数据分析 编辑:程序博客网 时间:2024/05/19 13:28

前言

每次特别好奇计算机如何产生随机数的,他这种完全按照规律执行的代码,如何才能产生随机数呢?这里我给出大家一个解答——答案是不可以。计算机产生的伪随机数。即给出一个种子,给出一些复杂的算法。把他变大之后,就产生了类似随机数的序列。

正文

下面我们来看一下代码

        Random random = new Random(50);        System.out.println(random.nextInt(100));        System.out.println(random.nextInt(100));        Random random1 = new Random(50);        System.out.println(random1.nextInt(100));        System.out.println(random1.nextInt(100));

运行结果是:

17
88
17
88

看呆了吧,这玩意完全就类似一个数组,如果你不信,你可以多输出几次,下面我们来看下源码。分析下到底他是一个什么样子的数组。并且如何才能产生我们需要的“伪随机数”。
首先观察构造函数,其实就时找到一个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);        }    }

这里本来我不想贴代码的。可是我发现无参构造函数比较复杂。我就还是研究下吧,注释很容易明白,一切的一切都是为了继承。其实我们只走if(true)流程。我们看下initialScramble(seed)

    private static final long mask = (1L << 48) - 1;    private static final long multiplier = 0x5DEECE66DL;    private static long initialScramble(long seed) {        return (seed ^ multiplier) & mask;    }

这里估计大部分人会迷茫,这他妹的是什么。说实在的我也不懂。multiplier 这个数据为啥是这个。有撒用,貌似一个最合理的解释的是吧数字变大,可以让种子变得更加貌似随机一点。至于mask,貌似要去掉正负标志位,其实没啥用,继续来研究另外一个构造函数。

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

seedUniquifier() 这个函数我不懂,网上搜到说是多线程。大家不要介意。只要知道这个数字貌似比较大,并且跟当前时间xor后更显得随机,好了开始我们真正的随机数的产生。

    public boolean nextBoolean() {        return next(1) != 0;    }

这个最简单。我们会发现出现一个next函数,看下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 = a* oldseed + b ;
这个东西。我们在getBool 中传递参数是1.我们很容易发现我们只保留了一位二进制数字。当然只能取两种结果。
好了我们来看一个复杂的东东

    public int nextInt(int bound) {        if (bound <= 0)            throw new IllegalArgumentException(BadBound);        int r = next(31);        int m = bound - 1;        if ((bound & m) == 0)  // i.e., bound is a power of 2            r = (int)((bound * (long)r) >> 31);        else {            for (int u = r; u - (r = u % bound) + m < 0; u = next(31))                ;        }        return r;    }

第四行r为一个无符号的int型’随机数’。if内部解释很清楚,直接看else。整理后的代码是这样,其实就是一直取余数,最终得带的余数小鱼边界就好了。不在详细研究。
至于小数的形式。这里不在研究,也比较简单。
下面说下如果想产生我们认为的足够随机的随机数。其实很简单。聪明的我们发现调用无参构造函数就好了嘛!的确,这里我就不再给大家写代码。另外还有一个java.math.random() 类对这个类进行了封装我们可以调用Math.random() .关于线程我这里给不出一个合理的答案。至于我这个写Android的应用的一般不会出现这种问题。这里就不再深入研究了。以后有机会再补充。

后记

总算理解了大概这个玩意。这里总算明白这个问题了,总算不是完全不直到到底随机数如何产生的。希望对大家有帮助

2 0
原创粉丝点击