【问题标题】:Selecting percentage of random elements in a C++ map在 C++ 映射中选择随机元素的百分比
【发布时间】:2019-02-10 06:48:48
【问题描述】:

我有一张 C++ 地图:std::map <std::string, int>

我想从这张地图中选择 p 百分比的随机元素。这里 p 是动态的。例如,此地图中所有 Key:Value 对的 10% 或 30% 是随机选择的。无法使用 c++11。

最好的方法是什么?

谢谢。

【问题讨论】:

  • 请说明什么是随机元素的百分比
  • number_of_elements_to_pick = p*the_map.size()/100; ?你的 C++03 安装中有 <random> 吗?
  • 要求“最佳”总是在征求意见,因此您可能需要澄清这一点。

标签: c++ dictionary random c++03


【解决方案1】:
  • 将布尔向量初始化为与地图相同的大小
  • 计算T = map.size() * percentage
  • 将向量的前 T 个元素初始化为“真”,其余为假
  • 随机打乱向量中的元素
  • 同时迭代地图和矢量 - 当矢量中的相应索引位置为真时,在地图中指定一个项目

示例代码:

#include <iostream>
#include <map>
#include <vector>
#include <string>

using namespace std;

void getRandomMapElements(map<string, int>& items, double percentage)
{
    const size_t count = items.size();
    vector<bool> vec;
    vec.resize(count); // all items in vec are "false"

    if (percentage < 0)
    {
        percentage = 0;
    }
    else if (percentage > 1.0)
    {
        percentage = 1.0;
    }

    size_t target = (size_t)(count * percentage); // actual number of items extracted

    // fill up the first TARGET count elements of the vector with true, the rest are kept at false
    for (size_t i = 0; i < target; i++)
    {
        vec[i] = true;
    }

    // shuffle the boolean vector
    for (size_t i = 0; i < count; i++)
    {
        bool val = vec[i];
        size_t swap = rand() % count;
        vec[i] = vec[swap];
        vec[swap] = val;
    }

    // iterate over the vector and map together
    map<string, int>::iterator itor = items.begin();
    for (size_t i = 0; i < count; i++)
    {
        if (vec[i])
        {
            cout << itor->first << " : " << itor->second << endl;
        }
        itor++;
    }
}

【讨论】:

    【解决方案2】:

    使用 C++17 std::sample 完全可以满足您的需要,但对于 c++98,它会稍微复杂一些。

    与c++98兼容的最短代码是:

    unsigned pick_below(unsigned n)
    {
         // poor distribution:
         return std::rand() % n;
    }
    std::vector<std::pair<std::string, int> >
    sample(const std::map<std::string, int> & data_in,
           unsigned p)
    {
     std::vector<std::pair<std::string, int> > shuffled(data_in.begin(), data_in.end());
     for (unsigned i=shuffled.size()  ; i > 1 ; --i)
       std::swap(shuffled[i-1], shuffled[pick_below(i)]);
     shuffled.erase(shuffled.begin() +p, shuffled.end());
    }
    

    这段代码在两个层面上存在问题:

    1. std::random 质量无法保证。
    2. 使用modulo in pick_below distorts the distribution

    要解决问题 2,要么使用 boost::random::uniform_int_distribution,要么根据 this 重写 pick_below 函数:

    unsigned pick_below(unsigned n)
    {
        unsigned x;
        do {
           x = rand();
        } while (x >= (RAND_MAX - RAND_MAX % n));
        return x % n;
    }
    

    可以通过使用第三方随机生成器(例如 boost::random::mt19937)来解决问题 1。

    不幸的是,这个解决方案的复杂度平均为 O(n)(因为 pick_below 不能保证终止,但在任何值上 p &lt; RAND_MAX / 2 迭代它超过 K 次的概率呈指数下降到小于 0.5 K。复杂性不能比 O(n) 好,因为没有办法选择地图的第 akth 个元素,除非迭代所有元素。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-19
      • 2015-01-17
      相关资源
      最近更新 更多