从程序可以看到,只要seed确定,那么nextseed也确定,那整个序列都可以被重建出来。故如果对于随机的情景,如果攻击者获取了初的种子seed,那么他将可以轻易模拟出随机数,并得到下一个seed。或者他通过穷举seed来获取计算的随机数来匹配程序的随机数。

  所以对安全性有要求的随机数应用情景,可以用java.security.SecureRandom。代替伪随机的Random类。该类继承自Random类,并覆盖了next(n)函数,所以可以利用其提供的强随机的种子算法(SHA1PRNG)来生成随机数。

  效率上肯定有损失,大概相差1个数量级。


    static int r3(int n) {
     final int offset = 123456;  // offset为固定值,避免被猜到种子来源(和密码学中的加salt有点类似)
     long seed = System.currentTimeMillis() + offset;
     SecureRandom secureRandom1;
  try {
   secureRandom1 = SecureRandom.getInstance("SHA1PRNG");
 
     secureRandom1.setSeed(seed);
     return secureRandom1.nextInt();
  } catch (NoSuchAlgorithmException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return 0;
    }
  四、附录

  a、nextInt(n)函数解析

  该函数进行的工作是把31位的原始随机范围next(31)的结果映射到[0,n)范围之内。但是,如果不经过特殊处理会出现概率不均匀。考虑下面这么一个例子,设原有均匀的随机数范围是1~100,当我要产生新的随机数范围为1~30时候,我本来可以用原随机范围产生的数分为三组,但是因为100不被30整除,所以余下第四组是不匀称的:[1,30), [31, 60), [61, 90), [91,100),所以实际产生的结果中,产生1~10的随机数概率会比11~30的要高。jdk对这种情况的做法是,如果对31bit的随机数映射到[0,n)的时候,如果next(31)产生的数字是后那部分,则丢弃重试。所以nextInt(n)的循环部分是处理这个问题。

  当n是2的整数次幂时,n铁定能被2^31整除,这时候可以直接映射,进行一次next(31)运算即可。

  当n不是2的整数次幂是,那会出现刚才例子中的不均匀情况。所以要特殊处理:当bits - val + (n-1) < 0 时,判断为不均匀部分,继续循环重试。那这句判断是什么意思呢。

  对于 2^31 / n = max ...val ,val是2^31除以n的余数, max是倍数,2^31-val=nMax 也是获取不大于2^31的能整除n的大整数。则[nMax,2^31)是不均匀部分。如果bits落入这个范围可判为丢弃并重试。这里我们可以获得:nMax < 2^31 < n(Max+1)

  当调用bits = next(31)时候,获取的是[0,2^31)的一个随机数,

  假如bits<nMax,则bits - val一定等于n的某个的倍数(小于Max)中,且bits-val+n-1一定小于nMax,故不需重试,直接返回结果。

  假如bits>=nMax,则bits-val = nMax, 则bits-val+n-1=nMax+n-1=n(Max+1) - 1 > 2^31 -1 ,等价于若bits>=nMax,则bits-val+n-1 >= 2^31 ,由于int型2^31溢出,所以2^31<0,

  所以用while(bits-val+n-1 < 0) 来判断是否需要重试。

  b、nextInt(n)的的循环次数为什么平均小于2

  附录a介绍了nextInt(n)的原理,了解到产生不均匀数后需要重试。但这个重试次数是多少呢。考虑坏情况,如nextInt(n)的javadoc中所说,当n=2^30+1时,糟糕,[nMax,2^31)的范围达到大。要使因为如果n<2^30+1更小,那么一定能找到newMax使得[nNewMax, 2^31) 的范围更小。

  当n=2^30+1,则不均匀范围[2^30+1, 2^31)和均匀范围[0, 2^30)的数目基本是一样的,所以这个时候有50%的机会要重试。即坏情况下也只有50%的概率重试。

  所以总体上来说nextInt(n)的平均调用next(n)次数小于2,这也是为什么比Math.random()要快的原因。