C ++。 Visual Studio 2010。
我有一个std::vector
V的N个唯一元素(重结构)。如何有效地从中挑选M个随机,独特的元素?
E.g。 V包含10个元素:{0,1,2,3,4,5,6,7,8,9}我挑了三个......
STL是首选。那么,这样的事情呢?
std::minstd_rand gen; // linear congruential engine??
std::uniform_int<int> unif(0, v.size() - 1);
gen.seed((unsigned int)time(NULL));
// ...?
// Or is there a good solution using std::random_shuffle for heavy objects?
答案 0 :(得分:27)
创建范围0, 1, ..., N - 1
的随机排列并选择其中的第一个M
;将那些索引用作原始载体。
使用std::iota
和std::random_shuffle
一起使用标准库可以轻松进行随机排列:
std::vector<Heavy> v; // given
std::vector<unsigned int> indices(V.size());
std::iota(indices.begin(), indices.end(), 0);
std::random_shuffle(indices.begin(), indices.end());
// use V[indices[0]], V[indices[1]], ..., V[indices[M-1]]
您可以为random_shuffle
提供您选择的随机数生成器;查看文档以获取详细信息。
答案 1 :(得分:10)
大多数情况下,Kerrek提供的方法就足够了。但是如果N非常大,并且M的数量级更小,则可能优选以下方法。
创建一组无符号整数,并在[0,N-1]范围内为其添加随机数,直到集合的大小为M.然后使用这些索引处的元素。
std::set<unsigned int> indices;
while (indices.size() < M)
indices.insert(RandInt(0,N-1));
答案 2 :(得分:2)
由于您希望它高效,我认为您可以获得摊销O(M)
,假设您必须执行该操作很多次。但是,这种方法不可重入。
首先创建static
(即std::vector<...>::size_type
会做)值的本地(即unsigned
)向量。
如果您输入了自己的功能,请调整矢量大小以匹配N
,并将其填充为旧尺寸的值N-1
:
static std::vector<unsigned> indices;
if (indices.size() < N) {
indices.reserve(N);
for (unsigned i = indices.size(); i < N; i++) {
indices.push_back(i);
}
}
然后,从该向量中随机选择M
个唯一数字:
std::vector<unsigned> result;
result.reserver(M);
for (unsigned i = 0; i < M; i++) {
unsigned const r = getRandomNumber(0,N-i); // random number < N-i
result.push_back(indices[r]);
indices[r] = indices[N-i-1];
indices[N-i-1] = r;
}
现在,您的结果位于result
向量中。
但是,您仍需要修改indices
的更改以进行下一次运行,以便indices
再次单调:
for (unsigned i = N-M; i < N; i++) {
// restore previously changed values
indices[indices[i]] = indices[i];
indices[i] = i;
}
但是这种方法只是有用,如果你必须经常运行那个算法而且N
不会变得那么大,以至于你不能忍受indices
一直占用RAM。< / p>