具有std :: unordered_multimap等效键的元素的随机元素

时间:2014-05-09 08:50:12

标签: c++ random stl multimap bucket

如何从具有std::unordered_multimap等效键的元素组中选择随机元素?

这样做的惯用方法是什么?我知道我可以使用mmap.equal_range(key)获取给定键的元素范围。有没有办法使用这个范围作为均匀分布的界限?

3 个答案:

答案 0 :(得分:2)

  

std::unordered_multimap::count:返回键的元素数。

     

std::unordered_multimap::equal_range:返回包含容器中带有键的所有元素的范围。范围由两个迭代器定义,第一个指向所需范围的第一个元素,第二个指向范围的最后一个元素。

所以,很容易(我猜):

auto n = random(0, my_map.count(key) - 1);
auto val = std::next(my_map.equal_range(key).first, n)->second;

我在这里做的只是使用std::next推进迭代器。 random是您可能想要使用的更复杂的统一int分布的简写。

答案 1 :(得分:1)

您可以使用bucket成员函数来获取存储某个密钥的存储桶,然后只检查具有本地迭代器的那个存储桶:

T const & get_random(std::unordered_map<K, T> const & m)
{
    auto const b = m.bucket(k);
    // return random element from [m.begin(b), m.end(b))
}

答案 2 :(得分:1)

看看以下代码:

#include <iostream>
#include <unordered_map>
#include <string>
#include <random>

static std::mt19937_64 rng;

int main()
{
    std::unordered_multimap<std::string, std::string> fruits = {
        { "apple", "red" },
        { "apple", "green" },
        { "apple", "blue"},
        { "apple", "white"},
        { "apple" , "black"},
        { "orange", "orange" },
        { "strawberry", "red" }
    };
    std::random_device rd; // obtain a random number from hardware
    std::mt19937 eng(rd()); // seed the generator
    for (auto i(0); i < fruits.bucket_count(); ++i) {
        auto d = std::distance(fruits.begin(i), fruits.end(i));
        if (d > 0) {
          std::uniform_int_distribution<> distr(0, d - 1); // define the range
          auto r = distr(eng);
          std::cout << "random index = " << r << std::endl;
          auto elem = fruits.begin(i);
          std::advance(elem, r);
          std::cout << elem->first << " : " << elem->second << std::endl;
        }
    }

    return 0;
}

更新

更便携和通用的版本:

#include <iostream>
#include <unordered_map>
#include <string>
#include <random>

static std::mt19937_64 rng;

// Returns random iterator from an input range.
template<typename LIST_ITERATOR>
LIST_ITERATOR
getRandomIteratorFromRange(LIST_ITERATOR const &begin, 
                           LIST_ITERATOR const &end, 
                           std::mt19937 &random_engine)
{
  auto d = std::distance(begin, end);
  if(d > 0) {
    LIST_ITERATOR out(begin);
    std::advance(out, std::uniform_int_distribution<>(0, d - 1).operator()(random_engine));
    return out;
  }
  return end;
}

int main()
{
  std::unordered_multimap<std::string, std::string> fruits = {
  {"apple", "red"}, { "apple", "green" }, { "apple", "blue" }, { "apple", "white" },
  {"apple", "black"}, {"apple", "pink"},{"orange","orange"},{"strawberry", "red"}};

  std::random_device rd; // obtain a random number from hardware
  std::mt19937 eng(rd()); // seed the generator
  for(auto i(0); i < fruits.bucket_count(); ++i) {
    auto it = getRandomIteratorFromRange(fruits.begin(i), fruits.end(i), eng);
    if(it != fruits.end(i)) std::cout << it->first << " : " << it->second << std::endl;
  }

  return 0;
}