随机类表现奇怪吗?
在这段代码中:
Random random = new Random(441287210); for(int i=0;i<10;i++) System.out.print(random.nextInt(10)+" "); }
每次输出为1 1 1 1 1 1 1 1 1 1
。
为什么是这样? Random
应该是……好吧……随机? 我认为Random
类使用System.nanoTime
,因此输出通常应该是随机的。 有人可以解释一下吗?
Random
类生成的值是伪随机的 :它们是使用基于种子值的确定性算法创建的。 通常(例如,如果使用无参数构造函数),使用当前时间初始化种子,这显然是唯一值。 因此,生成了唯一的“随机”序列。
在这里,您使用的是一个常量种子值,该值不会在代码执行之间发生变化。 因此,您始终获得相同的序列。 对于这个特定的种子,恰好这个序列是1 1 1 1 1 1 ...
让它打印更多,前100个
1 1 1 1 1 1 1 1 1 1 3 4 7 2 2 6 0 3 0 2 8 4 1 6 0 0 0 2 8 2 9 8 9 2 5 2 1 1 4 5 3 4 1 4 1 8 7 6 6 0 6 5 0 4 5 5 6 0 8 3 8 9 7 4 0 9 9 7 7 9 3 9 6 4 5 0 6 3 7 4 9 8 7 6 2 8 9 8 4 4 8 4 9 0 1 6 9 6 1 5
看起来还不错。
每个好的(伪)随机序列都包含重复数字的条纹,这个条纹以一个开头。
没有什么可说的,连续10 1
s的序列是不可能的。 谁给了你种子值441287210
恰好发现了这样一个值,导致连续10 1
开始。 如果继续调用nextInt()
(即超过10次),您将看到随机值。 应该可以找到将导致其他“明显非随机”序列的其他种子值。
随机是线性同余发生器 ; 即它基于以下forms的公式:
N <- (N * C1 + C2) % M
其中C1,C2和M是常数。
这类发生器的一个特性是具有高自相关性。 实际上,如果您绘制连续数字,您可以在数字中看到清晰的剥离模式。
你的测试程序有效地从底层生成器中获取了10个连续的数字,计算它们的模数为10 ...并发现它们都是相同的。 实际上,模10在短时间内与发电机的自然周期“共振”。
这是使用具有高自相关性的PRNG的缺点之一。 通俗地说......它“不是很随机”......如果你在随机性至关重要的情况下使用它,你就会遇到麻烦。
笔记:
- 随机不是随机数发生器。 它是一个伪随机数发生器。 这意味着如果您知道初始状态,则生成的数字完全可以预测。
- 对Random使用真正的随机种子并没有多大帮助。 它只会使问题难以重现。
- Random可能有其他种子可以为您提供与此特定测试相似的模式。
- 从纯粹的数学观点来看,十个数字与任何其他十个数字序列相比都不是“随机”的。 但从数学的角度来看,
Random
并不是随机的。 实际上,一旦你弄清楚N
的当前值是什么,它就完全可以预测。 问题是自相关使得序列看起来非直观。 - 如果你想避免这种直观的非随机性,那么使用SecureRandom,它应该是一个真正的随机数源,或者是一个非常难以预测的伪随机数生成器。
如果你使用for(int i=0;i<100;i++)
,输出的序列再次“更随机”。 随机序列连续发生的概率可能很小,但并非不可能。 (只要给出足够的样本,几乎肯定会发生任何序列。)
这只是一个有趣的巧合。
当你调用nextInt()时,Random类使用seed来生成随机数,并且建议你是一个长数,当你创建随机对象时,你提供的int对于随机性是不够的。
尝试运行循环20次,您将看到随机性或移除种子或提供非常长的种子值