【问题标题】:Pick a unique random subset from a set of unique values从一组唯一值中选择一个唯一的随机子集
【发布时间】:2012-03-27 21:41:02
【问题描述】:

C++。视觉工作室 2010。

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

例如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?

【问题讨论】:

  • 您对“独特”的定义通常称为“(绘图)无需替换”

标签: c++ stl random


【解决方案1】:

创建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 提供您选择的随机数生成器;详情请查看文档。

【讨论】:

  • 该死的,太快了!我可以在 8 分钟内接受答案,所以我有时间测试一下:)
  • 注意! std::random_suffle 在 C++14 中已弃用,在 C++17 中已被删除。相反,将 std::shuffle 与 C++11 及更高版本一起使用。此函数与 C++11 中大大改进的 RNG 功能配合得更好。
【解决方案2】:

大多数时候,Kerrek 提供的方法就足够了。但如果 N 很大,而 M 小几个数量级,则可能首选以下方法。

创建一组无符号整数,并在 [0,N-1] 范围内添加随机数,直到集合的大小为 M。然后使用这些索引处的元素。

std::set<unsigned int> indices;
while (indices.size() < M)
    indices.insert(RandInt(0,N-1));

【讨论】:

  • 不保证所需的“唯一性”(即一个值可以在indices 中出现多次)
  • @AndreHolzner:是的,它确实保证了唯一性。任何值都不能在 indices 中出现多次。 std::set 负责这一点。如果你尝试插入一个副本,它不会进去,并且集合的大小将保持不变。
  • 如果 M 非常接近 N,这是低效的。用 M=N=1000 运行它会产生可怕的结果。
  • @kritzikratzi:显然。第一段是否没有明确说明这种方法在哪些条件下更可取?
  • 在 C++11 中,std::unordered_set 对于性能来说是首选,因为顺序并不重要
【解决方案3】:

既然您希望它高效,我认为您可以获得摊销的O(M),假设您必须执行该操作很多次。但是,这种方法是不可重入的。

首先创建一个包含std::vector&lt;...&gt;::size_type(即unsigned)值的本地(即static)向量。

如果您输入您的函数,请调整向量的大小以匹配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 一直占用内存的情况下才有用。

【讨论】:

    猜你喜欢
    • 2011-03-01
    • 1970-01-01
    • 2016-05-09
    • 1970-01-01
    • 1970-01-01
    • 2018-02-16
    • 1970-01-01
    • 2019-01-20
    • 2012-08-20
    相关资源
    最近更新 更多