不难发现,每次循环都多出了几步运算。事实上,这种随机数生成的方法存在着以下三个问题:</p> 首先,nextInt()返回的值是趋于均匀分布在Integer.MIN_VALUE 和 Integer.MAX_VALUE之间的。如果你取Integer.MIN_VALUE的绝对值,得到的仍然不是一个正数。事实上,Math.abs(Integer.MIN_VALUE)等于Integer.MIN_VALUE。因此,存在着这样一种情况(虽然很少见):rand.nextInt()=Integer.MIN_VALUE,经过取绝对值Math.abs(rand.nextInt())之后,得到是一个负数。这种几率为 1/(2^31),在我们的测试中不太可能发生——循环次数只有1000000次。- s8 C( {9 `- N- n3 v- H( _
其次,当你对nextInt()取模时,你使结果的随机性大打折扣。随机数中较小的值出现的几率更大一些。这就是众所周知的伪随机数生成,因此我们不是用取模的方法。, G7 ]2 N; c3 |7 Q/ D% [- X' ]" s
最后,也可能是最糟糕的:随机数不是均匀分布。如果你执行了上述的两段代码,第一段代码的结果将会大于715,000,000,考虑到数值范围的中点(midpoint)是715,827,882,所以这是一个可以接受的结果。然而,你会吃惊的发现第二段代码得到的平均值肯定不会超过600,000,000。
, c$ E4 P& K6 R" h; v 为何第二段代码的结果会如此的偏差?纠其本质,问题出在数值分布的不均匀。当你进行取模运算时,你将过大的数转换成了较小的。这使得较小的数更容易产生。4 M2 \% Y% }/ t3 N: {* C" b. C$ M
使用nextInt(range)将会解决上述的三个问题。/ i3 [+ c, d- S6 j$ G) B, [
还有一种随机数生成方法——使用Math.random()。这个方法的效果如何?
0 \- n, R5 U& M& W- e& ^4 M/ T以下是引用片段:
0 `0 E4 u) s7 e sum = 0; - _0 ^( n" L0 C/ ~
for (int i=0; i 9 a+ I8 }3 T% f1 }
sum += (int)(Math.random() * range);
) ^+ y! m5 V! ^$ Z }
3 q. U; A: ^" `* t System.out.println(sum/count);
, T( j7 f: U. n( w* o9 T
4 h* ]! M5 X/ K6 m) _" w) P 很好,使用random()不会碰到nextInt()的麻烦。你不会得到负数返回值,没有使用取模运算,值分布也是均匀的。还有什么问题吗?你有没有考虑到Math.random()使用了浮点运算,而nextInt()和nextInt(range)只有整数操作?Math.random()可能会慢上四倍。再加上从浮点到整数的类型转换,整个运算将会更慢。
2 D+ `. r; b1 B; ^2 `) ^7 r6 _ 好了,经过一番比较,我们发现使用nextInt(range)生成随机数更为有效,因为它避免了其他方法的种种弊端。0 j0 @5 r& g& d( d1 `
最后再给出一段代码,通过测试可以比较本文提到的几种随机数生成方法。
$ ~# X* i9 J; U- k9 u% u以下是引用片段:
1 t4 C4 C ]. F; `. q import java.util.*;
4 F: X+ @& x+ X6 p0 D1 M- ~ import java.text.*;
c& D) r2 Z' D) d1 D8 z3 L! D public class RandomTest {
) R( U, g( R G public static void main(String args[]) {
/ `+ j7 H- M" Q( @1 b7 K o% l NumberFormat nf = NumberFormat.getInstance(); ( J, W" Y c+ ^& V1 W! t5 Z
int count = 1000000;
. b; ]1 [8 x2 o2 ` int range = Integer.MAX_VALUE / 3 * 2;
/ Z9 r4 e. j& h* I( q( i System.out.println("Midpoint: " + nf.format(range/2));
' R6 }% M6 V; J( H. G double sum = 0;
# V5 r' m. F2 |5 v Random rand = new Random();
5 m3 W% s3 t& [ w2 e6 X: s for (int i=0; i , v. ^. Q1 a0 V8 J$ e
sum += rand.nextInt(range);
4 C2 n$ t( p3 r; {. N8 z6 _ } - k2 F6 _1 E5 J) A3 M
System.out.println("Good : " + nf.format(sum/count));
- l2 i& j& ~( `0 ]7 |2 x$ C- X sum = 0; & }9 |& m5 D6 H+ M( ?
for (int i=0; i ! i3 v5 {+ q5 s% _/ q4 K7 `* k5 X
sum += Math.abs(rand.nextInt()) % range;
4 }: v- B2 ?. Z8 n } 1 s7 \# J7 }5 Q( z% z0 M+ [
System.out.println("Bad : " + nf.format(sum/count));
" z' a$ E v/ z# y+ I sum = 0;
3 c, C7 W3 m# i( x n k for (int i=0; i " V# R' d- x( h$ s }
sum += (int)(Math.random() * range);
$ \) C& g) `* |6 R7 A) t( D% ?. m }
* O- ~# l& Y- U/ ]+ u% s# ]. J# _ System.out.println("Longer : " + nf.format(sum/count)); * q. I, b. m8 ~9 O5 _: X
}
^1 j V) @8 Z }2 J A) d1 ?1 m }
& G9 l4 i. C3 B8 y* Q# ?- |