【问题标题】:Random element from elements with equivalent keys of std::unordered_multimap来自具有 std::unordered_multimap 等效键的元素的随机元素
【发布时间】:2014-05-09 08:50:11
【问题描述】:

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

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

【问题讨论】:

  • equal_range 只会返回与key 匹配的元素。与key 相同的桶中可能还有其他元素,那么您想要一个匹配key 的随机元素,还是随机桶中的一个随机元素?
  • 我正在寻找一种从存储桶中检索随机元素的方法。我想从中提取的桶的键是已知的。
  • 听起来你仍然对桶是什么感到困惑,仅仅因为两个键的哈希值相等并不意味着键本身相等。桶可以持有不同的键。
  • 感谢您指出这一点 - 我相应地更改了问题的标题。

标签: c++ random stl multimap bucket


【解决方案1】:

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

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

所以,很容易(我猜):

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 分布的简写。

【讨论】:

  • 正是我想要的。
  • 这里有很多问题:mmap是系统调用,你应该使用[0;count)(不包括计数)范围内的值,第二个字符串必须是auto val = (std::next(mymap.equal_range(key).first, n))->second;
【解决方案2】:

您可以使用bucket成员函数获取存储某个key的bucket,然后使用本地迭代器只检查一个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))
}

【讨论】:

    【解决方案3】:

    看看下面这段代码:

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

    【讨论】:

    • 我喜欢更紧凑的版本更长。
    猜你喜欢
    • 1970-01-01
    • 2023-01-14
    • 2013-03-03
    • 2011-10-10
    • 2014-01-17
    • 2012-08-30
    • 2011-06-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多