Простой алгоритм перечисления всех циклов длины 3 неориентированного графа
1. Введение
Проблема перечисления всех элементов заданного типа в графе возникает при исследовании сетей различных видов. В статье [3], в частности, показано, каким образом алгоритмы подсчёта треугольных элементов графа могут быть использованы при определении Интернет-спама и в анализе характеристик пользователей социальной сети.2. Постановка задачи
Неориентированный граф G = (V, E) состоит из множества вершин V и множества связывающих их рёбер E. Одним из наиболее удобных видов представления графа является матрица смежности A, каждый элемент которой содержит число, определяющее наличие ребра между вершиной-строкой и вершиной-столбцом. Совокупность последовательно соединённых вершин графа, в которой первая и последняя вершины совпадают, называется циклом. Требуется найти алгоритм, эффективно перечисляющий все треугольники (циклы длины 3) некоторого графа.Пример графа, содержащего треугольники:
Рис. 1. Граф G
Множество рёбер E = [(1, 2), (1, 3), (2, 3), (1, 4), (3, 4), (2, 5), (3, 5), (3, 6), (4, 6), (5, 6)].
Данный граф содержит 5 треугольников, T = [(1, 2, 3), (1, 3, 4), (2, 3, 5), (3, 4, 6), (3, 5, 6)].
1 | 2 | 3 | 4 | 5 | 6 | |
1 | 0 | 1 | 1 | 1 | 0 | 0 |
2 | 1 | 0 | 1 | 0 | 1 | 0 |
3 | 1 | 1 | 0 | 1 | 1 | 1 |
4 | 1 | 0 | 1 | 0 | 0 | 1 |
5 | 0 | 1 | 1 | 0 | 0 | 1 |
6 | 0 | 0 | 1 | 1 | 1 | 0 |
Таблица 2. Матрица смежности A
3. Идея решения
Три вершины a, b и c графа G образуют треугольник, если в соответствующей матрице смежности ячейки A[a, b], A[b, c] и A[a, c] одновременно не равны нулю. Все возможные тройки вершин (a, b, c) составят тогда пространство поиска решений, удовлетворяющих данному условию.Для всех a от 0 до n: Для всех b от 0 до n: Для всех c от 0 до n: Если A[a, b], A[b, c] и A[a, c] не равны нулю, напечатать (a, b, c)Данный алгоритм, однако, имеет серьёзные недостатки. При выводе результатов каждый треугольник будет повторен 6 (3! перестановок) раз, а количество проверок условия существования треугольника составит T = n^3. Для нашего графа G: T = 6^3 = 216.
Можно значительно сузить пространство поиска, перебирая комбинации троек (a, b, c), упорядоченных, например, по возрастанию(a < b < c).
Для всех a от 0 до n: Для всех b от a + 1 до n: Для всех c от b + 1 до n: Если A[a, b], A[b, c] и A[a, c] не равны нулю, напечатать (a, b, c)Этот алгоритм выдаст точный результат, без дублей. Количество проверяемых комбинаций в нём равно числу сочетаний без повторений из n по 3, T = C(n, 3). Для графа G: T = 20.
4. Алгоритм A
Пространство поиска можно сократить ещё более, заметив, что в отсутствие некоторого ребра (a, b) нет необходимости перебирать варианты в соответствующем внутреннем цикле по c.Для всех a от 0 до n: Для всех b от a + 1 до n: Если A[a, b] = 0, переходим на следующий шаг цикла b Для всех c от b + 1 до n: Если A[b, c] и A[a, c] не равны нулю, напечатать (a, b, c)Быстродействие данного алгоритма зависит от количества присутствующих в графе рёбер, и значение T лишь в худшем случае (матрица A состоит из одних единиц) достигает показателей предыдущего варианта. Для графа G: T = 16.
5. Алгоритм B
Впрочем, даже такой алгоритм возможно улучшить для случая разрежённых больших графов. Чтобы это осуществить, наряду с матрицей смежности, понадобится дополнительная структура, список связи L. Номер вершины графа соответствует очередному элементу списка связи, который хранит в себе внутренний список вершин, соединённых с вершиной данного номера. Важно, что элементы списка связи упорядочены, в частности каждый следующий элемент внутреннего списка должен быть больше предыдущего, а начальный — больше соответствующего номера вершины в основном списке.Для всех a из L: Для всех b из L[a]: Для всех c из L[b]: Если A[a, c] не нуль, напечатать (a, b, c)Для графа G: T = 12.
6. Выводы
Алгоритм A отличается простотой реализации, не требует дополнительной памяти и успешно находит треугольники даже в несвязанных графах. Алгоритм B более эффективен, если есть возможность мириться с накладными расходами на создание списка связи.7. Пример реализации
Ниже представлен исходный код алгоритма A на языке Python.# Triangles enumeration algorithm(A) def triangles(g): n = len(g) for a in range(0, n): for b in range(a + 1, n): if not g[a][b]: continue for c in range(b + 1, n): if g[b][c] and g[a][c]: print(a + 1, b + 1, c + 1) g = [ [0, 1, 1, 1, 0, 0], [1, 0, 1, 0, 1, 0], [1, 1, 0, 1, 1, 1], [1, 0, 1, 0, 0, 1], [0, 1, 1, 0, 0, 1], [0, 0, 1, 1, 1, 0]] triangles(g)
Список литературы
[1] | Р. Миллер, Л. Боксер. “Последовательные и параллельные алгоритмы”. Бином. Лаборатория знаний, 2006 г. |
[2] | Альфред В. Ахо, Джон Хопкрофт, Джеффри Д. Ульман. “Структуры данных и алгоритмы”. Вильямс, 2000 г. |
[3] | “Semi-Streaming Algorithms for Local Triangle Counting in Massive Graphs”. http://aeolus.ceid.upatras.gr/scientific-reports/2nd_year_reports/comm.pdf |