解密随机数生成器

翻译 2017年01月03日 10:12:34

前短时间写了个公司年会抽奖的程序,不少人反应有的人连续几年中奖,于是怀疑程序的问题,今天特地抽出点时间翻看了下java的源码,解读下随机函数的生成算法。

在Eclipse中输入java.util.Random,按F3转到Random类的源代码:

首先,我们看到这样一段说明:

/**
 * An instance of this class is used to generate a stream of
 * pseudorandom numbers. The class uses a 48-bit seed, which is
 * modified using a linear congruential formula. (See Donald Knuth,
 * <i>The Art of Computer Programming, Volume 2</i>, Section 3.2.1.)
 * <p>


翻译过来是:

这个类的一个实现是用来生成一串伪随机数。这个类用了一个48位的种子,被线性同余公式修改用来生成随机数。

显然,java的Random类使用的是线性同余法来得到随机数的。

接着往下看,我们找到了它的构造函数与几个方法,里面包含了获得48位种子的过程:


/**
     * 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);

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

这里先使用System.nanoTime()获取一个纳秒级的时间量,参与48位种子的构成,这里使用了System.nanoTime()方法来得到一个纳秒级的时间量,参与48位种子的构成,然后还进行了一个很变态的运算——不断乘以181783497276652981L,直到某一次相乘前后结果相同——来进一步增大随机性。、

好了,现在我不得不佩服这位工程师的变态了:到目前为止,这个程序已经至少进行了三次随机:

 

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

 

2、不断与一个变态的数——181783497276652981L相乘(天知道这些数是不是工程师随便滚键盘滚出来的-.-)直到某一次相乘前后数值相等

 

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

接下来利用了线性同余算法

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

    线性同余法是一个很古老的随机数生成算法,它的数学形式如下:

Xn+1 = (a*Xn+c)(mod m) 

其中,

m>0,0<a<m,0<c<m

 

这里Xn这个序列生成一系列的随机数,X0是种子。随机数产生的质量与m,a,c三个参数的选取有很大关系。这些随机数并不是真正的随机,而是满足在某一周期内随机分布,这个周期的最长为m。根据Hull-Dobell Theorem,当且仅当:

1. c和m互素;

 

2. a-1可被所有m的质因数整除;

 

3. 当m是4的整数倍,a-1也是4的整数倍

 

时,周期为m。所以m一般都设置的很大,以延长周期。



 private static long initialScramble(long seed) {
        return (seed ^ multiplier) & mask;
    


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


对比数学公式,a = 0x5DEECE66DL, c = 0xBL, m = 2^48,就可以得到一个48位的随机数,而且这个谨慎的工程师进行了迭代,增加结果的随机性。再把结果移位,就可以得到指定位数的随机数。



随机算法中,最核心的地方就在于使用static变量AtomicLong来记录每次调用Random构造函数时使用的种子,下次再调用Random构造函数的时候避免和上次一样。


这里有点晦涩难懂,不多说了,请参见解密随机数生成器(2)——从java源码看线性同余算法



最后将随机数打印出来:0.47730637287039723
0.8024508793535536
0.5481799061191626
0.27565830804489333
0.047160003035978115
0.7035259724009703
0.6550010669273417
0.6482388034909883
0.0576209291980867
0.07069050337047089
0.9336952730245858
0.13472579417197172
0.10999371780411427
0.38668647801543354
0.994986602594389
0.37053761228696236
0.4269821582971406
0.30965439072331447
0.26730127990451324
0.15780248921270124
0.4874277631424623
0.43866923824434134
0.8379735008094493
0.46585350668133274
0.03358481257929935
0.8894735657161117
0.46912683775771524
0.9431080765561418
0.9167830882120481
0.17286940293254904
0.19988344534144653

... ...


其结果是[0,1) 中的随机分布值





                    

解密随机数生成器

从小就一直很好奇,MP3播放器的随机播放功能是如何实现的,今天读到一篇关于随机数的文章,又勾起了我的那时好奇心,索性上下求索,了解了随机数背后的很多知识,顿觉豁然开朗,特意写这篇文章和大家总结分享一下...

随机数生成器

  • 2016年08月04日 16:48
  • 6KB
  • 下载

Linux随机数生成器的原理及缺陷

  • 2013年05月22日 16:53
  • 275KB
  • 下载

NOI 2012 随机数据生成器 random

还有20天就NOI了。。 赶紧做真题= -=  很简单= -=  矩阵乘法乱搞可以85分 然后剩下的5分因为爆int64 可以用快速乘 就是a*b写成b个a连加的形式 这个我们可以类比快...
  • trigon
  • trigon
  • 2013年06月25日 14:29
  • 850

随机数生成器

  • 2014年09月14日 15:14
  • 819KB
  • 下载

随机数生成器

  • 2015年11月30日 13:48
  • 193KB
  • 下载

【JZOJ 3366】随机数生成器

Description栋栋最近迷上了随机算法,而随机数生成是随机算法的基础。栋栋准备使用线性同余法(Linear Congruential Method)来生成一个随机数列,这种方法需要设置四个非负整...
  • HOWARLI
  • HOWARLI
  • 2017年02月09日 21:19
  • 384

随机数生成器源码

  • 2013年11月14日 23:05
  • 46.23MB
  • 下载

随机数生成器

  • 2013年11月09日 22:25
  • 354KB
  • 下载

bzoj3122【SDOI2013】随机数生成器

扩展欧几里得+BSGS
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:解密随机数生成器
举报原因:
原因补充:

(最多只允许输入30个字)