【问题标题】:Random element in a map地图中的随机元素
【发布时间】:2010-09-14 14:27:51
【问题描述】:

从地图中选择随机元素的好方法是什么? C++。据我了解,地图没有随机访问迭代器。关键是很长很长,地图人口稀少。

【问题讨论】:

  • 为什么需要这样做?使用地图意味着您希望基于键快速查找,随机查找将是 O(N)...

标签: c++ random map


【解决方案1】:
map<...> MyMap;
iterator item = MyMap.begin();
std::advance( item, random_0_to_n(MyMap.size()) );

【讨论】:

  • 我还没有尝试过,但如果它有效,它看起来很完美。我回家时会试试的。这需要什么#includes?
  • #include & include 加上你必须滚动你自己的 random_0_to_n()
  • ...并确保您的 random_0_to_n() 始终为
  • 如果你想随机地遍历地图的所有元素,这种方法会不会有重复的风险?基本上,我想选择 1 个而不用替换。
  • @mannyglover 好的,所以我迟到了 1.5 年才回答这个问题:在这种情况下,将地图元素复制到向量中,然后随机播放向量。
【解决方案2】:

如果地图很小或者您不经常需要随机值,我喜欢 James 的回答。如果它很大并且您经常这样做以使速度变得重要,那么您可以保留一个单独的键值向量以从中选择一个随机值。

map<...> MyMap;
vector<...> MyVecOfKeys; // <-- add keys to this when added to the map.

map<...>::key_type key = MyVecOfKeys[ random_0_to_n(MyVecOfKeys.size()) ];
map<...>::data_type value = MyMap[ key ];

当然,如果地图真的很大,您可能无法像这样存储所有键的副本。如果您能负担得起,尽管您可以获得对数时间查找的优势。

【讨论】:

  • 地图现在不是很大,所以我有兴趣用简单的方法来做,但它可能会变得很大。然后可能值得稍微定制一下地图类,以允许我以这种方式选择一个随机键,而无需存储冗余向量。有没有办法做到这一点?
  • 我认为没有一种很好的方法可以在不诉诸于线性时间遍历地图的情况下获取地图的键,因为它被存储为一棵树。您确定地图是最适合您的数据结构吗?
  • 我认为我们可以加快速度,看看我的回答。
【解决方案3】:

可能会随机绘制一个密钥,然后使用lower_bound 找到实际包含的最接近的密钥。

【讨论】:

  • 如果密钥分布均匀,那可能是个好主意。如果不是,您最终会比其他人更频繁地获得一把钥匙。
  • 这确实是从样本的empirical distribution中采样的方式。
【解决方案4】:

继续 ryan_s 主题的预构建地图和快速随机查找:我们可以使用迭代器的并行地图代替向量,这应该会加快随机查找速度。

map<K, V> const original;
...

// construct index-keyed lookup map   
map<unsigned, map<K, V>::const_iterator> fast_random_lookup;
map<K, V>::const_iterator it = original.begin(), itEnd = original.end();
for (unsigned i = 0; it != itEnd; ++it, ++i) {
  fast_random_lookup[i] = it;
}

// lookup random value
V v = *fast_random_lookup[random_0_to_n(original.size())];

【讨论】:

    【解决方案5】:

    如果您的地图是静态的,那么不要使用地图,而是使用向量以键顺序存储您的键/值对,使用二进制搜索在 log(n) 时间内查找值,并使用向量索引来获取随机对在恒定时间内。您可以将矢量/二进制搜索包装起来,使其看起来像具有随机访问功能的地图。

    【讨论】:

      【解决方案6】:

      也许你应该考虑Boost.MultiIndex,但请注意它有点过于繁重。

      【讨论】:

        【解决方案7】:

        这是 所有 地图项必须以随机顺序访问的情况。

        1. 将地图复制到矢量。
        2. 洗牌向量。

        在伪代码中(它紧密地反映了以下 C++ 实现):

        import random
        import time
        
        # populate map by some stuff for testing
        m = dict((i*i, i) for i in range(3))
        # copy map to vector
        v = m.items()
        # seed PRNG   
        #   NOTE: this part is present only to reflect C++
        r = random.Random(time.clock()) 
        # shuffle vector      
        random.shuffle(v, r.random)
        # print randomized map elements
        for e in v:
            print "%s:%s" % e, 
        print
        

        在 C++ 中:

        #include <algorithm>
        #include <iostream>
        #include <map>
        #include <vector>
        
        #include <boost/date_time/posix_time/posix_time_types.hpp>
        #include <boost/foreach.hpp>
        #include <boost/random.hpp>
        
        int main()
        {
          using namespace std;
          using namespace boost;
          using namespace boost::posix_time;
        
          // populate map by some stuff for testing
          typedef map<long long, int> Map;
          Map m;
          for (int i = 0; i < 3; ++i)
            m[i * i] = i;
        
          // copy map to vector
        #ifndef OPERATE_ON_KEY
          typedef vector<pair<Map::key_type, Map::mapped_type> > Vector;
          Vector v(m.begin(), m.end());
        #else
          typedef vector<Map::key_type> Vector;
          Vector v;
          v.reserve(m.size());
          BOOST_FOREACH( Map::value_type p, m )
            v.push_back(p.first);
        #endif // OPERATE_ON_KEY
        
          // make PRNG
          ptime now(microsec_clock::local_time());
          ptime midnight(now.date());
          time_duration td = now - midnight;
          mt19937 gen(td.ticks()); // seed the generator with raw number of ticks
          random_number_generator<mt19937, 
            Vector::iterator::difference_type> rng(gen);
        
          // shuffle vector
          //   rng(n) must return a uniformly distributed integer in the range [0, n)
          random_shuffle(v.begin(), v.end(), rng);
        
          // print randomized map elements
          BOOST_FOREACH( Vector::value_type e, v )
        #ifndef OPERATE_ON_KEY
            cout << e.first << ":" << e.second << " ";
        #else
            cout << e << " ";
        #endif // OPERATE_ON_KEY
          cout << endl;
        }
        

        【讨论】:

          【解决方案8】:

          有人试过吗? https://github.com/mabdelazim/Random-Access-Map "用于随机访问映射的 C++ 模板类。这类似于 std::map 但您可以使用语法 my_map.key(i) 和 my_map.data(i) 按索引随机访问项目"

          【讨论】:

            【解决方案9】:
            std::random_device dev;
            std::mt19937_64 rng(dev());
            
            std::uniform_int_distribution<size_t> idDist(0, elements.size() - 1);
            auto elementId= elements.begin();
            std::advance(elementId, idDist(rng));
            

            现在 elementId 是随机的 :)

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2020-05-10
              • 1970-01-01
              • 2015-05-03
              • 2017-11-10
              • 1970-01-01
              • 2011-11-08
              • 2010-10-23
              相关资源
              最近更新 更多