有没有更快的方法来搜索累积分布?

时间:2014-05-30 06:21:51

标签: java algorithm random probability

我有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。因此,在使用二分查找时,停止标准仍然不明确。

如果有一个图书馆来帮助解决这类问题,我也会感兴趣。

2 个答案:

答案 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。当概率列表很短时,简单的线性扫描可能会变得和二进制搜索一样高效。

相关问题