随机类表现异常?

15

在这段代码中:

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,所以输出结果应该是大致上随机的。能否有人请解释一下?


可能是 https://dev59.com/PFPTa4cB1Zd3GeqPl7gK 的重复问题。 - Jeremy D
1
也许它是随机的!(http://search.dilbert.com/comic/Random%20Number%20Generator) - Richard JP Le Guen
1
@JeremyD 并不完全正确-该问题会每次创建一个新的随机对象。 - Mike Park
3
任何考虑使用算术方法产生随机数字的人,都处于一种罪恶的状态。- 约翰·冯·诺伊曼 - Steve Kuo
如果你不只看问题,而是看第二个答案,那么它就是正确的答案,与此处提出的答案相同 :) - Jeremy D
你使用了错误的种子,请使用-45134723714! - AlexWien
6个回答

20

让它再打印几个,前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

这看起来还不错。

每个良好的(伪)随机序列都包含一连串重复的数字,而这个序列就是以一个重复数字开始的。


3
Nathan,如果在线扑克使用劣质的线性同余发生器(LCG),我会感到恐惧。 - Joey
而且,他们不能太直言不讳,因为没有欺诈,可以赚取太多的钱。 - Daniel Fischer
他们让它看起来很好,但并没有考虑所有可能性。他们确保所有能够产生行动的可能性都存在,但忽略了现实生活中存在的一些更平凡的情况,从而创造更多的刺激和行动,并给较差的玩家更好的机会。 - nathan hayfield
@nathanhayfield 每种用于赌博的游戏都是有预谋地被操纵,即使庄家公平对待,设计上也旨在从长远来看为庄家赢得钱财。在线赌场相比实体赌场经营规模巨大,几乎可以保证持续的利润。 - millimoose
1
人们对数百万手在线扑克进行了严格的统计分析,从未发现任何证据表明他们做的不是真正的随机洗牌。此外,这里有三个事实:1.几乎没有动机去操纵牌局,2.如果发现他们在操纵,会有巨大的反激励作用,3.如果真的被操纵,很容易让某人证明它是被操纵的。简而言之,在线扑克公司绝对不会做任何类似的事情。 - Joe K

14
Random类生成的值是伪随机数:它们使用确定性算法基于种子值创建。通常(例如,如果您使用无参构造函数),种子使用当前时间进行初始化,这显然是一个唯一的值。因此,生成了一个唯一的“随机”序列。

在此处,您使用一个恒定的种子值,其在代码执行之间不会更改。因此,您始终获得相同的序列。对于这个特定的种子,序列只是1 1 1 1 1 1 ...


除了从诸如/dev/random之类的源中提取熵的类/方法外,哪种随机生成器不是伪随机的? - NullUserException
我只想澄清可能存在的混淆,与“随机”这个词在此处的使用有关。通常人们知道他们正在引用伪随机(即确定性计算)数字,但是这里的OP似乎希望得到真正的(非确定性)随机性。 - Xion

5

并没有规定连续10个1不可能出现。赋予你种子值441287210的人只是恰好找到了这样一个值,可以使得从头开始连续10个1。如果继续调用nextInt()(即超过10次),您将看到随机值。应该可以找到其他种子值,以产生其他“表面上非随机”的序列。


1
实际上,由于“long”有1.8e19个可能的值和“Random”可以生成的序列,因此对于每个可能的10个数字序列,都存在一个种子,使得“Random”将它们作为“nextInt(10)”的前十个结果生成。(留给读者更擅长统计/概率的练习是确定这一点的实际概率。或者说,1.8e19个真正随机选择的10个数字中最终选出所有可能的10个数字序列的概率是多少。) - millimoose

3

Random 是一个线性同余发生器,即它基于如下形式的公式:

linear congruential generator
N <- (N * C1 + C2) % M

其中C1、C2和M都是常数。

这类生成器的一个特性是具有高自相关性。实际上,如果你绘制连续的数字,你会看到数字中存在明显的条纹模式。

你的测试程序已经从底层生成器中取出了10个连续的数字,计算它们对10取模的值......并发现它们都相同。实际上,对于这种情况,对10取模正好与生成器的自然周期“共振”......只在短时间内。

这是使用具有高自相关性的伪随机数生成器的缺点之一。用通俗易懂的话来说......它“不太随机”......如果在需要随机性的情况下使用它,可能会遇到麻烦。


注意:

  • Random不是真正的随机数生成器。它是一个伪随机数生成器。这意味着,如果你知道初始状态,生成的数字就是完全可预测的。
  • 使用真正的随机种子对Random没有帮助。它只会使问题更难以重现。
  • 很可能还有其他Random种子会在这个特定的测试中产生类似的模式。
  • 从纯数学的角度来看,十个1与任何其他十个数字序列一样“随机”。但从数学的角度来看,Random根本不是随机的。事实上,一旦你确定了N的当前值,它就是完全可预测的。问题在于自相关性使得这个序列看起来直观上不随机。
  • 如果你想避免这种直观上的非随机性,请使用SecureRandom,它应该是真正的随机数源,或者是生成伪随机数的生成器,这些生成器要难得多,难以预测。

您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - AlexWien

1
如果您使用for(int i=0;i<100;i++),输出的序列将再次变得“更随机”。连续出现十个1的随机序列的概率可能很小,但并非不可能。(在足够的样本中,任何序列几乎肯定会出现。)这只是一个有趣的巧合。

2
连续出现十个数字1的概率与任何其他序列的概率完全相同。 - Christoffer Hammarström
很可能那个种子不是手工采摘的。 - AlexWien
@AlexWien 实际上,我反驳说这是极有可能的。随机数生成器被明确地种子化,使用一个产生非常可识别的句子的数字。你怎么想到 OP 是偶然发现的呢?你不能在不使用反射的情况下访问 Random 实例的种子,因此你假设某人正在生成小于 10 的整数,并记录序列以完成某些完全无关的目的,并检查每个生成的数字的种子以便能够重现这个结果。 - millimoose
基本上,我认为OP发现的任何情况都是偶然发现的,这种情况相当复杂,即不太可能,更有可能的是有人有意寻找“有趣”的随机种子。例如,一位试图困惑/逗乐他的学生的老师。 - millimoose
我是对的:这就是暴力破解法:请看这里:http://insights.dice.com/2014/01/24/generating-random-numbers-javas-random-class/ - AlexWien
显示剩余3条评论

0

Random类在调用nextInt()方法生成随机数时使用种子(seed),建议使用长整型(long)数字,当您创建随机对象时,提供的int类型数字不足以保证随机性。

尝试运行20次循环,您将看到随机性或者移除种子或提供一个非常长的种子值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接