У меня есть коллекция std::set
, Я хочу найти пересечение всех наборов в этой коллекции, самым быстрым способом. Количество наборов в коллекции обычно очень мало (~ 5-10), а количество элементов в каждом наборе обычно меньше 1000, но иногда может доходить до 10000. Но мне нужно сделать эти пересечения десятками тысячи раз, как можно быстрее. Я попытался сравнить несколько методов следующим образом:
std::set
объект, который изначально копирует первый набор. Затем для последующих наборов он перебирает все свои элементы и i-й набор коллекции и удаляет элементы из себя по мере необходимости.std::set_intersection
во временный std::set
, поменяйте содержимое на текущий набор, затем снова найдите пересечение текущего набора со следующим набором и вставьте во временный набор, и так далее.vector
в качестве контейнера назначения вместо std::set
,std::list
вместо vector
подозревая list
обеспечит более быстрое удаление из середины.std::unordered_set
) и проверка всех предметов во всех наборах.Как оказалось, используя vector
незначительно быстрее, когда число элементов в каждом наборе мало, и list
немного быстрее для больших наборов. Использование на месте set
существенно медленнее, чем оба, а затем set_intersection
и хэш-наборы. Существует ли более быстрый алгоритм / структура данных / приемы для достижения этой цели? Я могу опубликовать фрагменты кода, если требуется. Спасибо!
Вы можете попробовать обобщение std::set_intersection()
алгоритм использует итераторы для всех множеств:
end()
его соответствующего набора, вы сделали. Таким образом, можно предположить, что все итераторы верны.x
,std::find_if()
первый элемент по крайней мере такой же большой, как x
,x
сделайте это новым значением кандидата и ищите снова в последовательности итераторов.x
Вы нашли элемент пересечения: запишите его, увеличьте все итераторы, начните заново.Ночь — хороший советник, и я думаю, что у меня есть идея;)
Вот почему скорость имеет значение, vector
(или возможно deque
) такие замечательные структуры: они очень хорошо играют с памятью. Поэтому я определенно рекомендую использовать vector
как наши посреднические структуры; хотя необходимо соблюдать осторожность, чтобы только когда-либо вставлять / удалять из конечности, чтобы избежать перемещения.
Поэтому я подумал о довольно простом подходе:
#include <cassert>
#include <algorithm>
#include <set>
#include <vector>
// Do not call this method if you have a single set...
// And the pointers better not be null either!
std::vector<int> intersect(std::vector< std::set<int> const* > const& sets) {
for (auto s: sets) { assert(s && "I said no null pointer"); }
std::vector<int> result; // only return this one, for NRVO to kick in
// 0. Check obvious cases
if (sets.empty()) { return result; }
if (sets.size() == 1) {
result.assign(sets.front()->begin(), sets.front()->end());
return result;
}// 1. Merge first two sets in the result
std::set_intersection(sets[0]->begin(), sets[0]->end(),
sets[1]->begin(), sets[1]->end(),
std::back_inserter(result));
if (sets.size() == 2) { return result; }// 2. Merge consecutive sets with result into buffer, then swap them around
// so that the "result" is always in result at the end of the loop.
std::vector<int> buffer; // outside the loop so that we reuse its memory
for (size_t i = 2; i < sets.size(); ++i) {
buffer.clear();
std::set_intersection(result.begin(), result.end(),
sets[i]->begin(), sets[i]->end(),
std::back_inserter(buffer));
swap(result, buffer);
}
return result;
}
Похоже на то правильный, Я не могу гарантировать его скорость, хотя, очевидно.