Алгоритм объединения множеств, имеющих не менее 2 элементов

учитывая список наборов:

  • С_1 : [ 1, 2, 3, 4 ]
  • С_2 : [ 3, 4, 5, 6, 7 ]
  • S_3 : [ 8, 9, 10, 11 ]
  • С_4 : [ 1, 8, 12, 13 ]
  • S_5 : [ 6, 7, 14, 15, 16, 17 ]

какой наиболее эффективный способ объединить все наборы, которые разделяют по крайней мере 2 элемента? Я полагаю, что это похоже на проблему связанных компонентов. Таким образом, результат будет:

  • [ 1, 2, 3, 4, 5, 6, 7, 14, 15, 16, 17] (S_1 UNION S_2 UNION S_5)
  • [ 8, 9, 10, 11 ]
  • [ 1, 8, 12, 13 ] (s_4 разделяет 1 с S_1 и 8 С S_3, но не объединяется, потому что они разделяют только один элемент в каждом)

наивной реализацией является O (N^2), где N-число множеств, которое не работает для нас. Это должно быть эффективно для миллионов наборов.

5 ответов


Let there be a list of many Sets named (S)

Perform a pass through all elements of S, to determine the range (LOW .. HIGH).

Create an array of pointer to Set, of dimensions (LOW, HIGH), named (M).

do
    Init all elements of M to NULL.   

    Iterate though S, processing them one Set at a time, named (Si).

        Permutate all ordered pairs in Si. (P1, P2) where P1 <= P2.
        For each pair examine M(P1, P2)
            if M(P1, P2) is NULL
                Continue with the next pair.
            otherwise
                Merge Si, into the Set pointed to by, M(P1, P2).
                Remove Si from S, as it has been merged.
                Move on to processing Set S(i + 1)

        If Si was not merged, 
            Permutate again through Si
            For each pair, make M(P1, P2) point to Si.

while At least one set was merged during the pass.

моя голова говорит, что это порядок (2N ln N). Прими это с солью.


Если вы можете заказать элементы в наборе, вы можете посмотреть на использование Mergesort на наборы. Единственное изменение, необходимое для проверки дубликатов на этапе слияния. Если один из них найден, просто отбросьте дубликат. Поскольку mergesort - O(n*log(n)), это предложит imrpoved скорость по сравнению с наивным алгоритмом O (n^2).

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


одно замечание: это зависит от того, как часто это происходит. Если большинство пар множеств do share по крайней мере два элемента, это может быть наиболее эффективным, чтобы построить новый набор в то же время, как вы шагаете через сравнение, и выбросить его, если они не соответствуют условию. Если большинство пар не поделитесь по крайней мере двумя элементами, а затем отложите построение нового набора до подтверждения условия может быть более эффективным.


Я не вижу, как это можно сделать менее чем за O(n^2).

каждый набор необходимо сравнить с каждым другим, чтобы увидеть, содержат ли они 2 или более общих элементов. Это N * (n-1)/2 сравнения, поэтому O (n^2), даже если проверка общих элементов занимает постоянное время.

при сортировке наивной реализацией является O (n^2), но вы можете воспользоваться транзитивным характером упорядоченного сравнения (так, например, вы ничего не знаете в нижнем разделе quicksort необходимо сравнить с чем-либо в верхнем разделе, так как он уже сравнивался с pivot). Это то, что приводит к сортировке o (n * log n).

Это здесь не применяется. Поэтому, если в наборах нет чего-то особенного, что позволяет нам пропускать сравнения на основе результатов предыдущих сравнений, это будет O(n^2) в целом.

Павел.


Если ваши элементы численны по своей природе или могут быть естественно упорядочены (т. е. вы можете назначить значение, такое как 1, 2, 42 и т. д...), Я бы предложил использовать сортировку radix на Объединенных наборах и сделать второй проход, чтобы подобрать уникальные элементы.

этот алгоритм должен иметь O (n), и вы можете оптимизировать сортировку radix довольно много, используя побитовые операторы сдвига и битовые маски. Я сделал что-то подобное для проекта, над которым работал, и он работает как шарм.