




map<...> MyMap;
iterator item = MyMap.begin();
std::advance( item, random_0_to_n(MyMap.size()) );
Я еще не пробовал, но если работает, то выглядит отлично. Попробую, когда вернусь домой. какие #includes для этого требуются?
#include <map> & include <iterator> плюс вам нужно запустить свой собственный random_0_to_n ()
... и убедитесь, что ваш random_0_to_n () всегда <n :)
Если вы хотите пройти через элементы все карты, но случайным образом, рискнет ли этот метод повториться? В принципе, я хочу выбрать 1 замену без.
@mannyglover Хорошо, поэтому я на 1,5 года позже отвечу на это: в этом случае скопируйте элементы карты в вектор, а затем перетасуйте вектор.
Мне нравится ответ Джеймса, если карта маленькая или случайное значение не требуется очень часто. Если он большой и вы делаете это достаточно часто, чтобы важна скорость, вы могли бы сохранить отдельный вектор ключевых значений для выбора случайного значения.
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 ];
Конечно, если карта действительно огромна, вы не сможете хранить копии всех таких ключей. Если вы можете себе это позволить, вы получите преимущество поиска в логарифмическом времени.
Карта сейчас не очень большая, поэтому мне интересно сделать ее простым способом, но она может вырасти до большого размера. Тогда, возможно, стоит немного настроить класс карты, чтобы я мог выбирать случайный ключ таким образом, не сохраняя избыточный вектор. Есть ли способ сделать это уже?
Я не думаю, что есть хороший способ получить ключи карты, не прибегая к перемещению по карте в линейном времени, из-за того, что она хранится в виде дерева. Вы уверены, что карта - лучшая структура данных для ваших целей?
Думаю, мы можем немного ускорить это, посмотрите мой ответ.
Возможно, вам стоит рассмотреть Boost.MultiIndex, хотя обратите внимание, что он слишком тяжелый.
Возможно, составьте случайный ключ, а затем используйте нижняя граница, чтобы найти ближайший ключ, который действительно содержится.
Если ключи распределены равномерно, это может быть хорошей идеей. В противном случае вы бы получали один ключ чаще, чем другие.
Это действительно способ взять сэмпл из эмпирическое распределение сэмпла.
Вот случай, когда к элементам карты все необходимо обращаться в произвольном порядке.
В псевдокоде (он точно отражает следующую реализацию 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;
}
Продолжая тему предварительно сконструированных карт и быстрого случайного поиска 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) и векторный индекс для получения случайных пар за постоянное время. . Вы можете обернуть векторный / двоичный поиск, чтобы он выглядел как карта с функцией произвольного доступа.
Кто-нибудь пробовал это? https://github.com/mabdelazim/Random-Access-Map "Класс шаблона C++ для карты произвольного доступа. Это похоже на 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 случайный :)
Зачем тебе это нужно? Использование карты подразумевает, что вам нужен быстрый поиск на основе ключа, случайный поиск будет O (N) ...