有效的Java项目47:了解并使用您的库 – 有缺陷的随机整数方法示例
在Josh给出的有缺陷的随机方法的例子中,该方法产生具有给定上界n
的正随机数,我不明白他陈述的两个缺陷。
书中的方法是:
private static final Random rnd = new Random(); //Common but deeply flawed static int random(int n) { return Math.abs(rnd.nextInt()) % n; }
- 他说,如果n是2的小幂,则生成的随机数序列将在短时间后重复出现。 为什么会这样?
Random.nextInt()
的文档说明Returns the next pseudorandom, uniformly distributed int value from this random number generator's sequence.
所以不应该是,如果n是一个小整数,那么序列将重复,为什么这只适用于2的幂? - 接下来他说如果n不是2的幂,一些数字平均会比其他数字更频繁地返回。 如果
Random.nextInt()
生成均匀分布的随机整数,为什么会发生这种情况呢? (他提供了一个代码片段,清楚地certificate了这一点,但我不明白为什么会出现这种情况,以及这与n是2的权力有什么关系)。
问题1:如果n是2的小幂,则生成的随机数序列将在短时间后重复出现。
这不是约什所说的任何必然结果; 相反,它只是线性同余生成器的已知属性。 维基百科有如下说法:
LCG的另一个问题是,如果m被设置为2的幂,则生成的序列的低阶位具有比整个序列短得多的周期。通常,基数中的n个最低有效位。 b输出序列的表示,其中对于某些整数k,b k = m,重复最多具有周期b n 。
这在Javadoc中也有提到:
已知线性同余伪随机数发生器(例如由该类实现的那些)在其低阶位的值序列中具有短周期。
函数的另一个版本Random.nextInt(int)
通过在这种情况下使用不同的位来解决这个问题(强调我的):
该算法特别处理n是2的幂的情况:它从底层伪随机数发生器返回正确数量的高阶位。
这是Random.nextInt(int)
使用Random.nextInt()
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相关的资源:
- 光谱测试是用于评估LCG质量的统计测试。 在这里和这里阅读更多。
- 具有线性结构的经典伪随机数生成器的集合具有一些漂亮的散点图(Java中使用的生成器称为DRAND48 )。
- 关于crypto.SE有一个关于从Java生成器预测值的讨论 。
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
是否是n
的幂,都会发生这种情况。两个与否,但是,如果不是10个,我们选择15个(即2 ^ 4-1),那么任何n
,即2的幂将导致均匀分布,因为没有“过剩“在该范围的末尾留下的数字会导致偏差,因为可能值的总数可以完全被可能的剩余数量整除。
- JUnit:运行一个具有不同配置的测试
- 如何模拟内存不足:请求的arrays大小超过VM限制
- JAVA SAX DefaultHandler startCDATA()未触发
- 日食中奇怪的自动生成的吸气剂和二传手
- 无法使用Java的URLConnection获取响应头位置
- 为什么Java有NullPointerException而不是NullReferenceException?
- DateTimeFormatter自动更正无效(语法上可能)的日历日期
- 启动创意时出错。 无法加载JVM DLL C:\ Program Files \ Java \ jdk1.8.0_112
- 排序整数的arraylist的arraylist