【问题标题】:Can Java's random function be zero?Java的随机函数可以为零吗?
【发布时间】:2010-06-17 20:53:22
【问题描述】:

出于好奇,Math.random() 可以为零吗?

例如,如果我有:

while (true){
  if (Math.random() == 0)
    return 1;
}

我真的会得到一个回报吗?还需要考虑舍入误差,因为 Math.random() 返回一个双精度值。

我问是因为我的 CS 教授说 random() 从 0 到 1 包括在内,我一直认为它是排他的。

【问题讨论】:

  • 您的 CS 教授是在谈论 Java,还是在谈论一个等分布的纯数学随机函数?因为这样的函数,对于从 0 到 1 的实数,没有任何概率为 0。也不是 1。也不是 0.5:概率只能分配给一个区间。但这对于我们在 Java 中的离散数字范围当然不是真的......(我敢肯定,这就是他的意思;)
  • 只需检查 Math.random() 的文档,它会指示它是包含还是独占。
  • 顺便说一句,这里没有舍入错误。你只会得到一个double,其尾数中有 53 个均匀分布的伪随机位。
  • 这篇文章在一个关于 Minecraft 错误的 YT 视频中得到推荐:youtube.com/watch?v=JFHGiv6qeX8
  • 我想我实际上得到了一次零。我正在编写一个脚本以随机速度移动一系列圆圈,而其中一个恰好在周围的混乱中静止

标签: java random


【解决方案1】:

是的,它真的可以。 Math.random() 使用种子 (System.currentTimeMillis() ^ 0x5DEECE66DL) & ((1L << 48) - 1) 创建一个全局 java.util.Random-generator 并为其调用 nextDouble()。如果它的种子达到状态107048004364969L(它会,因为java.util.Random 有完整的周期),生成的下一个double 将是0.0。 尽管运气不好,您最终可能会在周期中得到错误的奇偶校验,因为 Random.nextDouble() 将状态推进两次。如果运气差一点,您可能不得不在循环终止之前生成 2^47 个随机数,因为我没有找到任何其他提供 0.0 的种子。

种子像seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);一样前进 使用两个连续种子值的 26 和 27 高位生成双精度。在示例中,接下来的两个种子值将是 0L11L

如果您设法使用System.currentTimeMillis()==107038380838084L 创建全局生成器,您的代码会立即返回。您可以使用以下方法进行模拟:

java.util.Random k = new java.util.Random(107038380838084L); System.out.println(k.nextDouble()==0);

【讨论】:

  • 而且,如果您认为在下一次尝试中击中零看起来太可疑,请使用new java.util.Random(164311266871034L),您将在两次尝试中击中它。或new java.util.Random(240144965573432L),然后尝试三下。或new java.util.Random(881498),在调用nextDouble 376050 次后你会得到一个零。
  • 这应该是公认的答案。该问题询问 java 的 Math.random,它(第一次调用时)创建一个新的伪随机数生成器,就像通过表达式 new java.util.Random() 一样,然后又将其用作 Random.nextDouble(),此后所有调用 Math.random() . nextDouble() 工作方式为:return (((long)next(26) << 27) + next(27)) / (double)(1L << 53);(在早期的 Java 版本中错误地为:return (((long)next(27) << 27) + next(27)) / (double)(1L << 54);)。这里next返回请求的最高位(int)(seed >>> (48 - bits))
  • 如果有人想试试这个,请在​​ 29.11.5361 的 3:33:58.084 运行你的程序。只是需要一点等待。实际上,我不知道那天会有多少程序做一些意想不到的事情。这就像2038年的问题。除了我非常怀疑System.currentTimeMillis 是否仍会在 5361 中以当前形式使用。
【解决方案2】:

根据the documentation,“返回一个带正号的双精度值,大于或等于 0.0 且小于 1.0。”这意味着它可以为零。

作为Hank wrote,它在上边界上是唯一的(永远不能是1),所以也许这就是你的困惑的来源:-)。

【讨论】:

    【解决方案3】:

    它很可能永远不会完全返回零。 Java 包含的 PRNG 是一个 48 位 LCG,其中仅使用了 32 位。要使 double 尾数的所有 53 位为零,您基本上需要至少 一个 调用 next() ,其中高 32 位为零,而另一个大部分为 0。 (如果我没记错的话,我会说发电机的工作方式永远不会发生这种情况,但已经晚了,我累了,我不会赌太多。)

    由于方法文档明确说明了如何获得随机数,因此 Java 运行时的其他实现也几乎没有余地来产生不同的结果。 合约 可能会说你得到的数字来自 [0, 1)。但在实践中,有很多值你永远不会命中(因为你需要来自生成器的两个连续值,它强制在连续值之间产生线性依赖关系——只有 48 位状态。你不能生成所有不同的53 位组合——至少不是它是如何完成的。)。

    当然,由于Math.random() 自动为静态Random 实例播种,我们可能还必须在此处考虑种子,可能需要非常具体才能使测试用例解决.这可能意味着那个确切的时间点可能在几十年或几千年后。

    【讨论】:

    • 作为一个实际的反例,Angs 的answer below 显示了一个特定的 PRNG 状态,其中生成器将完全返回 next()s 的组合。
    【解决方案4】:

    它包括零,不包括一,例如,[0, 1)0 <= x < 1,具体取决于您喜欢哪种表示法。

    【讨论】:

      【解决方案5】:

      理论上,它可以返回零值。

      实际上,您可能需要等待很长时间才能准确地归零。如果随机数生成器实现得好,它至少有 56 位的内部状态(否则返回结果的所有位都不会是随机的)。这意味着,如果随机产生的值的分布是平坦的,那么您最多有 2^56 次机会取回一个所有位都为零的值。大约是 10^-19。我不会屏住呼吸。

      (其他人正确地观察到,正如记录的那样,理论上 [可能在实践中] 它不能返回值 1.0)。

      【讨论】:

      • 您可以为 56 位内部状态的任意组合设置相同的情况。如果随机数生成器实现得好,返回零的机会应该与任何其他离散双精度相同。
      • @Gilbert:同意,但问题是“可以为零吗?”
      • Java 使用 48 位 LCG,其中仅使用了 32 位。 nextLong() 的文档中也指出了这一点,它只是无法生成所有可能的 long 值。 doubles 也是如此(显然)。
      • @Johannes:好的,所以随机数并不像它们可能的那样随机(32 位而不是 56 位)。似乎是一个非常愚蠢的实现;不是很随机的随机数生成器有什么意义?仍然只有 2^32 中的 1 次机会,或者不到十亿分之一。
      • 几十年来人们一直在构建随机数生成器;当我在 70 年代的数值算法课上学习时,有大量的理论。当 Java 出现时,如何构建一个好的产品很容易获得。在我看来,他们只是做得很糟糕。是的,您可以自己编写代码。框架或库的重点是安排它,这样您就不必这样做了。
      【解决方案6】:

      来自 java API。

      返回一个带正号的双精度值,大于或等于 0.0 且小于 1.0

      http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Math.html#random()

      所以是的。

      【讨论】:

        【解决方案7】:

        Math.random() 是documented 返回“一个带正号的双精度值,大于等于0.0 小于1.0” 即包含0.0但不包含1.0

        【讨论】:

          【解决方案8】:

          在兼容的 JRE 实现中,它也有可能永远不会返回 0。

          【讨论】:

          • 这也是可能的,给定一个真正的随机生成器,并且由于随机性的性质,在兼容的 JRE 实现中,它总是返回 0。
          • @Stephen:阅读 Javadocs(对于标准库,它被视为规范的一部分)。 Math.random() 的任何实现必须使用java.util.Random,而java.util.Random必须是特定的LCG。
          • @Johannes:我阅读了 javadocs,并在我的回答中链接到它们。我的观点,与不可靠的回答有关,虽然确实可能永远不会返回 0.0,但也有可能总是返回 0.0。为了符合要求,必须可以返回任何值 x 其中 0.0 x 不太可能 的情况 正确的。
          【解决方案9】:

          math.random() 理论上有可能返回零值,但在现实世界中,您可以指望它几乎不会发生。

          我曾经连续运行我的电脑一周等待一个,它生成了大约 10 万亿个随机数,没有零。

          但更直接地说,它实际上是两种方式都互斥的。

          【讨论】:

          • 理论上不可能,但完全有可能。请参阅@Angs 和 cmets 提供的答案。
          • 我跑了一个月,从来没得过零。
          【解决方案10】:

          来自http://java.sun.com/javase/6/docs/api/java/lang/Math.html

          random() 返回一个带正号的双精度值,大于等于 0.0 且小于 1.0。

          是的,它可以是 0,但不能是 1。换句话说,比起你的 CS 教授,更喜欢 Java 文档 =)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-07-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多