我有List<Double>
,其中包含对项目进行抽样的概率(权重)。例如,List
包含5个值,如下所示。
0.1,0.4,0.2,0.1,0.2
每个第i个Double
值是对另一个List<Object>
的第i个项目进行采样的概率。
如何根据这些概率构建算法来执行采样?
我试过这样的事情,在那里我首先将概率列表变成累积形式。
0.1,0.5,0.7,0.8,1.0
然后我的方法如下。我生成一个随机的double,并遍历列表以找到大于random double的第一个项,然后返回它的索引。
Random r = new Random();
double p = r.nextDouble();
int total = list.size();
for(int i=0; i < total; i++) {
double d = list.get(i);
if(d > p) {
return i;
}
}
return total-1;
这种方法很慢,因为我按顺序爬过列表。实际上,我的列表中有800,000个项目与我需要采样的权重(概率)相关联。所以,不用说,这种顺序方法很慢。
我不确定二进制搜索如何提供帮助。假设我生成了p = 0.01。然后,二进制搜索可以使用列表中的递归。
compare 0.01 to 0.7, repeat with L = 0.1, 0.5 compare 0.01 to 0.1, stop compare 0.01 to 0.5, stop
0.01小于0.7,0.5和0.1,但我显然只想要0.1。因此,在使用二分查找时,停止标准仍然不明确。
如果有一个图书馆来帮助解决这类问题,我也会感兴趣。
答案 0 :(得分:2)
这不是最节省内存的方法,但使用NavigableMap,其中累积列表的值是关键。然后你可以使用floorEntry(randon.nextDouble())
。像二进制搜索一样,它是log(n)空间和n存储器。
因此...
NavigableMap<Double, Object> pdf = new TreeMap<>();
pdf.put(0.0, "foo");
pdf.put(0.1, "bar");
pdf.put(0.5, "baz");
pdf.put(0.7, "quz");
pdf.put(0.8, "quuz");
Random random = new Random();
pdf.floorEntry(random.nextDouble()).getValue();
答案 1 :(得分:2)
以下是使用二进制搜索的方法,从累积概率开始:
public static void main (String[] args) {
double[] cdf = {0.1, 0.5, 0.7, 0.8, 1.0};
double random = 0.75; // generate randomly between zero and one
int el = Arrays.binarySearch(cdf, random);
if (el < 0) {
el = -(el + 1);
}
System.out.println(el);
}
P.S。当概率列表很短时,简单的线性扫描可能会变得和二进制搜索一样高效。