【发布时间】:2019-08-21 21:45:27
【问题描述】:
我想在(封闭)范围[0, rnd_max] 内有效地生成唯一(非重复)整数的随机样本,范围内的每个数字都可以选择,并且每个数字都与样本权重相关联(权重越大,该数字被选中的可能性就越大,如果该数字尚未包含在样本中,则概率恰好是下一个选择 weight[i] / sum(weight[not_taken]))。
我看到C++有std::discrete_distribution可以生成随机加权整数,但是如果我用它来生成随机整数并丢弃重复的整数,当要取的样本相对于可能范围的长度很大时,就会有已经采集了许多不合格的样本,导致程序效率极低。我不清楚弗洛伊德的算法是否对样本权重的情况有一些扩展 (https://math.stackexchange.com/questions/178690/whats-the-proof-of-correctness-for-robert-floyds-algorithm-for-selecting-a-sin) - 我个人想不出一个。
也可以例如使用std::discrete_distribution 将权重降至零,或执行部分加权洗牌,如此答案:C++. Weighted std::shuffle - 但在该答案中,std::discrete_distribution 在每次迭代时重新生成,因此运行时间变为二次方(它需要循环遍历每次传递给它的权重)。
想知道什么是 C++ 中唯一整数的有效加权随机样本,它适用于不同的样本大小(例如,可用范围内从 1% 到 90% 的样本数)。
#include <vector>
#include <random>
#include <algorithm>
int main()
{
size_t rnd_max = 1e5;
size_t ntake = 1e3;
unsigned int seed = 12345;
std::mt19937 rng(seed);
std::gamma_distribution<double> rgamma(1.0, 1.0);
std::vector<double> weights(rnd_max);
for (double &w : weights) w = rgamma(rng);
std::vector<int> chosen_sample(ntake);
// sampler goes here...
return 0;
}
【问题讨论】:
-
我对 C++ 发行版不太熟悉,所以我不知道。我可以告诉你如何使用
uniform_distribution在O(n log^2 n)总时间(每次采样的log^2 n时间)中自己实现它。你感兴趣吗? -
如果它们“不重复”,那么它们就不是随机的!
-
@dyukha :是的,拜托,那也很棒。 @Adrian:是的,他们是:想象以下过程:从一个空集开始,然后使用
p[i] = {w[i] / sum(w[not taken]) if not taken, 0 otherwise}按顺序添加元素 - 结果是随机的非重复数字。