在Josh给出的有缺陷的随机方法的例子中,该方法生成一个具有给定上限n
的正随机数,我不明白他所陈述的两个缺陷。
书中的方法是:
private static final Random rnd = new Random();
//Common but deeply flawed
static int random(int n) {
return Math.abs(rnd.nextInt()) % n;
}
Random.nextInt()
的文档说Returns the next pseudorandom, uniformly distributed int value from this random number generator's sequence.
所以不应该是,如果n是一个小整数,那么序列会重复,为什么这只适用于2的幂?Random.nextInt()
生成均匀分布的随机整数,为什么会发生这种情况? (他提供了一个代码片段,清楚地证明了这一点,但我不明白为什么会这样,以及这与n是2的权力有什么关系。)答案 0 :(得分:36)
问题1:如果n是2的小幂,则生成的随机数序列将在短时间后重复出现。
这不是乔希所说的任何事情的必然结果;相反,它只是linear congruential generators的已知属性。维基百科有以下说法:
中也提到了这一点LCG的另一个问题是,如果m被设置为2的幂,则生成序列的低阶位具有比整个序列短得多的周期。通常,n个最低有效位在输出序列的基数b表示中,其中对于某些整数k,b k = m,重复最多为句点b n 。
已知线性同余伪随机数生成器(例如由该类实现的生成器)在其低阶位的值序列中具有短周期。
函数的另一个版本Random.nextInt(int)
通过在这种情况下使用不同的位来解决这个问题(强调我的):
该算法特别处理n为2的幂的情况:它从底层伪随机数生成器返回正确数量的高阶位。
这是一个很好的理由,希望Random.nextInt(int)
优先使用Random.nextInt()
并进行自己的范围转换。
问题2:接下来他说如果n不是2的幂,那么平均一些数字会比其他数字更频繁地返回。
nextInt()
可以返回2个 32 个不同的数字。如果您尝试使用% n
将它们放入n个桶中,并且n不是2的幂,则某些桶将具有比其他桶更多的数量。这意味着即使原始分布是统一的,某些结果也会比其他结果更频繁地发生。
让我们用小数字看一下。假设nextInt()
返回了四个等概率结果,0,1,2和3.让我们看看如果我们将% 3
应用于它们会发生什么:
0 maps to 0
1 maps to 1
2 maps to 2
3 maps to 0
正如您所看到的,算法将返回0的频率是返回1和2中每一个的两倍。
当n是2的幂时,这不会发生,因为一个2的幂可以被另一个整除。考虑n=2
:
0 maps to 0
1 maps to 1
2 maps to 0
3 maps to 1
这里,0和1以相同的频率出现。
其他资源
以下是一些额外的 - 如果只是切向相关 - 与LCG相关的资源:
答案 1 :(得分:5)
1)当n
是2的幂时,rnd % n
相当于选择原始的几个低位。由java使用的生成器类型生成的较低位数已知比较高位“较不随机”。它只是用于生成数字的公式的属性。
2)想象一下,random()
返回的最大可能值为10,n = 7
。现在,n % 7
将数字7,8,9和10分别映射为0,1,2,3。因此,如果原始数字是均匀分布的,则结果会严重偏向较低的数字,因为它们的出现次数是4,5和6的两倍。在这种情况下,无论是否n
,都会发生这种情况。是2的幂,但是,如果不是10,我们选择15(即2 ^ 4-1),那么任何n
,即2的幂将导致均匀分布,因为在范围的末尾不会留下“过剩”数字来引起偏差,因为可能值的总数可以被可能的剩余数量完全整除。