Подтвердить что ты не робот

Самый быстрый способ найти объединение множеств

У меня есть пары пар int like set<pair<int,int> > x1, x2, ... xn (n может быть от 2 до 20). Каков самый быстрый способ найти объединение этих множеств?

Извините Если бы я не прояснился с самого начала, я имел в виду быструю производительность, выделение памяти не проблема.

4b9b3361

Ответ 1

К сожалению, я считаю, что вы ограничены линейным решением O(N), так как весь союз будет представлять собой комбинацию элементов в обоих наборах.

template<typename S>
S union_sets(const S& s1, const S& s2)
{
     S result = s1;

     result.insert(s2.cbegin(), s2.cend());

     return result;
}

Ответ 2

Предполагая, что результат тоже должен быть набором, тогда у вас нет выбора, кроме как вставить каждый элемент из каждого x_i в этот результирующий набор. Таким образом, очевидная реализация:

set<pair<int,int>> x(x1);
x.insert(x2.begin(), x2.end());
// etc

Остается вопрос, может ли это быть избито для скорости.

Одноэлементный insert принимает подсказку position, которая при правильной скорости вставки. Поэтому может получиться, что что-то вроде этого быстрее, чем x.insert(x2.begin(), x2.end());:

auto pos = x.begin()
for (auto it = x2.begin(); it != x2.end(); ++it) {
    pos = x.insert(pos, *it);
}

Однако это зависит от данных: эта позиция может быть или не быть точным. Вы можете убедиться, что это, поместив все элементы в порядок, прежде чем вы начнете, для которого лучшим инструментом, вероятно, является set_union. Это можно назвать merge_and_dedupe_sorted_ranges, потому что то, что он делает, не имеет ничего общего с std::set. Вы могли бы либо set_union в промежуточные векторы, либо в такие как:

set<pair<int,int>> x;
set_union(x1.begin(), x1.end(), x2.begin(), x2.end(), inserter(x, x.end());

Моя проблема с использованием set_union заключается в том, что для того, чтобы получить преимущество добавления элементов в набор в возрастающем порядке, вам нужно создать новый пустой контейнер каждый раз, когда вы его вызываете (потому что, если он не пуст, добавленные элементы должны чередоваться с уже имеющимися значениями). Накладные расходы этих контейнеров могут быть выше, чем накладные расходы на вставку в набор в произвольном порядке: вам придется его протестировать.

Ответ 3

Сначала найдите объединение наименьших множеств. Это упорядочивает ваши наборы по заданной длине, вычисляет объединение двух наименьших множеств, удаляет эти множества, вставляет объединение в ваш список наборов по его размеру.

Если бы у вас было измерение того, насколько похожи два сета, то лучше всего сначала сначала найти объединение наиболее похожих наборов. Это предпочитает операции объединения, которые устраняют дубликаты раньше.

Изменить: И для каждой операции объединения между двумя наборами - объединить меньшее множество в большее множество.

Ответ 4

Я предполагаю, что с быстрым вы подразумеваете быстрое выполнение.

Затем: std:: set_union (*)

Пример для двух наборов:

#include <set>
#include <algorithm>
#include <iterator>
using namespace std;

int main () {
    set<pair<int,int> > a, b, uni;
    set_union (a.begin(), a.end(),
               b.begin(), b.end(),
               inserter(uni, uni.begin()));

}

для n наборов, рукописное письмо может быть наиболее удобным для обслуживания решением:

#include <set>
#include <vector>
using namespace std;

int main () {
    vector<set<pair<int,int>>> sets;
    set<pair<int,int>> uni;

    for (const auto &s : sets)
        for (const auto &elem : s)
            uni.insert (elem);
}

хотя в целом, следует отдать предпочтение стандартным алгоритмам и получать прибыль от их качественной реализации.

Если по быстрому вы подразумеваете производительность, мы не можем помочь, поскольку у нас нет требований. Различные подходы могут дать разные результаты для разных обстоятельств.


(*) note: сайт неодобрительно полагается на то, что он не был на 100% точным и стандартным

Ответ 5

Попробуйте set_union в алгоритме заголовка.

Ответ 6

Вы можете использовать std:: set_union рекурсивно или просто вставлять все наборы в результирующий набор (дублирующиеся элементы устраняются набором). Если количество элементов очень мало, вы можете попробовать вставить все это в вектор, отсортировать его и использовать std:: unique на векторе.

Ответ 7

Чтобы сохранить выделение памяти и улучшить локальность, было бы лучше использовать одну рабочую память vector<T>.

Построить a vector<T> и зарезервировать общее количество элементов во всех s (подсчет дубликатов). Затем, начиная с пустого диапазона [v.begin(), v.begin()), расширьте его до набора (уникальный, отсортированный) диапазон, добавив содержимое каждого набора, слияния и uniquifying:

vector<T> v;
v.reserve(<total size>);
for (set<T> &s: sets) {
    auto middle = v.insert(v.end(), s.begin(), s.end());
    inplace_merge(v.begin(), middle, v.end());
    v.erase(v.unique(v.begin(), v.end()), v.end());
}