Лучший алгоритм обнаружения циклов в ориентированном графе

Каков наиболее эффективный алгоритм для обнаружения всех циклов в ориентированном графе?

У меня есть ориентированный граф, представляющий график заданий, которые должны быть выполнены, причем задание является node, а зависимость - ребро. Мне нужно обнаружить случай ошибки цикла внутри этого графика, приводящего к циклическим зависимостям.

Ответы

Ответ 2

Учитывая, что это расписание заданий, я подозреваю, что в какой-то момент вы собираетесь отсортировать их в предлагаемом порядке выполнения.

Если это так, то реализация topological sort может в любом случае обнаруживать циклы. UNIX tsort конечно. Я думаю, что вполне вероятно, что поэтому более эффективно обнаруживать циклы одновременно с сортировкой, а не на отдельном шаге.

Таким образом, может возникнуть вопрос: "Как я могу наиболее эффективно уничтожать", а не "как я наиболее эффективно обнаруживаю петли". На что ответ, вероятно, "использует библиотеку", но в противном случае следующая статья в Википедии:

http://en.wikipedia.org/wiki/Topological_sorting

имеет псевдокод для одного алгоритма и краткое описание другого из Тарьяна. Обе имеют сложность времени O(|V| + |E|).

Ответ 3

Начните с DFS: цикл существует тогда и только тогда, когда в DFS обнаружен обратный край. Это доказано в результате теоремы белого пути.

Ответ 4

Самый простой способ сделать это - сделать первый проход по глубине (DFT) графика.

Если граф имеет n вершины, это алгоритм временной сложности O(n). Поскольку вам, возможно, придется делать DFT, начиная с каждой вершины, общая сложность становится O(n^2).

Вы должны поддерживать стек, содержащий все вершины в первом прохождении текущей глубины, причем его первым элементом является корень node. Если вы сталкиваетесь с элементом, который уже находится в стеке во время DFT, у вас есть цикл.

Ответ 5

На мой взгляд, наиболее понятным алгоритмом для обнаружения цикла в ориентированном графе является алгоритм раскраски графа.

В принципе, алгоритм раскраски графа перемещает график по методу DFS (Depth First Search, что означает, что он полностью исследует путь до изучения другого пути). Когда он находит задний край, он отмечает график как содержащий цикл.

Подробное объяснение алгоритма раскраски графа читайте в этой статье: http://www.geeksforgeeks.org/detect-cycle-direct-graph-using-colors/

Кроме того, я предоставляю реализацию раскраски графа в JavaScript https://github.com/dexcodeinc/graph_algorithm.js/blob/master/graph_algorithm.js

Ответ 6

Если вы не можете добавить свойство "посещенный" к узлам, используйте набор (или карту) и просто добавьте все посещенные узлы в набор, если они уже не находятся в наборе. Используйте уникальный ключ или адрес объектов в качестве "ключа".

Это также дает вам информацию о "root" node циклической зависимости, которая пригодится, когда пользователь должен устранить проблему.

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

Хотя это может показаться сложным для O (N * M), вы должны помнить, что стек имеет очень ограниченную глубину (поэтому N мал) и что M становится меньше при каждой зависимости, которую вы можете проверить как "выполненный" плюс вы можете остановить поиск, когда вы нашли лист (так что вам никогда приходится проверять каждый node → M тоже).

В MetaMake я создал график как список списков, а затем удалил каждый node по мере их выполнения, что естественным образом сократило объем поиска. Мне никогда не приходилось запускать независимую проверку, все это происходило автоматически при нормальном выполнении.

Если вам нужен только режим "только тест", просто добавьте флаг "сухой", который отключит выполнение фактических заданий.

Ответ 7

Нет алгоритма, который может найти все циклы в ориентированном графе в полиномиальное время. Предположим, что ориентированный граф имеет n узлов, и каждая пара узлов имеет соединения друг с другом, что означает, что у вас есть полный граф. Таким образом, любое непустое подмножество этих n узлов указывает цикл и существует 2 ^ n-1 число таких подмножеств. Таким образом, алгоритм полиномиального времени не существует.   Итак, предположим, что у вас есть эффективный (недурный) алгоритм, который может рассказать вам количество направленных циклов в графе, вы можете сначала найти сильные связанные компоненты, а затем применить свой алгоритм к этим связанным компонентам. Поскольку циклы существуют только внутри компонентов, а не между ними.

Ответ 8

Я реализовал эту проблему в sml (императивное программирование). Вот контур. Найдите все узлы, которые имеют либо неопределенность, либо превышение 0. Такие узлы не могут быть частью цикла (поэтому удалите их). Затем удалите все входящие или исходящие ребра из таких узлов. Рекурсивно применить этот процесс к полученному графику. Если в конце вы не останетесь с каким-либо node или ребер, график не имеет циклов, иначе он имеет.

Ответ 9

Если DFS находит ребро, указывающее на уже посещенную вершину, у вас есть цикл.

Ответ 10

То, как я это делаю, это сделать топологическую сортировку, подсчитывая количество посещенных вершин. Если это число меньше общего числа вершин в DAG, у вас есть цикл.

Ответ 11

https://mathoverflow.net/questions/16393/finding-a-cycle-of-fixed-length Мне нравится это решение лучше всего для 4-х длин:)

Также физик-волшебник говорит, что вы должны делать O (V ^ 2). Я считаю, что нам нужно только O (V)/O (V + E). Если граф подключен, DFS будет посещать все узлы. Если граф связан с подграфами, то каждый раз, когда мы запускаем DFS в вершине этого подграфа, мы найдем связанные вершины и не будем рассматривать их для следующего прогона DFS. Поэтому вероятность запуска для каждой вершины неверна.

Ответ 12

Как вы сказали, у вас есть задание, оно должно выполняться в определенном порядке. Topological sort, учитывая требуемый порядок планирования заданий (или проблем с зависимостями, если это direct acyclic graph). Запустите dfs и сохраните список и начните добавлять node в начало списка, и если вы столкнулись с node, который уже посещен. Затем вы нашли цикл в заданном графике.

Ответ 13

//this is better solution in java- 

`

class Package{
    private List<Package> dep;
    private String name;
    boolean visited;
    List<Package> getDependencies(){
        return this.dep;
    }
    String getName(){}


     public void getBuildOrder(Package p){
         p.visited=true;
         if(p.getDependencies.size()==0) syso(p.getName());
        for( Package p1 : p.getDependencies){
            if(p1.visited) {
                syso("cyclic dependency");
                return;
            }
            getBuildOrder(p1);

        }

    }
     main(){
         Package p = new Package();
         // this p  i having all infor
         getBuildOrder(p);
     }
 }


`

Ответ 14

Если граф удовлетворяет этому свойству

|e| > |v| - 1

то график содержит, по крайней мере, цикл.