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