【问题标题】:Pick random element subset from map从地图中选择随机元素子集
【发布时间】:2020-01-19 19:07:59
【问题描述】:

我有一张元素地图:

   std::map<char,int> values;

   values['a']=10;
   values['b']=30;
   values['c']=50;
   values['d']=70;
   values['e']=90;
   values['f']=100;
   values['g']=120;

所以我需要从values 中选择 N 个元素作为配对映射(输出格式和输入格式)。

我尝试了 stackoverflow 的其他不同解决方案,但它们大多适用于矢量,不适用于任何类型的 C++ 11 容器,或者对我来说看起来太复杂了。

我需要一些比 random_shuffle 更有效的方法,它实际上会改变 C++ 容器。

如果这个函数适用于任何类型的 C++ 容器,那就太好了。

【问题讨论】:

标签: c++ algorithm dictionary c++11 random


【解决方案1】:

您可以将std::map&lt;char, int&gt; 的密钥复制到std::vector&lt;char&gt; 中。然后,用std::random_shuffleX 打乱这个向量。最后,返回映射的num 元素:其键是向量中最后一个键num 的元素:

std::vector<std::pair<char, int>> pick_random(const std::map<char, int>& m, size_t num)
{
   std::vector<char> keys;
   keys.reserve(m.size());

   // copy the map's keys    
   std::transform(m.begin(), m.end(), std::back_inserter(keys),
      [](const std::pair<const char, int>& p) {
         return p.first;
      }
   );

   // shuffle the keys
   std::random_shuffle(keys.begin(), keys.end());

   // number of elements to pick
   num = std::min(num, m.size());

   std::vector<std::pair<char, int>> res;
   res.reserve(num);

   // pick num elements
   std::generate_n(std::back_inserter(res), num,
      [&keys, &m]() {
         auto it = m.find(keys.back());
         keys.pop_back();
         return *it;
      }
   );

   return res;
}

这个想法是随机打乱包含键的向量中的元素(即keys)。因此,您将映射到映射中元素的键打乱。您使用这些随机打乱的键以随机方式从地图中获取元素。


Xstd::shuffle insted,因为std::random_shuffle 在 C++14 中已被弃用并在 C++17 中被删除。

【讨论】:

  • 仅供参考,std::random_shuffle 在 C++14 中已弃用,在 C++17 中完全删除。将 std::shuffle 与正确提供的 urng 参数一起使用。
  • 感谢您的解决方案。看起来这是唯一的选择
【解决方案2】:

std::sample 是 C++17,但您可以在 C++11 中轻松实现它。例如,对于前向迭代器(并且std::map 迭代器确实满足此要求),典型实现依赖于选择采样算法。其描述可在第 2 卷中找到。来自 Knuth 的 TAOCP,第 142 页。

最简单的实现:

template<class Forw_it, class Out_it, class URBG>
void sample(Forw_it first, Forw_it last, Out_it out, std::ptrdiff_t n, URBG&& g) {
    auto sz = std::distance(first, last);
    n = std::min(n, sz);

    while (n != 0) {
        std::uniform_int_distribution<std::ptrdiff_t> d(0, --sz);
        if (d(g) < n) {
            *out++ = *first;
            --n;
        }
        ++first;
    }
}

使用示例:

std::map<char, int> values;
// ... assign values ...

std::vector<std::pair<char, int>> vec;
sample(values.begin(), values.end(), std::back_inserter(vec), 4, std::mt19937{});

for (auto p : vec)
    std::cout << p.first << ' ' << p.second << '\n';

样本输出:

b 30
e 90
f 100
g 120

Full demo

【讨论】:

    猜你喜欢
    • 2010-09-12
    • 1970-01-01
    • 2014-04-21
    • 1970-01-01
    • 1970-01-01
    • 2023-02-20
    • 2010-09-08
    • 2012-08-30
    • 1970-01-01
    相关资源
    最近更新 更多