从非常大的值集中快速加权随机选择

时间:2011-05-19 00:42:51

标签: c++ random selection complexity-theory probability

我目前正在研究一个需要随机选择集合中元素的问题。每个元素都有一个与之相关的权重(选择概率)。

我的问题是,对于具有少量元素的集合,例如5-10,我所接受的解决方案的复杂性(运行时间)是可接受的,但是随着元素数量的增加,说1K或10K等,运行时间变得不可接受。

我目前的策略是:

  1. 选择范围为[0,1)
  2. 的随机值X.
  3. 迭代元素,将它们的权重相加,直到总和大于X
  4. 选择并返回导致总和超过X的元素
  5. 对于大型集合和大量选择,此过程开始呈现二次行为,简而言之,是否有更快的方法?或许更好的算法?

3 个答案:

答案 0 :(得分:16)

您想使用Walker算法。有了N个元素,就有了一个设置 O(N)的成本。但是,采样成本是O(1)。参见

  • 甲。 J. Walker,一种有效的生成方法 离散随机变量和一般分布,ACM TOMS 3,253-256 (1977)。
  • Knuth,TAOCP,Vol 2,Sec 3.4.1.A。

a RandomLib的RandomSelect类 实现这个算法。

答案 1 :(得分:12)

假设元素权重是固定的,您可以使用预先计算的和。这就像直接使用累积概率函数,而不是密度函数。

然后可以将查找实现为二进制搜索,因此在元素数量中为log(N)。

二进制搜索显然需要random_access到权重的容器。

或者,使用std::map<>upper_bound()方法。

#include <iostream>
#include <map>
#include <stdlib.h>

int main ()
{
  std::map<double, char> cumulative;
  typedef std::map<double, char>::iterator It;

  cumulative[.20]='a';
  cumulative[.30]='b';
  cumulative[.40]='c';
  cumulative[.80]='d';
  cumulative[1.00]='e';

  const int numTests = 10;
  for(int i = 0;
      i != numTests;
      ++i)
  {
      double linear = rand()*1.0/RAND_MAX;  
      std::cout << linear << "\t" << cumulative.upper_bound(linear)->second << std::endl;
  }

  return 0;
}

答案 2 :(得分:1)

如果你有足够快的方法可以统一采样随机元素,你可以使用拒绝采样;所有你需要知道的是最大重量。它的工作原理如下:假设最大权重为M.在[0,1]中统一选取数字X.重复取样元素,直到找到重量至少为M * X的元素;选择这一个。

或者,近似解:随机均匀地挑选100个元素;在这个集合中选择一个与重量成比例的。