Я определяю unordered_map
следующим образом:
std::unordered_map<std::string, Edge> edges;
Существует ли эффективный способ выбора случайного края из краев неупорядоченного_мапа?
Я определяю unordered_map
следующим образом:
std::unordered_map<std::string, Edge> edges;
Существует ли эффективный способ выбора случайного края из краев неупорядоченного_мапа?
Решение до С++ 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()));
С++ 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
таким образом, который разрешает прямое назначение.
Существует ли эффективный способ выбора случайного края из краев неупорядоченного_мапа?
Если по эффективности вы имеете в виду O (1), то нет, это невозможно.
Так как итераторы, возвращаемые unordered_map::begin / end
, являются ForwardIterator
s, подходы, которые просто используют 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:)
Решение
std::unordered_map<std::string, Edge> edges;
auto random_it = std::next(std::begin(edges), rand_between(0, edges.size()));
очень медленно....
Гораздо более быстрое решение будет:
std::vector<std::string> vec
int index
в диапазоне от 0
до vec.size() - 1
edges[vec[index]]