Эффективный алгоритм поиска транзитивного замыкания ориентированного ациклического графа

14

Я пытаюсь решить проблему с графиком (это не для домашней работы, просто для тренировки моих навыков). Дана DAG , где V - множество вершин, а E - ребра. Граф представлен в виде списка смежности, поэтому A v - это множество, содержащее все соединения v . Моя задача состоит в том, чтобы найти , какие вершины достижимы из каждой вершины об V . Решение, которое я использую, имеет сложность O ( V 3 )G(V,E)VEAvvvVO(V3), с транзитивным замыканием, но я читал, что в блоге это может быть быстрее, хотя он не показал, как. Может кто-нибудь сказать мне другой способ (с большей сложностью), чтобы решить проблему транзитивного замыкания в DAG?

Ронтогианнис Аристофанис
источник
Взгляните на: stackoverflow.com/questions/3517524/…
AJed
и это: cs.hut.fi/~enu/thesis.html
AJed
Тем не менее, я предлагаю сохранить . но попробуйте уменьшить количество сравнений в среднем. То есть угадывайте и добавляйте простые правила в свой алгоритм. Вы можете использовать матричное умножение - но если вы используете его для небольших графиков - тогда это просто беспорядок, и на самом деле ваш метод лучше. O(|V|3)
AJed
@AJed В этой конкретной задаче превысит ограничение по времени. О(В3)
Rontogiannis Aristofanis
2
@RondogiannisAristophanes Когда вы говорите об ограничении времени, вы имеете в виду, что это проблема в некоторых задачах программирования / алгоритма, таких как topcoder и т. Д.? Если это так, и если вы уверены, что правильно внедрили решение , вы, возможно, захотите еще раз взглянуть на проблему. Может быть какое-то другое скрытое свойство, которое может упростить вещи, или может быть лучший способ выразить проблему, чтобы транзитивное замыкание не требовалось. Потому что транзитивное замыкание так же сложно, как умножение матриц. Прочитайте student.cs.uwaterloo.ca/~cs466/Old_courses/F08/…O(V3)
Paresh

Ответы:

8

Тот факт, что наш граф является ациклическим, значительно упрощает эту проблему.

Топологическая сортировка может дать нам порядок вершин такой, что если i < j , то не существует ребра от v j до v i . Мы перечислили вершины так, что все ребра в нашем списке идут вперед.v1,v2,,vni<jvjvi

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

Теперь мы просто вернемся назад по этому списку, начиная с последней вершины . Транзитивное замыкание v n само по себе. Также добавьте v n к транзитивному замыканию каждой вершины с ребром в v n .vnvnvnvn

Для каждой другой вершины , идущей от конца назад, сначала добавьте v i к своему собственному транзитивному замыканию, затем добавьте все в транзитивном замыкании v i к транзитивному замыканию всех вершин с ребром к v i .vivivivi

Время выполнения в худшем случае с n числом вершин и m O ( n 2 ) числом ребер. Топологическая сортировка занимает время O ( n + m ) . Затем мы выполняем еще одну O ( m n ) работу в обратном проходе: когда мы идем назад по списку, для каждого ребра мы должны добавить до nO(n+m+nm)=O(n3)nmO(n2)O(n+m)O(mn)n вершины к чьему-то транзитивному замыканию.

Обратите внимание, что вы можете получить хорошее ускорение с постоянным коэффициентом, представляя транзитивное замыкание каждого с помощью битовых массивов. Скажем, у вас было только ; тогда вы бы использовали один 64-битный тип int, где бит i равен 1, если i находится в моем транзитивном замыкании, и 0 в противном случае. Тогда та часть , где мы добавляем все в я «s транзитивного замыкания на J » S очень быстро: Мы просто взять гр J | = с я . (Двоичная операция ИЛИ.)n=64iiijcjci

Для вам придется хранить их в массивах и выполнять некоторую арифметику, но это будет намного быстрее, чем набор объектов.n>64

OO(n3)

усул
источник
1
Скорее всего, вы могли бы использовать связное представление списка для наборов, и они в конечном итоге делятся общими хвостами.
Кайл Батт