【问题标题】:Java's Random class behaviour clarificationJava 的 Random 类行为说明
【发布时间】:2017-03-10 00:05:32
【问题描述】:

我有一个类“A”,它包含一个 Random 实例,它模拟掷骰子,例如6 面,每个“滚动”产生 1 - 6 个结果。

我有另一个类“B”,它可能包含对 A 类型对象的引用,并且应该委托给它的“滚动”方法。

另一个人需要编写一个单元测试来评估 B 类中的方法是否正确地委托给 A 类中的“滚动”方法。为此,我只需调用方法 X 次并检查分数是否更新。

目前在单元测试中,我有一个循环多达 1000 次,如果分数尚未更新,那么我认为相同的数字可能连续滚动 1000 次超出了可能性范围!然而,我凭空选择了数字 1000。我想根据 Java Random 类的实际行为选择一个更准确的数字。

通过阅读 API,它说它是“一个线性同余伪随机数生成器......”。我试图确定的是基于它使用 48 位,是否有多次不可能重复相同的值。例如。在我可以生成数字 1 - 6 的情况下,是否说不可能获得相同的数字,例如6、连续20次以上?如果有人知道,我正在寻找该信息。

更新——我会让问题更简单。使用 Java 随机类,如果我调用 nextInt(6),我可以连续收到多少次相同的结果。例如。在数学上,基于 48 位种子和 Random 类算法的工作原理是否存在限制?我想要一个说明,例如“绝对不可能获得超过 X 次相同的结果”,其中 X 是我的答案。

【问题讨论】:

  • 请输入您的实际代码
  • “检查分数是否更新”不是很清楚。话虽如此,如果您想测试 A 是否将 roll 委托给 B,您应该模拟 B 并验证已在模拟上调用了 roll。你可以看看 EasyMock 或 Mockito 来模拟 B。
  • 真正的单元测试会模拟对象 A 并直接验证它的 roll 方法是否被调用。
  • 此外,当测试使用确定性伪随机数生成器的代码时,在测试用例中指定种子很有用。这样,代码应该产生完全相同的事件序列。
  • 基本错误,您每次使用不同的随机生成器而不指定种子,因此采用标准种子。这意味着每次创建具有相同种子的新随机生成器并因此给出相同的数字。给你的 die 类一个静态随机生成器。并用适当的种子喂它,就像当前时间一样。

标签: java random generator probability


【解决方案1】:

通常您会尝试模拟 A 类(即使用 Mockito)。 Here 是一个 SO 问题/答案,它显示了如何验证一个方法是否被调用了 n 次。

【讨论】:

  • 导入静态 org.mockito.Mockito.*; -- 我不能在我的 JDK 安装中使用这个库。我不想透露原因。我会让问题更简单。使用 Java 随机类,如果我调用 nextInt(6),我可以连续收到多少次相同的结果。例如。在数学上,基于 48 位种子和 Random 类算法的工作原理是否存在限制?我想要一个说明,例如“绝对不可能获得超过 X 次相同的结果”,其中 X 是我的答案。
  • @Tranquility 如果您的测试成功或失败取决于Random 类的内部工作,那么它不再是真正的单元测试,是吗?
【解决方案2】:

Random 使用的 PRNG 根据文档只有 248 种不同的状态。因此,nextInt(6) 生成的数字序列以最多 248 的周期重复。

我们可以假设所有 6 种可能的结果最终都是独立于种子值生成的,否则,生成器的质量非常差。因此,必须有一个极限 X 48 满足“绝对不可能得到相同结果超过 X 次”。

然而,要找到最小的这样的 X 并非易事。蛮力法会产生 248 个连续的数字并检查。

【讨论】:

  • 非常感谢!这是我一直在寻找的信息。为了澄清,我的意思是连续 X 次获得相同的结果,因此连续 X 次达到数字 5。