这是通常的做法:
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