Как определить, приводит ли добавление ребра к направленному графу к циклу?
Я ожидание-графики и мне интересно, есть ли эффективные алгоритмы для обнаружения, если добавление ребра к направленному графу приводит к циклу?
рассматриваемые графики изменчивы (они могут иметь добавленные или удаленные узлы и ребра). И мы не заинтересованы в том, чтобы действительно знать цикл нарушения, просто зная, что он есть (чтобы предотвратить добавление оскорбительного края).
конечно, можно использовать алгоритм для вычисления сильно связанные компоненты (например, Tarjan), чтобы проверить, является ли новый граф ациклическим или нет, но запуск его снова каждый раз, когда добавляется ребро, кажется довольно неэффективным.
4 ответов
Если я правильно понял ваш вопрос, то новое ребро (u,v) вставляется только в том случае, если раньше не было пути от v до u (т. е. если (u,v) не создает цикл). Таким образом, ваш граф всегда является DAG (направленный ациклический граф). Использование алгоритма Тарьяна для обнаружения сильно связанных компонентов (http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm) звучит как перебор в этом случае. Перед вставкой (u, v) все, что вам нужно проверить, есть ли является направленным путем от v К u, что можно сделать с помощью простого BFS/DFS.
таким образом, самый простой способ сделать это следующий (n = | V|, m = |E/):
- вставка (u, v): проверьте, есть ли путь от v до u (BFS/DFS). Сложность: O (m)
- удаление ребра: просто удалите их из графика. Сложность: O (1)
хотя вставка (u, v) занимает O (m) время в худшем случае, это, вероятно, довольно быстро в вашей ситуации. При выполнении BFS / DFS, начиная с v, чтобы проверить, достижим ли u, вы посещаете только вершины, которые достижимы из v. Я бы предположил, что в вашей настройке граф довольно разрежен и что количество вершин, достижимых другим, не так велико.
однако, если вы хотите улучшить теоретическое время работы, вот несколько подсказок (в основном показывая, что это будет не очень просто). Предположим, мы стремимся проверка в O(1) времени, существует ли направленный путь от v до u. Ключевым словом в этом контексте является транзитивное замыкание DAG (т. е. граф, содержащий ребро (u, v) тогда и только тогда, когда в DAG есть направленный путь от u до v). К сожалению, поддержание транзитивного закрытия в динамической настройке кажется не таким простым. Есть несколько документов, рассматривающих эту проблему, и все документы, которые я нашел, были документами STOC или FOC, что указывает на то, что они очень участвует. Самый новый (и самый быстрый) результат, который я нашел, находится в документе динамическое транзитивное замыкание через динамическую матрицу Inverse Санковского (http://dl.acm.org/citation.cfm?id=1033207).
даже если вы готовы понять один из этих динамических алгоритмов транзитивного закрытия (или даже хотите его реализовать), они не дадут вам никакой скорости по следующей причине. Эти алгоритмы предназначены для ситуации, когда у вас есть много запросов на подключение(которые затем могут быть выполнены за O (1) Время) и только несколько изменений в графике. Цель состоит в том, чтобы сделать эти изменения дешевле, чем пересчитать транзитивное закрытие. Однако это обновление все еще медленнее, чем одна проверка подключения. Таким образом, если вам нужно обновить каждый запрос подключения, лучше использовать простой подход, упомянутый выше.
Итак, почему я упоминаю этот подход поддержания транзитивного закрытия, если он не соответствует вашим потребностям? Ну, это показывает, что поиск алгоритма, потребляющего только O(1) время запроса, вероятно, не приведет вас к решению быстрее, чем простой с помощью BFS/DFS. Вы можете попробовать получить время запроса быстрее, чем O(m), но хуже, чем O(1), в то время как обновления также быстрее, чем O(m). Это очень интересная проблема, но мне кажется, что это очень амбициозная цель (поэтому, возможно, не тратьте слишком много времени на ее достижение..).
как предложил Марк, можно использовать структуру данных, которая хранит подключенные узлы. Лучше всего использовать boolean matrix |V|x|V|
. Значения могут быть инициализированы с помощью алгоритма Флойда-Уоршелла. Это делается в O(|V|^3)
.
пусть T(i)
быть набором вершин, есть путь к вершине i
и F(j)
набор вершин, где существует путь из вершины j
. Во-первых истинны в i
' - й ряд и вторая правда в 'ом.
добавление край (i,j)
- это простая операция. Если i
и j
не подключался раньше, чем для каждого a
С T(i)
и друг b
С F(j)
установить матричный элемент (a,b)
в true. Но операция стоит недешево. В худшем случае это O(|V|^2)
. То есть в случае направленной линии, а добавление ребра из конца в начало вершины делает все вершины связанными со всеми другими вершинами.
удаление краю (i,j)
не так просто, но не дороже операция в худшем случае : -) если есть путь от i
to j
после удаления края, чем ничего не меняется. Это проверено с Dijkstra, меньше, чем O(|V|^2)
. Вершины, которые больше не связаны, являются (a,b)
:
-
a
наT(i)
-i
-T(j)
, -
b
наF(j)
+j
только T(j)
изменяется при удалении edge (i,j)
, поэтому его нужно пересчитать. Это делается любым видом обхода графа (BFS, DFS), путем движение в противоположном направлении от вершины j
. Это делается менее чем O(|V|^2)
. Так как установка матричного элемента в худшем случае снова O(|V|^2)
, эта операция имеет ту же наихудшую сложность, что и добавление ребра.
Если график направлен, вам нужно будет только проверить родительские узлы (перейдите вверх, пока не достигнете корня) узла, где должно начаться новое ребро. Если один из родительских узлов равен концу ребра, добавление ребра создаст цикл.
Если все предыдущие задания находятся в топологически отсортированном порядке. Затем, если вы добавляете ребро, которое, кажется, тормозит сортировку и не может быть исправлено, у вас есть цикл.
https://stackoverflow.com/a/261621/831850
Так что если у нас есть отсортированный список узлов:
1, 2, 3, ..., x, ..., z, ...
Such that each node is waiting for nodes to its left.
скажем, мы хотим добавить ребро из x - >z. Ну, это, кажется, тормозит вид. Таким образом, мы можем переместить узел в x в положение z+1, которое исправит сортировку iif ни один из узлов (x, z] имейте ребро к узлу в x.