从一组唯一值中选择一个唯一的随机子集

时间:2012-03-10 23:10:59

标签: c++ stl random

C ++。 Visual Studio 2010。

我有一个std::vector V的N个唯一元素(结构)。如何有效地从中挑选M个随机,独特的元素?

E.g。 V包含10个元素:{0,1,2,3,4,5,6,7,8,9}我挑了三个......

  • 4,0,9
  • 0,7,8
  • 但不是这个:0,5,5< ---不唯一!

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?

3 个答案:

答案 0 :(得分:27)

创建范围0, 1, ..., N - 1随机排列并选择其中的第一个M;将那些索引用作原始载体。

使用std::iotastd::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>