Как оптимально найти подмножества в наборе данных многие-ко-многим?

Есть два класса с отношением многие-ко-многим: напр. юзеры (U) и сообщества (C), в которых они состоят. Один юзер может состоять в нескольких сообществах, и в сообществе может быть несколько юзеров.

Мы знаем, кто в каком состоит. Можно представить данные в любом удобном виде. Пока остановился на таком:

c1: [u1, u2, u3],
c2: [u1, u2, u4],
c3: [u2, u3, u5, u6],
...

«Ядрами» называю отношения с, как минимум, двумя участниками с каждой стороны. В этом примере – два «ядра»:

[u1, u2] => [c1, c2],
[u2, u3] => [c1, c3],

Как найти все ядра среди довольно крупных данных?

Кроме перебора всех возможных комбинаций пока ничего не пришло в голову. Очень неэффективно на больших наборах (десятки тысяч с каждой стороны).
  • Вопрос задан
  • 369 просмотров
Решения вопроса 1
Mrrl
@Mrrl
Заводчик кардиганов
Есть простое решение за (число сообществ)*(число связей "пользователь-сообщество").

Для каждого сообщества Y:
- заводим массив R[C], где C - число сообществ
- для каждого пользователя X из сообщества Y:
- - для каждого сообщества M, в которое входит пользователь X: R[M]=R[M]+1
- для каждого сообщества M: если M!=Y и R[M] > 1, то пара (Y,M) - ядро.

Быстрее пока не получается.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 3
gbg
@gbg
Любые ответы на любые вопросы
Храните сами "ядра" в классической реляционной базе "многие ко многим". Три таблицы - пользователи, группы, связи.
строки в таблице связи: ид пользователя, ид группы.
Ответ написан
Fesor
@Fesor
Full-stack developer (Symfony, Angular)
Стоит подумать о том что бы преобразовать структуру в граф, что бы была связь не только между сообществом и пользователем но и наоборот. Так перебор будет уже эффективнее. Так же если данных много можно вооружиться neo4j
Ответ написан
@SeptiM
Для начала рассмотрим произвольный граф. Каждая вершина будет сообществом, а на каждое ребро посадим по два юзера. Юзер принадлежит сообществу, если его ребро касается соответствующей вершины. Для такого примера будет Omega(C^2) ядер, все различные. Это накладывает некоторые нижние оценки на алгоритм.

Тривиальный способ будет работать за O(C^2 U) + сортировка юзеров в каждом сообществе. Понятно, что сравниваем сообщества попарно, и ищем пересечение за линию по отсортированным спискам.

Можно улучшить алгоритм через минхэш (en.wikipedia.org/wiki/MinHash) заплатив точностью. Минхэш позволяет считать символ Жаккара -- размер пересечения двух множеств делить на размер объединения. Можно отсеять только крупные пересечения.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы