Ответ 1
map<...> MyMap;
iterator item = MyMap.begin();
std::advance( item, random_0_to_n(MyMap.size()) );
что такое хороший способ выбора случайного элемента с карты? С++. Насколько я понимаю, на картах нет итераторов с произвольным доступом. Ключ длинный, а карта малонаселенная.
map<...> MyMap;
iterator item = MyMap.begin();
std::advance( item, random_0_to_n(MyMap.size()) );
Мне нравится ответ Джеймса, если карта мала или вам не нужна случайная величина очень часто. Если он большой, и вы делаете это достаточно часто, чтобы сделать скорость важной, вы можете сохранить отдельный вектор значений ключа, чтобы выбрать случайное значение.
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 ];
Конечно, если карта действительно огромна, возможно, вы не сможете сохранить копию всех таких ключей. Если вы можете себе это позволить, хотя вы получаете преимущество поиска в логарифмическом времени.
Возможно, создайте случайный ключ, затем используйте lower_bound, чтобы найти самый близкий ключ, который действительно содержится.
Продолжая тему 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())];
Если ваша карта статична, то вместо карты используйте вектор для хранения пар ключ/значение в порядке очереди, двоичный поиск для поиска значений в log (n) времени, а векторный индекс для получения случайных пар в постоянное время. Вы можете обернуть векторный/двоичный поиск, чтобы он выглядел как карта со случайным доступом.
Возможно, вам стоит рассмотреть Boost.MultiIndex, хотя обратите внимание, что он немного слишком тяжелый.
Вот случай, когда все элементы карты должны иметь доступ в случайном порядке.
В псевдокоде (он точно отражает следующую реализацию на С++):
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
В С++:
#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;
}
Кто-нибудь пробовал это? https://github.com/mabdelazim/Random-Access-Map "Класс шаблона С++ для карты произвольного доступа. Это похоже на std:: map, но вы можете обращаться к элементам случайным по индексу с синтаксисом my_map.key(i) и my_map.data(i)"
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 является случайным :)