Java中的随机int函数行为

时间:2014-12-25 00:38:44

标签: java random

我有以下代码:

public class Main {
private static final Random rnd = new Random();

private static int getRand(int n) {
    return (Math.abs(rnd.nextInt())%n);
}

public static void main(String[] args) {
    int count=0, n = 2 * (Integer.MAX_VALUE/3);
    for(int i=0; i<1000000; i++) {
        if(getRand(n) < n/2) {
            count++;
        }
    }
    System.out.print(count);
}
}

这总是给我一个接近666,666的数字。这意味着产生的数字的三分之二低于n的下半部分。并非当n = 2/3 * Integer.MAX_VALUE时获得此值。 4/7是另一个给我类似差异的分数(~5714285)。但是,如果n = Integer.MAX_VALUE或者n = Integer.MAX_VALUE / 2,则得到均匀分布。这种行为与使用的分数有何不同。有人可以对此有所了解。

PS:我从Joshua Bloch的书“Effective Java”中得到了这个问题。

4 个答案:

答案 0 :(得分:4)

问题在于模数(%)运算符导致数字分布不均。

例如,假设MAX_INT为10,并且n = 7,mod运算符将分别将值8,9和10映射到1,2和3。这将导致数字1,2和3的概率是所有其他数字的两倍。

解决此问题的一种方法是检查rnd.nextInt()的输出,并在它大于N时重试。

答案 1 :(得分:2)

如果只保留Math.abs(rnd.nextInt())的值在[0..2 / 3(Integer.MAX_VALUE)]的范围内,则会得到50-50。对于剩余的1/3 * Integer.MAX_VALUE数字,由于模数,您将得到[0..1 / 3 Integer.MAX_VALUE]范围内的较小数字。

总而言之,[0..1 / 3 Integer.MAX_VALUE]范围内的数字有两倍的出现机会。

答案 2 :(得分:1)

Random类旨在生成伪随机数。这意味着它们是具有均匀分布的已定义序列的元素。如果您不知道序列,它们似乎是随机的。

话虽如此,问题在于你通过使用模数运算符搞乱了均匀分布。关于编码恐怖,有一个很好的article解释了这个问题,虽然问题略有不同。现在,您可以找到问题的解决方案以及证明here

答案 3 :(得分:0)

如上所述,getRand 在[0,n]范围内生成均匀分布的随机数。

通常,假设n = a * Integer.MAX_VALUE / b,其中a / b> 0.5

为便于写作,设M = Integer.MAX_VALUE

getRand(n)的概率密度函数(PDF)由下式给出:

PDF(x)= 2 / M为0&lt; x&lt; (B-A)M / B

   =  1/M   for  (b-a)M/b < x < aM/b

n / 2对应于[0,aM / b] = aM / 2b

范围的中点

将PDF整合到上半年&#39;范围[0,n / 2]我们发现getRand(n)小于n / 2的概率(P)由下式给出:

P = a / b

示例:

a = 2,b = 3。 P = 2/3 = 2/3 = 0.66666 ......由提问者计算。

a = 4,b = 7。 P = 4/7 = 0.5714 ...接近提问者的计算结果。