Я определяю unordered_map
как это:
std::unordered_map<std::string, Edge> edges;
Есть ли эффективный способ выбрать случайное ребро из ребра unordered_map?
Решение до C ++ 11:
std::tr1::unordered_map<std::string, Edge> edges;
std::tr1::unordered_map<std::string, Edge>::iterator random_it = edges.begin();
std::advance(random_it, rand_between(0, edges.size()));
C ++ 11 и далее:
std::unordered_map<std::string, Edge> edges;
auto random_it = std::next(std::begin(edges), rand_between(0, edges.size()));
Функция, которая выбирает действительное случайное число, зависит от вашего выбора, но убедитесь, что она возвращает число в диапазоне [0 ; edges.size() - 1]
когда edges
не пусто
std::next
функция просто оборачивает std::advance
функционировать таким образом, что позволяет прямое назначение.
Есть ли эффективный способ выбрать случайное ребро из ребра unordered_map?
Если под эффективностью вы подразумеваете O (1), то нет, это невозможно.
Поскольку итераторы возвращены unordered_map::begin / end
являются ForwardIterator
с, подходы, которые просто используют std::advance
O (n) в количестве элементов.
Если ваше конкретное использование позволяет, вы можете обменять некоторую случайность на эффективность:
Вы можете выбрать случайный ведро (это можно получить в O (1)), а затем случайный элемент внутри этого сегмента.
int bucket, bucket_size;
do
{
bucket = rnd(edges.bucket_count());
}
while ( (bucket_size = edges.bucket_size(bucket)) == 0 );
auto element = std::next(edges.begin(bucket), rnd(bucket_size));
куда rnd(n)
возвращает случайное число в диапазоне [0, n).
На практике, если у вас есть приличный хэш, большинство блоков будет содержать ровно один элемент, в противном случае эта функция будет слегка отдавать предпочтение элементам, которые находятся в их сегментах.
Вот как вы можете получить случайный элемент с карты:
std::unordered_map<std::string, Edge> edges;
iterator item = edges.begin();
int random_index = rand() % edges.size();
std::advance(item, random_index);
Или посмотрите на этот ответ, который обеспечивает следующее решение:
std::unordered_map<std::string, Edge> edges;
iterator item = edges.begin();
std::advance( item, random_0_to_n(edges.size()) );
Строгое решение O (1) без ведер:
Однако есть ограничение: если вы хотите удалить элементы с карты, кроме случайного выбора, вам нужно исправить свой ключевой вектор, это требует O (n) при наивном подходе. Но все же есть способ получить производительность O (1): держите карту, которая сообщает вам, где находится ключ в векторе ключей, и обновляйте его с помощью swap 🙂