【问题标题】:Math.random() versus Random.nextInt(int)Math.random() 与 Random.nextInt(int)
【发布时间】:2010-10-18 19:50:16
【问题描述】:

Math.random() * nRandom.nextInt(n) 之间有什么区别,其中n 是整数?

【问题讨论】:

  • 我不知道数学,但我知道如果你使用 Math.random(),FindBugs 会抱怨
  • 记住 Random 没有静态方法,所以使用:(new Random()).nextInt(n))。为了让 Math 生成类似的整数,请使用:Math.floor((Math.random()*n)+1);

标签: java random


【解决方案1】:

根据https://forums.oracle.com/forums/thread.jspa?messageID=6594485&#6594485Random.nextInt(n)Math.random() * n 效率更高,偏见更少

【讨论】:

  • 我建议实际上引用他的一些帖子并在这里进行一些总结。该链接应该是您答案的补充。
【解决方案2】:

下面是the detailed explanation 说明为什么“Random.nextInt(n)Math.random() * n 更高效且更少偏见”来自 Gili 链接到的 Sun 论坛帖子:

Math.random() 在内部使用 Random.nextDouble()。

Random.nextDouble() 使用 Random.next() 两次生成一个 double,其尾数中的位大致均匀分布,因此它均匀分布在 0 到 1-(2^-53) 的范围内。

Random.nextInt(n) 使用 Random.next() 的次数平均少于两次——它使用一次,如果获得的值高于 n 的最高倍数,低于 MAX_INT,它会再次尝试,否则返回模数n(这可以防止高于 n 的最高倍数低于 MAX_INT 的值扭曲分布),因此返回一个在 0 到 n-1 范围内均匀分布的值。

在缩放 6 之前,Math.random() 的输出是从均匀分布中提取的 2^53 个可能值之一。

按 6 缩放不会改变可能值的数量,并且强制转换为 int 然后将这些值强制放入六个“桶”(0、1、2、3、4、5)之一,每个桶对应于范围包括 1501199875790165 或 1501199875790166 的可能值(因为 6 不是 2^53 的除数)。这意味着,对于足够数量的骰子(或具有足够多面数的骰子),骰子将显示自己偏向较大的桶。

您将等待很长时间掷骰子才能显示此效果。

Math.random() 也需要大约两倍的处理,并且需要同步。

【讨论】:

  • Random.nextInt 和 nextDouble 也需要同步。
  • 在这种情况下,“较少偏见”是什么意思?
  • @ΦXocę웃Пepeúpaツ 这只是意味着某些数字比其他数字更有可能被绘制。因为它偏向于选择一些数字而不是其他数字(因此不是完全随机的或给定足够大的样本量统一)
  • 请注意,该线程的最后一条评论报告:“所描述的偏差是 2^53 的一部分,但使用的 PRNG 的最大循环长度仅为 2^48。所以你将在应用程序中看到的是底层 PRNG 的数据分布,而不是偏差。”这表明这两种方法是等价的
  • @ΦXocę웃Пepeúpaツ 在骰子上将6 替换为5:它将是“5-biased”。在你注意到骰子有问题之前,你可以掷几次骰子。在您发现随机生成器出现问题之前,您必须执行极其复杂的彻底检查。
【解决方案3】:

另一个重要的一点是 Random.nextInt(n) 是可重复的,因为您可以使用 same 种子创建两个 Random 对象。这对于 Math.random() 是不可能的。

【讨论】:

    【解决方案4】:

    根据此示例,Random.nextInt(n) 的可预测输出低于 Math.random() * n。根据 [排序数组比未排序数组快][1] 我认为我们可以说 Random.nextInt(n) 难以预测

    usingRandomClass : 时间:328 英里秒。

    使用MathsRandom:时间:187 英里秒。

    package javaFuction;
    import java.util.Random;
    public class RandomFuction 
    {
        static int array[] = new int[9999];
        static long sum = 0;
        public static void usingMathsRandom() {
            for (int i = 0; i < 9999; i++) {
             array[i] = (int) (Math.random() * 256);
           }
    
            for (int i = 0; i < 9999; i++) {
                for (int j = 0; j < 9999; j++) {
                    if (array[j] >= 128) {
                        sum += array[j];
                    }
                }
            }
        }
    
        public static void usingRandomClass() {
            Random random = new Random();
            for (int i = 0; i < 9999; i++) {
                array[i] = random.nextInt(256);
            }
    
            for (int i = 0; i < 9999; i++) {
                for (int j = 0; j < 9999; j++) {
                    if (array[j] >= 128) {
                        sum += array[j];
                    }
                }
    
            }
    
        }
    
        public static void main(String[] args) {
            long start = System.currentTimeMillis();
            usingRandomClass();
            long end = System.currentTimeMillis();
            System.out.println("usingRandomClass " + (end - start));
            start = System.currentTimeMillis();
            usingMathsRandom();
            end = System.currentTimeMillis();
            System.out.println("usingMathsRandom " + (end - start));
    
        }
    
    }
    

    【讨论】:

    • 在第二个循环中检查 >= 50,这在 50% 以上的情况下是正确的。这将导致这个 if 语句在大多数情况下都是正确的,这使得它更具可预测性。因此,您的结果偏向于您的答案
    • 这是拼写错误...在第二个示例中输入 128 你会得到相同的结果。