20150701 星期三 北京
如果希望产生位于0到某个上届之间的随机整数,如何处理?
privatestatic final Random rnd = new Random();
staticint random (int n) {
returnMath.abs(rnd.nextInt()) % n;
}
存在的错误不容易发觉,可以运行,但是运行的原理导致问题.
1.如果n是比较小的2的乘方,经过比较短的周期,随机数序列会重复;
2.如果n不是2的乘方,某些数会比其他数字出现更加频繁.如果n比较大,缺点更加明显.
可以通过下面的程序体现出来
private static final Random rnd = new Random();
static int random (int n) {
returnMath.abs(rnd.nextInt()) % n;
}
public static void main(String[] args) {
intn = 2 * (Integer.MAX_VALUE / 3);
intlow = 0;
for(int i = 0; i < 1000000; i++) {
if(random(n)< n/2) {
low++;
}
}
System.err.println(low);//666575
}
代码会产生1百万个指定范围的随机数,并打印出来多少数字落在取值范围的前半部分.
预期的结果是打印的数字接近50万,实际结果是666575,由random方法产生的数字2/3落在随机数取值的前半部分.
3.极少情况,结果是灾难性的,返回落在指定范围之外的数字.因为方法调用Math.abs(),将rnd.nextInt()的返回值取绝对值int,如果nextInt()方法返回Integer.MIN_VALUE,那么Math.abs()会返回Integer.MIN_VALUE.确实如此.
System.err.println(Math.abs(Integer.MIN_VALUE));//-2147483648
如果n不是2的次方,那么取模运算返回的是负数,并且很难复现.
System.err.println(Math.abs(Integer.MIN_VALUE) %3); //-2
如何修正这3个问题呢?
有必要了解伪随机数,数论,2的求补运算.但是现有API,Random.nextInt(int)解决问题.
通过使用标准类库,可以充分发挥标准类库专家的知识,以及在你之前其他人的使用经验.