【问题标题】:Normally distributed random integers?正态分布的随机整数?
【发布时间】:2011-03-17 20:19:07
【问题描述】:

有没有很好的方法来获得随机生成的正态分布整数?

我想到的第一个方法:

int rndi = (int)Math.floor(random.nextGaussian()*std);

有没有更好的办法?

【问题讨论】:

  • wtf 正态分布整数?请说明你想做什么。
  • 我想从数组中获取随机元素,但有些元素的概率更高。
  • 那么你可能想要一个二项分布。

标签: java integer random


【解决方案1】:

严格来说,你不能有正态分布的整数。也许您想要的是分类到桶中的正态分布的输出。在这种情况下,您可能希望根据数组的大小来移动和缩放正态分布。如果您只是从标准正态分布(均值 = 0 和尺度 = 1)中抽取样本,则大约 99% 的时间会得到介于 -2 和 2 之间的样本。

假设您想要从大小为 N 的数组中随机抽样。您希望中间的条目比末尾的样本更频繁地被选择,但您希望靠近末端的样本偶尔出现,比如 1%的时间。然后你可能想要计算像 N/2 + N*z/4 这样的东西,其中 z 是你的标准法线,然后将这些数字转换为整数。如果你这样做,你偶尔会在你的数组之外得到一个索引。只需对此进行测试并在发生这种情况时获得一个新值。

【讨论】:

    【解决方案2】:

    您应该更新问题以明确您的用例到底是什么。

    根据您的评论,您根本不应该使用正态分布。而是尝试许多 离散 分布中的一种,因为您希望最后是整数。其中有很多,但我推荐一个 - 非常简单。它使用stochastic vector 作为离散概率分布。

    这里是示例实现:

    public class DiscreteRandom {
    
        private final double[] probDist;
    
        public DiscreteRandom(double... probs) {
            this.probDist = makeDistribution(probs);
        }
    
        private double[] makeDistribution(double[] probs) {
            double[] distribution = new double[probs.length];
            double sum = 0;
            for (int i = 0; i < probs.length; i++) {
                sum += probs[i];
                distribution[i] = sum;
            }
            return distribution;
        }
    
        public int nextInt() {
            double rand = Math.random();
            int i = 0;
            while (rand > probDist[i]) i++;
            return i;
        }
    
        /**
         * Simple test
         */
        public static void main(String[] args) {
            // We want 0 to come 3 times more often than 1.
            // The implementation requires normalized probability
            // distribution thus testProbs elements sum up to 1.0d.
            double[] testProbs = {0.75d, 0.25d};
            DiscreteRandom randGen = new DiscreteRandom(testProbs);
    
            // Loop 1000 times, we expect:
            // sum0 ~ 750
            // sum1 ~ 250
            int sum0 = 0, sum1 = 0, rand;
            for (int i = 0; i < 1000; i++) {
                rand = randGen.nextInt();
                if (rand == 0) sum0++;
                else           sum1++;
            }
            System.out.println("sum0 = " + sum0 + "sum1 = " + sum1);
        }
    }
    

    【讨论】:

    • 一些 cmets:(1) 使用从累积概率分布到整数的 TreeMap,并在 nextInt() 中使用 TreeMap.floorEntry。 (2) 您的方法仅处理从 0 到 N-1 的 N 个整数。如果 OP 想要从 -N 到 +N 的 2N+1 个整数,或者从 1000 到 1000+N-1 的 N 个整数怎么办?
    • (1) - 很棒的优化,我一直想知道如何正确地做到这一点。谢谢。关于(2)我认为一些包装方法可以在没有太多编码的情况下处理这种情况。
    【解决方案3】:

    这取决于您要如何处理这些随机数。

    java.util.Random 有一些缺陷。如 JavaDoc 中所述,nextGaussian() 方法使用 Box Muller 变换。它依赖于使用线性同余生成器实现的Random.nextDouble()。正如错误修复提案中所述,实施并不是最好的:

    Sun 的方法使用 48 位种子,并且(就最低位而言)仅访问其中的 17 位 - 产生非常严重的非随机性。

    因此,如果您对高统计质量感兴趣,则应该避免使用 Sun 的实现。看看这个"Not so random" applet,看看它有多糟糕。

    如果您关心统计质量,您能做的最好的事情就是使用一些外部 PRNG 库。

    【讨论】:

    • 引用的文章中强调的病态行为是当 m 是 2 的幂时线性同余生成器产生的低位限制;它不是特定于 Sun 的实施。如果使用得当,java.util.Random 可为许多应用程序提供可接受的结果。 en.wikipedia.org/wiki/Linear_congruential_generator
    • 当然,没错。对于这种用途,它绝对足够了。主要问题(没有后面的评论)含糊不清,我急忙回答。
    【解决方案4】:

    您可以预先计算一个“随机”整数列表,然后手动调整该列表以获得您想要的分布。

    然后,当您想要一个“随机”数字时,只需从列表中拉出下一个可用的数字...

    这样可以确保分布,从而确保特定项目被选中的概率。为了好玩,您可以随时“混合”您的列表。

    【讨论】:

      猜你喜欢
      • 2016-09-21
      • 2017-07-24
      • 1970-01-01
      • 1970-01-01
      • 2010-11-21
      • 2011-12-27
      • 2014-07-13
      • 2015-03-05
      • 1970-01-01
      相关资源
      最近更新 更多