解密随机数生成器

翻译 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播放器的随机播放功能是如何实现的,今天读到一篇关于随机数的文章,又勾起了我的那时好奇心,索性上下求索,了解了随机数背后的很多知识,顿觉豁然开朗,特意写这篇文章和大家总结分享一下...
  • lihui126
  • lihui126
  • 2015年05月29日 14:53
  • 5391

[NOI2014]随机数生成器(模拟+贪心)

【题解】 先一步步按题目的操得出序列  然后在方阵中找出最小的数(对于初始方阵是1)的位置(x,y) 再在矩阵(1,1)-(x,y),(x,y)-(n,m)中分别找出最小数的位置  递归是...
  • cjk_cjk
  • cjk_cjk
  • 2015年07月14日 11:42
  • 947

随机数及其生成器

伪随机数与伪随机数生成器 计算机是确定性的机器,因此它无法直接生成真正的随机数,而浑沌系统的随机数生成速度又比较慢,在许多情况下不适合作为快速的(伪)随机数库函数算法。快速的伪随机数生成算法中最...
  • herorenme
  • herorenme
  • 2013年12月13日 11:33
  • 2061

BZOJ3122 && XOJ17 [Sdoi2013]随机数生成器

exgcd+BSGS
  • Monster__Yi
  • Monster__Yi
  • 2016年06月19日 21:23
  • 248

java加密的强随机数生成器

java.security.SecureRandom 所有已实现的接口:Serializable public class SecureRandom extends Random ...
  • rwdxll
  • rwdxll
  • 2014年06月03日 23:41
  • 2305

C++11带来的随机数生成器

点击打开链接 1. random_device   标准库提供了一个非确定性随机数生成设备.在Linux的实现中,是读取/dev/urandom设备;Windows的实现居然是用ran...
  • u011213163
  • u011213163
  • 2017年06月21日 16:14
  • 127

NOI2014 随机数生成器

3757. 【NOI2014】随机数生成器 (Standard IO) Time Limits: 5000 ms  Memory Limits: 262144 KB      ...
  • razorjxt
  • razorjxt
  • 2014年08月10日 19:02
  • 205

BZOJ-3122-随机数生成器-SDOI2013-BSGS

描述分析 关键就是对式子的变形, 将原递推公式转化为通项公式, 中间会用到等比数列求和公式. 然后切记此时方程两边同乘(1-a), 然后就化简开了. 之后得到的式子是 a^(n-1) = ... (m...
  • qq_21110267
  • qq_21110267
  • 2015年03月20日 23:25
  • 1029

随机数发生器原理

随机数发生器的基本原理:下一个随机数 = 数学函数(前若干个随机数) 评价一个随机数发生器的好坏: 看起来是[0,1]之间的随机均匀分布运行快,内存消耗低可以重复,英文是reproduci...
  • LegenDavid
  • LegenDavid
  • 2017年03月30日 18:24
  • 835

3671: [Noi2014]随机数生成器

其实这题真的很水。。。。。。一眼就能看出来解法。 但是 但是 但是 坑~坑~坑~坑~坑~坑啊。 首先模拟一遍出变换后的排列,时间O(N*M+Q),扫一遍排列,用x[i],y[i]记录排列中值...
  • nlj1999
  • nlj1999
  • 2015年12月17日 14:59
  • 262
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:解密随机数生成器
举报原因:
原因补充:

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