Java 随机数比较和分析(二)

2014-11-24 08:59:20 · 作者: · 浏览: 1
[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

假如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()要快的原因。



作者:UltraNi