我目前正在研究一个需要随机选择集合中元素的问题。每个元素都有一个与之相关的权重(选择概率)。
我的问题是,对于具有少量元素的集合,例如5-10,我所接受的解决方案的复杂性(运行时间)是可接受的,但是随着元素数量的增加,说1K或10K等,运行时间变得不可接受。
我目前的策略是:
对于大型集合和大量选择,此过程开始呈现二次行为,简而言之,是否有更快的方法?或许更好的算法?
答案 0 :(得分:16)
您想使用Walker算法。有了N个元素,就有了一个设置 O(N)的成本。但是,采样成本是O(1)。参见
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个元素;在这个集合中选择一个与重量成比例的。