在这段代码中:
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 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
,所以输出结果应该是大致上随机的。能否有人请解释一下?
让它再打印几个,前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
这看起来还不错。
每个良好的(伪)随机序列都包含一连串重复的数字,而这个序列就是以一个重复数字开始的。
Random
类生成的值是伪随机数:它们使用确定性算法基于种子值创建。通常(例如,如果您使用无参构造函数),种子使用当前时间进行初始化,这显然是一个唯一的值。因此,生成了一个唯一的“随机”序列。
在此处,您使用一个恒定的种子值,其在代码执行之间不会更改。因此,您始终获得相同的序列。对于这个特定的种子,序列只是1 1 1 1 1 1 ...
。
/dev/random
之类的源中提取熵的类/方法外,哪种随机生成器不是伪随机的? - NullUserException并没有规定连续10个1
不可能出现。赋予你种子值441287210
的人只是恰好找到了这样一个值,可以使得从头开始连续10个1
。如果继续调用nextInt()
(即超过10次),您将看到随机值。应该可以找到其他种子值,以产生其他“表面上非随机”的序列。
Random 是一个线性同余发生器,即它基于如下形式的公式:
linear congruential generatorN <- (N * C1 + C2) % M
其中C1、C2和M都是常数。
这类生成器的一个特性是具有高自相关性。实际上,如果你绘制连续的数字,你会看到数字中存在明显的条纹模式。
你的测试程序已经从底层生成器中取出了10个连续的数字,计算它们对10取模的值......并发现它们都相同。实际上,对于这种情况,对10取模正好与生成器的自然周期“共振”......只在短时间内。
这是使用具有高自相关性的伪随机数生成器的缺点之一。用通俗易懂的话来说......它“不太随机”......如果在需要随机性的情况下使用它,可能会遇到麻烦。
注意:
Random
根本不是随机的。事实上,一旦你确定了N
的当前值,它就是完全可预测的。问题在于自相关性使得这个序列看起来直观上不随机。for(int i=0;i<100;i++)
,输出的序列将再次变得“更随机”。连续出现十个1
的随机序列的概率可能很小,但并非不可能。(在足够的样本中,任何序列几乎肯定会出现。)这只是一个有趣的巧合。Random
实例的种子,因此你假设某人正在生成小于 10 的整数,并记录序列以完成某些完全无关的目的,并检查每个生成的数字的种子以便能够重现这个结果。 - millimooseRandom类在调用nextInt()方法生成随机数时使用种子(seed),建议使用长整型(long)数字,当您创建随机对象时,提供的int类型数字不足以保证随机性。
尝试运行20次循环,您将看到随机性或者移除种子或提供一个非常长的种子值。