分享
 
 
 

GENERATING INTEGER RANDOM NUMBERS(幾種產生隨機數方法的效率分析)

王朝java/jsp·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

GENERATING INTEGER RANDOM NUMBERS

There are many ways to generate random numbers with the base libraries found in the Java 2 SDK, Standard Edition. If you haven't kept up to date with changes to the libraries, you might be using an inefficient mechanism or, possibly worse, getting results that are not uniformly distributed. Here's a look at a good way to generate integer random numbers, and then a look at what's not so good about some other ways.

The java.util.Random class has been available for generating random numbers since the initial release of the system libraries. Starting with the 1.2 release of the Java 2 SDK, Standard Edition, the Random class has a nextInt() method that accepts an integer argument:

public int nextInt(int n)

Given some value n, nextInt(n) returns a value greater than or equal to zero but less then that value: 0 <= nextInt(n) < n.

All you have to do is create a Random object first, then call nextInt(n) to return the next random int value.

Here's a demonstration. The following code snippet generates a large set of random numbers and prints out the average:

int count = 1000000;

int range = Integer.MAX_VALUE / 3 * 2;

double sum = 0;

Random rand = new Random();

for (int i=0; i<count; i++) {

sum += rand.nextInt(range);

}

System.out.println(sum/count);

After looping a million times, the average value should be approximately at the midpoint of the selected range.

So far, that isn't too complicated, but what's special about using nextInt(n)? Why is using nextInt(n) better than (1) using the older nextInt() method, with no range, then (2) using Math.abs() to get the absolute value, and then (3) using the mod (%) operator to get the value into the right range? The latter approach is demonstrated in the following code snippet. While there are a few extra operations in this approach, isn't it just as good as the first approach?

sum = 0;

for (int i=0; i<count; i++) {

sum += Math.abs(rand.nextInt()) % range;

}

System.out.println(sum/count);

There are actually three problems with the latter approach.

First, nextInt() is equally likely to return a value between Integer.MIN_VALUE and Integer.MAX_VALUE. If you take the absolute value of Integer.MIN_VALUE, the result is not a positive number. In fact, the result of Math.abs(Integer.MIN_VALUE) is Integer.MIN_VALUE. So, on rare occasions, you'll get back a negative number. Given the rarity of the event, 1 out of 2^31 times, the likelihood of the event happening during testing, and being repeatable, is highly unlikely.

Second, when you mod (%) the results of nextInt(), you effectively reduce the randomness of the results. The low order bits of random numbers can repeat more regularly than the entire number. This is a known issue with pseudo-random number generators, and so it's another reason not to use mod (%).

Finally, and possibly worst of all, the results are not evenly distributed. If you execute the loops in the two approaches, the first loop will produce results above 715 million, with a midpoint of the range at 715,827,882. That is within an acceptable tolerance for randomness. Surprisingly, the results of the second loop are consistently less than 600,000,000.

How can the second loop be so far off base? Essentially, the problem is that the mapping is unfair. When you use the mod (%) operator, you are taking values that are too large and squeezing them into the low end. This favors the lesser values. Compare this to rolling a single die with three other friends. Because there are only four of you and six possible values, the mapping for the first four values is easy. Die value 1 is mapped to person 1, value 2 to person 2, and so on. Now what about the larger values, 5 and 6? If you take the same approach as the mod (%) operator, you map the large values in such a way that any time a 5 is rolled, person 1 wins, and any time a 6 is rolled, person 2 wins. Is this fair? Well, that's what happens when you mod (%) the results of nextInt().

Using the nextInt(range) method solves all three of these problems.

That leaves the random() method of the Math class as another possible way to generate random numbers. Using that method can't be that bad, can it? You just have to multiply the result by the range:

sum = 0;

for (int i=0; i<count; i++) {

sum += (int)(Math.random() * range);

}

System.out.println(sum/count);

Well, using this method at least doesn't have any of the problems of nextInt(). You can't get a negative number back, you are not using the mod (%) operator so you don't run into the low-order byte range problem. And the range is uniform.

What is the problem, though, is that Math.random() uses floating point arithmetic, and both versions of nextInt() work with only integer operations. Math.random() can be up to four times slower because of its use of floating point operations. Throw in the cast, and the operation is even slower.

Because it avoids the problems inherent in using the other approaches, using the nextInt(range) method of Random is a better way to generate integer random numbers.

Here's a complete program you can use to test the different approaches discussed in this tip.

import java.util.*;

import java.text.*;

public class RandomTest {

public static void main(String args[]) {

NumberFormat nf = NumberFormat.getInstance();

int count = 1000000;

int range = Integer.MAX_VALUE / 3 * 2;

System.out.println("Midpoint: " +

nf.format(range/2));

double sum = 0;

Random rand = new Random();

for (int i=0; i<count; i++) {

sum += rand.nextInt(range);

}

System.out.println("Good : " +

nf.format(sum/count));

sum = 0;

for (int i=0; i<count; i++) {

sum += Math.abs(rand.nextInt()) % range;

}

System.out.println("Bad : " +

nf.format(sum/count));

sum = 0;

for (int i=0; i<count; i++) {

sum += (int)(Math.random() * range);

}

System.out.println("Longer : " +

nf.format(sum/count));

}

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有