Meilleur algorithme pour la détection de cycles dans un graphe orienté
Ce qui est le plus efficace algorithme pour la détection de tous les cycles dans un graphe orienté?
J'ai un graphe orienté représentant un calendrier des travaux qui doivent être exécutés, d'un travail d'un nœud et d'une dépendance étant un avantage. J'ai besoin de détecter l'erreur le cas d'un cycle à l'intérieur de ce graphique conduisant à des dépendances cycliques.
- Vous dites que vous voulez détecter tous les cycles, mais votre cas d'utilisation suggère qu'il serait suffisante pour détecter s'il y a des cycles.
- Il serait préférable de détecter tous les cycles de sorte qu'ils pourraient être fixés en une fois, plutôt que de vérifier, corriger, vérifier, corriger etc.
- Vous devriez lire le livre "Trouver tous les circuits élémentaires d'un graphe orienté" par Donald B. Johnson. Il va trouver de l'élémentaire circuits, mais cela devrait être suffisant pour votre cas. Et voici mon Java mise en œuvre de cet algorithme prêt à l'emploi: github.com/1123/johnson
- Exécuter DFS avec d'autres modifications de l'algorithme: la marque de chaque nœud que vous avez visité. si vous visitez un nœud qui est déjà visités, alors vous avez un cicle. lorsque vous retraite à partir d'un chemin, enlever les nœuds qui sont visités.
- si vous visitez un nœud que vous avez déjà visités, il n'est pas nécessairement l'existence d'une boucle. Merci de lire mon commentaire cs.stackexchange.com/questions/9676/....
- Votre première phrase contredit votre dernière phrase; veuillez les corriger. Si vous voulez vraiment détecter tous les cycles (première phrase), votre pire des cas, la taille de sortie, et l'exécution, sera exponentielle par rapport à votre taille. Si vous voulez vraiment tout détecter l'erreur en cas de cycle (dernière phrase), vous pouvez le faire en temps linéaire en la taille de l'entrée. Je vous recommande le dernier.
- non, il ne serait pas forcément meilleur pour détecter tous les cycles. Considérer le cas où il y a un nombre exponentiel d'entre eux.
Vous devez vous connecter pour publier un commentaire.
Tarjan est fortement connecté composants de l'algorithme a
O(|E| + |V|)
temps de la complexité.Pour les autres algorithmes, voir Vivement les composants connectés sur Wikipédia.
O(|E| + |V|)
. L'utilisation de blanc (jamais visité), gris (nœud actuel est visité, mais accessible à tous les nœuds ne sont pas encore visité) et noir (tous accessibles nœuds sont visités avec l'actuel) le codage de la couleur, si un gris nœud trouve un autre gris nœud, puis nous avons un cycle. [À peu près ce que nous avons dans le Cormen l'algorithme du livre]. Vous vous demandez si 'l'algorithme de Tarjan' a aucun avantage sur ces DFS!!Étant donné que c'est un calendrier des travaux, je pense qu'à un certain moment vous allez sorte dans un projet d'ordonnance d'exécution.
Si c'est le cas, alors un tri topologique la mise en œuvre pourrait, en tout cas, de détecter les cycles. UNIX
tsort
n'est certainement. Je pense qu'il est probable que c'est par conséquent plus efficace pour détecter les cycles en même temps que tsorting, plutôt que dans une étape distincte.Donc, la question pourrait devenir, "comment puis-je la plus efficace de l'ordre de tsort", plutôt que "comment puis-je la plus efficace de détecter les boucles". La réponse est probablement "l'utilisation d'une bibliothèque", mais à défaut, à la suite de l'article de Wikipedia:
a le pseudo-code d'un algorithme, et une brève description de l'autre à partir de Tarjan. Les deux ont
O(|V| + |E|)
temps de la complexité.Commencer avec un DFS: un cycle existe si et seulement si un dos-bord est découvert au cours de DFS. C'est prouvé comme un résultat de blanc-chemin theorum.
La façon la plus simple de le faire est de faire un parcours en profondeur d'abord la traversée (DFT) du graphe.
Si le graphe a
n
sommets, c'est unO(n)
complexité temporelle de l'algorithme. Depuis que vous avez à faire une DFT à partir de chaque sommet, de la complexité devientO(n^2)
.Vous devez maintenir un pile contenant tous les sommets de la profondeur actuelle première traversée de, avec son premier élément du nœud racine. Si vous tombez sur un élément qui est déjà dans la pile lors de la DFT, alors vous avez un cycle.
O(n)
tout en vous suggérons de vérifier la pile pour voir si elle contient déjà visité nœud? La numérisation de la pile ajoute le temps deO(n)
l'exécution, car il a d'analyse de la pile sur chaque nouveau nœud. Vous pouvez obtenirO(n)
si vous marquez les nœuds visitésÀ mon avis, le plus compréhensible algorithme pour la détection de cycle dans un graphe orienté est le graphe-coloriage-algorithme.
Fondamentalement, la coloration de graphe d'algorithme parcourt le graphe dans un DFS manière (parcours en Profondeur d'Abord de Recherche, ce qui signifie qu'il explore un chemin complètement avant d'explorer une autre voie). Lorsqu'il trouve un bord arrière, il marque le graphique qui contient une boucle.
Pour une explication approfondie de la coloration de graphe d'algorithme, veuillez lire cet article: http://www.geeksforgeeks.org/detect-cycle-direct-graph-using-colors/
Aussi, j'fournir une implémentation de la coloration de graphe en JavaScript https://github.com/dexcodeinc/graph_algorithm.js/blob/master/graph_algorithm.js
Si vous ne pouvez pas ajouter un "visité" la propriété pour les nœuds, l'utilisation d'un ensemble (ou une carte) et ajoutez tous les nœuds visités à l'ensemble, sauf s'ils sont déjà dans le jeu. L'utilisation d'une clé unique ou de l'adresse des objets comme la "clé".
Cela vous donne aussi l'information sur la "racine" nœud de la dépendance cyclique qui va venir dans maniable quand un utilisateur doit résoudre le problème.
Une autre solution est d'essayer de trouver le prochain dépendance à exécuter. Pour cela, vous devez avoir une pile où vous pouvez vous souvenir où vous êtes maintenant, et ce que vous devez faire. Vérifier si une dépendance est déjà sur cette pile avant de l'exécuter. Si elle l'est, vous avez trouvé un cycle.
Même si cela peut sembler avoir une complexité de O(N*M) vous devez vous rappeler que la pile a une très faible profondeur (si N est petit) et que M est plus petit avec chaque dépendance que vous pouvez cocher que "exécuté" de plus, vous pouvez arrêter la recherche lorsque vous avez trouvé une feuille (de sorte que vous jamais vérifier chaque noeud -> M sera petite, trop).
Dans MetaMake, j'ai créé le graphique, sous forme d'une liste de listes, puis supprimés chaque nœud comme je l'ai exécuté qui, naturellement, de couper le volume de recherche. Je n'ai jamais vraiment eu à exécuter un contrôle indépendant, tout s'est fait automatiquement lors de l'exécution normale.
Si vous avez besoin d'un "test " seulement" mode, il suffit d'ajouter un "dry-run" drapeau qui désactive l'exécution de l'emploi.
Il n'y a pas d'algorithme permettant de trouver tous les cycles dans un graphe orienté en temps polynomial. Supposons que le graphe orienté a n nœuds et chaque paire de nœuds de connexions à l'autre ce qui signifie que vous avez un graphe complet. Ainsi, tout sous-ensemble non vide de ces n nœuds indique un cycle et il y a 2^n-1 nombre de ces sous-ensembles. Donc pas d'algorithme polynomial en temps existe.
Supposons donc que vous avez un efficace (pas stupide) de l'algorithme qui peut vous dire le nombre d'dirigé cycles dans un graphe, vous pouvez d'abord trouver le fort de composants connectés, puis en appliquant un algorithme sur ces composants connectés. Depuis les cycles n'existent que dans les composants et non pas entre eux.
D'après le Lemme 22.11 de Cormen et coll., Introduction aux Algorithmes (PLC):
Cela a été mentionné dans plusieurs réponses, là je vais également fournir un exemple de code basé sur le chapitre 22 des PLC. L'exemple de graphique est illustrée ci-dessous.
CLR " pseudo-code pour depth-first search lit:
Dans l'exemple de la CLR de la Figure 22.4, le graphique se compose de deux DFS arbres: l'un composé de nœuds u, v, x, et y, et l'autre de nœuds w et z. Chaque arbre contient un bord arrière: l'un de x à v et l'autre à partir de z à z (une auto-loop).
La clé de la réalisation, c'est que le bord est rencontré lorsque, dans le
DFS-VISIT
fonction, lors de l'itération sur les voisinsv
deu
, un nœud est rencontré avec leGRAY
couleur.Le code Python suivant est une adaptation des PLC' pseudo avec un
if
clause ajoutée qui détecte les cycles:Notez que dans cet exemple, le
time
en CLRS' pseudo n'est pas capturé, parce que nous sommes uniquement intéressés à la détection de cycles. Il y a aussi du code réutilisable pour la construction de la représentation des listes d'adjacence d'un graphe à partir d'une liste d'arêtes.Lorsque ce script est exécuté, il affiche la sortie suivante:
Ce sont exactement les bords arrières dans l'exemple de la CLR de la Figure 22.4.
J'avais mis en œuvre ce problème en sml ( programmation impérative) . Voici les grandes lignes . Trouver tous les nœuds qui ont un indegree ou outdegree de 0 . Ces nœuds ne peuvent pas faire partie d'un cycle ( donc les enlever ) . Ensuite retirez tous les entrants ou sortants bords de ces nœuds.
De manière récursive appliquer ce processus à le graphe obtenu. Si à la fin vous n'êtes pas de gauche avec un nœud ou un bord , le graphe n'a pas de cycles , autre chose, elle a.
Si DFS trouve un avantage à un déjà-sommet visité, vous avez un cycle.
La façon dont je le fais, c'est de faire un Tri Topologique, en comptant le nombre de sommets visités. Si ce nombre est inférieur au nombre total de sommets dans le groupe, vous avez un cycle.
https://mathoverflow.net/questions/16393/finding-a-cycle-of-fixed-length J'aime cette solution la meilleure spécialement pour les 4 longueur:)
Aussi phys assistant, dit-u doivent faire O(V^2). Je crois que nous avons besoin seulement O(V)/O(V+E).
Si le graphe est connecté, alors DFS va visiter tous les nœuds. Si le graphique est connecté sous graphes à chaque fois que nous organisons un DFS sur un sommet de ce sous-graphe, nous allons trouver les connectés des sommets et que vous n'avez pas à tenir compte de ces pour la prochaine course de la DFS. Par conséquent, la possibilité de courir pour chaque sommet est incorrect.
Comme vous l'avez dit, vous avez créé des emplois, il faut être exécutées dans un certain ordre.
Topological sort
donné ordre requis pour la planification de l'emploi(ou pour les problèmes de dépendance si c'est undirect acyclic graph
). Exécuterdfs
et de maintenir une liste, et commencer à ajouter le nœud dans le début de la liste, et si vous avez rencontré un nœud qui est déjà visités. Alors vous avez trouvé un cycle dans le graphique donné.Si un graphe satisfaire cette propriété
alors le graphe contient au moins sur le cycle.