正态分布的随机整数?

3

有没有一种好的方法可以获得正态分布的随机生成整数?

我想到的第一种方法是:

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

有更好的方法吗?

6
什么是正态分布的 WTF 整数?请解释一下你想做什么。 - Alexandre C.
我想从数组中获取随机元素,但有些元素具有更高的概率。 - liborw
3
那么您可能需要一个二项分布。 - Alexandre C.
4个回答

4

严格来说,你不能拥有正态分布的整数。也许你想要的是正态分布输出按桶排序后的结果。在这种情况下,你可能想根据数组大小调整和缩放你的正态分布。如果你只是从标准正态分布(平均值=0,范围=1)中取样,那么99%的时间会得到-2到2之间的样本。

假设你想要从大小为N的数组中随机抽样。你希望中间的条目被选择的频率比末尾的样本更高,但你希望接近末尾的样本偶尔出现,比如1%的时间。那么你可以计算类似于 N/2 + N*z/4 的东西,其中z是标准正态分布,然后将这些数字转换为整数。如果你这样做,偶尔会得到一个超出你的数组索引范围的索引。当发生这种情况时,只需测试并获取一个新值即可。


2
您应该更新问题,明确您的用例是什么。
根据您的评论,您根本不应该使用正态分布。相反,尝试使用众多离散分布之一,因为您最终需要整数。有很多这样的分布,但我推荐一个非常简单的——它使用随机向量作为离散概率分布。
以下是示例实现:
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);
    }
}

1
一些注释:(1)使用TreeMap,从累积概率分布到整数进行排序,在nextInt()中使用TreeMap.floorEntry。(2)您的方法仅处理从0到N-1的N个整数。如果OP想要从-N到+N的2N + 1个整数,或者从1000到1000 + N-1的N个整数,该怎么办? - Jason S
(1) - 很好的优化,我一直想知道如何正确地做。谢谢。至于(2),我认为一些包装方法可以处理这种情况,而不需要太多编码。 - Rekin

1

这取决于您想要如何使用这些随机数。

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

Sun 的方法使用了 48 位种子,并且(就底部位而言)只访问了其中的 17 位 - 产生了极其严重的非随机性

因此,如果您对高统计质量感兴趣,应该避免使用 Sun 的实现。查看此 "Not so random" applet 以获得可视化证明。

如果统计质量是您关注的问题,则最好的选择是使用一些外部 PRNG 库。


引用文章中突出的病态行为是线性同余生成器产生低阶位时_m_为2的幂次时的限制;这不是特定于Sun的实现。当正确使用时,java.util.Random对许多应用程序提供可接受的结果。http://en.wikipedia.org/wiki/Linear_congruential_generator - trashgod
没错,这是正确的。对于这个用途来说,绝对足够了。主要问题(不包括后面的评论)比较模糊,我匆忙回答了。 - Rekin

1
你可以预先计算一个“随机”的整数列表,然后手动调整该列表以获得所需的分布。
当你需要一个“随机”数字时,只需从列表中获取下一个可用的数字...
这样可以确保分布和因此选择特定项目的概率。为了好玩,你可以在需要时随意“混合”你的列表。

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