选择C ++映射中随机元素的百分比

时间:2019-02-10 06:48:49

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

我有一个C ++映射:std::map <std::string, int>

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

最好的方法是什么?

谢谢。

2 个答案:

答案 0 :(得分:2)

  • 初始化布尔矢量,使其与地图的大小相同
  • 计算T = map.size() * percentage
  • 将向量的前T个元素初始化为“ true”,其余的则为false
  • 随机地将向量中的元素
  • 迭代器遍历地图和向量-当向量中对应的索引位置为true时,在地图上指定一个项

示例代码:

#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++;
    }
}

答案 1 :(得分:1)

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

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

Public currentTrack As Integer
Public temp As Integer
Public generic As Random = New Random()
temp = gen.Next(0, listTracks.Items.Count + 1)
        currentTrack = temp
        MessageBox.Show(listTracks.Items(currentTrack))
        AxWindowsMediaPlayer1.URL = listTracks.Items(currentTrack)

此代码在两个级别上都有问题:

  1. 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()); } 的质量无法得到保证。
  2. 使用modulo in pick_below distorts the distribution

要解决第2个问题,请使用std::random或根据this重写boost::random::uniform_int_distribution函数:

pick_below

修复问题1可以通过使用第三方随机生成器(例如unsigned pick_below(unsigned n) { unsigned x; do { x = rand(); } while (x >= (RAND_MAX - RAND_MAX % n)); return x % n; } )来解决。

不幸的是,此解决方案的复杂度平均为O(n)(因为无法保证boost::random::mt19937会终止,但是在任何值pick_below上迭代超过K次的概率都会呈指数减小小于0.5 K 。复杂度不能比O(n)好,因为没有办法选择映射的ak th 元素,除非迭代所有元素