【问题标题】:Sample without replacement in Java with probabilities在 Java 中不带概率替换的示例
【发布时间】:2015-04-06 22:38:40
【问题描述】:

我有一个包含 10 个概率的列表(假设这些概率按降序排列):<p1, p2, ..., p10>。我想采样(不替换)10 个元素,使得选择 i-th 索引的概率为 p_i。

在 Random 等常见库中是否有现成的 Java 方法可供我使用?

示例:5 元素列表:

选择 5 个索引(无重复),以便它们的选择概率由上面列表中该索引处的概率给出。所以索引 0 将以 0.4 的概率被选中,索引 1 以概率 0.3 被选中,依此类推。

我已经编写了自己的方法来做到这一点,但觉得使用现有的方法会更好。如果您知道这种方法,请告诉我。

【问题讨论】:

  • 这确实是非常具体的行为,我怀疑是否有一些具有这种方法的常用库。
  • 好吧,这在概率域中并不是一个非常具体的行为。也许我应该在 Cross-Validated 中交叉发帖。

标签: java sampling


【解决方案1】:

这是通常的做法:

    static int sample(double[] pdf) {
        // Transform your probabilities into a cumulative distribution
        double[] cdf = new double[pdf.length];
        cdf[0] = pdf[0];
        for(int i = 1; i < pdf.length; i++)
            cdf[i] += pdf[i] + cdf[i-1];
        // Let r be a probability [0,1]
        double r = Math.random();
        // Search the bin corresponding to that quantile
        int k = Arrays.binarySearch(cdf, random.nextDouble());
        k = k >= 0 ? k : (-k-1);
        return k;
    }

如果您想返回概率,请执行以下操作:

    return pdf[k];

编辑:我刚刚注意到你在标题中说sampling without replacement。快速做到这一点并不是那么简单(我可以给你一些我有的代码)。无论如何,在这种情况下,您的问题没有任何意义。您无法从概率分布中进行抽样而不进行替换。你需要绝对频率。

即如果我告诉你我有一个装满两个球的盒子:橙色和蓝色,比例分别为 20% 和 80%。如果你不告诉我每个人有多少个球(绝对值),我无法告诉你几回合后你会有多少个球。

EDIT2:更快的版本。这不是通常的情况,但我在网上找到了这个建议,我也在我的项目中使用了它。

    static int sample(double[] pdf) {
        double r = random.nextDouble();
        for(int i = 0; i < pdf.length; i++) {
            if(r < pdf[i])
                return i;
            r -= pdf[i];
        }
        return pdf.length-1;  // should not happen
    }

对此进行测试:

// javac Test.java && java Test

import java.util.Arrays;
import java.util.Random;

class Test
{
    static Random random = new Random();

    public static void sample(double[] pdf) {
        ...
    }

    public static void main(String[] args) {
        double[] pdf = new double[] { 0.3, 0.4, 0.2, 0.1 };
        int[] counts = new int[pdf.length];
        final int tests = 1000000;
        for(int i = 0; i < tests; i++)
            counts[sample(pdf)]++;
        for(int i = 0; i < counts.length; i++)
            System.out.println(counts[i] / (double)tests);
    }
}

您可以看到我们得到的输出与使用的 PDF 非常相似:

0.3001356
0.399643
0.2001143
0.1001071

这是我运行每个版本时得到的时间:

  • 第一版:0m0.680s
  • 第二版:0m0.296s

【讨论】:

  • @fmbesteiro 我从我的一个项目中改编了它。是的,使用Arrays.binarySearch() 有一个错误。我已经修复了它,并添加了测试代码以确保它是随机的。我还添加了第二个更快的版本。
【解决方案2】:

使用 sample[i] 作为值数组的索引。

public static int[] withoutReplacement(int m, int n) {

    int[] perm = new int[n];
    for (int i = 0; i < n; i++) {
        perm[i] = i;
    }
    //take sample
    for (int i = 0; i < m; i++) {
        int r = i + (int) (Math.random() * (n - 1));
        int tmp = perm[i];
        perm[i] = perm[r];
        perm[r] = tmp;
    }
    int[] sample = new int[m];
    for (int i = 0; i < m; i++) {
        sample[i] = perm[i];
    }
    return sample;
}

【讨论】:

    猜你喜欢
    • 2022-01-06
    • 2016-03-25
    • 1970-01-01
    • 1970-01-01
    • 2023-03-21
    • 2012-01-01
    • 2020-12-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多