Pairwise Установить пересечение в Python

Если у меня есть переменное число множеств (пусть вызывается число n), у которых не более m элементов каждый, то какой наиболее эффективный способ вычисления попарных пересечений для всех пар множеств? Заметим, что это отличается от пересечения всех n наборов.

Например, если у меня есть следующие наборы:

A={"a","b","c"}
B={"c","d","e"}
C={"a","c","e"}

Я хочу найти:

intersect_AB={"c"}
intersect_BC={"c", "e"}
intersect_AC={"a", "c"}

Другим приемлемым форматом (если это станет проще) будет карта элементов в заданном наборе для наборов, содержащих этот же элемент. Например:

intersections_C={"a": {"A", "C"},
                 "c": {"A", "B", "C"}
                 "e": {"B", "C"}}

Я знаю, что одним из способов сделать это было бы создание словаря, сопоставляющего каждое значение в объединении всех n наборов с списком наборов, в которых оно происходит, и затем повторять все эти значения для создания списков, таких как intersections_C выше, но я не уверен, как это масштабируется с ростом n, а размеры набора становятся слишком большими.

Дополнительная дополнительная информация:

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

Ответы

Ответ 1

это должно делать то, что вы хотите

import random as RND
import string
import itertools as IT

mock некоторые данные

fnx = lambda: set(RND.sample(string.ascii_uppercase, 7))
S = [fnx() for c in range(5)]

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

idx = range(len(S))

получить все возможные уникальные пары элементов в S; однако, поскольку множество пересечений коммутативно, мы хотим комбинации, а не перестановки

pairs = IT.combinations(idx, 2)

написать функцию, выполняющую множество пересечений

nt = lambda a, b: S[a].intersection(S[b])

сбрасывает эту функцию по парам и выводит результат каждого вызова функции на его аргументы

res = dict([ (t, nt(*t)) for t in pairs ])

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

это решение, на самом деле это всего лишь две строки кода: (i) вычислить перестановки; (ii) затем примените некоторую функцию по каждой перестановке, сохранив возвращаемое значение в контейнере структурированного контейнера (ключ-значение)

объем памяти этого решения минимален, но вы можете сделать еще лучше, возвращая выражение генератора на последнем шаге, то есть

res = ( (t, nt(*t)) for t in pairs )

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

Ответ 2

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

Ответ 3

Как насчет использования метода пересечения множества. См. Ниже:

A={"a","b","c"}
B={"c","d","e"}
C={"a","c","e"}

intersect_AB = A.intersection(B)
intersect_BC = B.intersection(C)
intersect_AC = A.intersection(C)

print intersect_AB, intersect_BC, intersect_AC