Comment puis-je trouver (itérer sur) TOUS les cycles dans un graphe orienté depuis / vers un nœud donné?
Par exemple, je veux quelque chose comme ça:
A->B->A
A->B->C->A
mais pas: B-> C-> B
algorithm
graph-theory
graph-algorithm
user7305
la source
la source
Réponses:
J'ai trouvé cette page dans ma recherche et comme les cycles ne sont pas les mêmes que les composants fortement connectés, j'ai continué à chercher et finalement, j'ai trouvé un algorithme efficace qui répertorie tous les cycles (élémentaires) d'un graphe orienté. Il provient de Donald B. Johnson et l'article se trouve dans le lien suivant:
http://www.cs.tufts.edu/comp/150GA/homeworks/hw1/Johnson%2075.PDF
Une implémentation Java peut être trouvée dans:
http://normalisiert.de/code/java/elementaryCycles.zip
Une démonstration Mathematica de l'algorithme de Johnson peut être trouvée ici , l'implémentation peut être téléchargée à droite ( "Download author code" ).
Remarque: En fait, il existe de nombreux algorithmes pour ce problème. Certains d'entre eux sont répertoriés dans cet article:
http://dx.doi.org/10.1137/0205007
Selon l'article, l'algorithme de Johnson est le plus rapide.
la source
A->B->C->A
aussi élémentaire?simple_cycle
dans networkx.La première recherche approfondie avec retour arrière devrait fonctionner ici. Conservez un tableau de valeurs booléennes pour savoir si vous avez déjà visité un nœud auparavant. Si vous manquez de nouveaux nœuds vers lesquels aller (sans toucher un nœud que vous avez déjà été), alors revenez en arrière et essayez une autre branche.
Le DFS est facile à implémenter si vous avez une liste d'adjacence pour représenter le graphique. Par exemple, adj [A] = {B, C} indique que B et C sont les enfants de A.
Par exemple, pseudo-code ci-dessous. "start" est le nœud à partir duquel vous commencez.
Appelez la fonction ci-dessus avec le nœud de départ:
la source
if (node == start):
- ce qui estnode and start
dans le premier appelstart
). Il commence à ce sommet et effectue un DFS jusqu'à ce qu'il revienne à ce sommet, puis il sait qu'il a trouvé un cycle. Mais il ne produit pas réellement les cycles, juste un nombre d'entre eux (mais le modifier à la place ne devrait pas être trop difficile).start
. Vous n'avez pas vraiment besoin d'effacer les drapeaux visités car chaque drapeau visité sera effacé à cause devisited[node]=NO;
. Mais gardez à l'esprit que si vous avez un cycleA->B->C->A
, vous le détecterez 3 fois, tout commestart
3 d'entre eux. Une idée pour éviter cela est d'avoir un autre tableau visité où chaque nœud qui a été lestart
nœud à un moment donné est défini, puis vous ne les revisitez pas.Tout d'abord - vous ne voulez pas vraiment essayer de trouver littéralement tous les cycles car s'il y a 1, il y en a un nombre infini. Par exemple ABA, ABABA etc. Ou il peut être possible de réunir 2 cycles en un cycle semblable à 8 etc., etc ... L'approche significative est de rechercher tous les soi-disant cycles simples - ceux qui ne se croisent pas sauf dans le point de début / fin. Ensuite, si vous le souhaitez, vous pouvez générer des combinaisons de cycles simples.
L'un des algorithmes de base pour trouver tous les cycles simples dans un graphe orienté est le suivant: effectuez une traversée en profondeur d'abord de tous les chemins simples (ceux qui ne se croisent pas) dans le graphe. Chaque fois que le nœud actuel a un successeur sur la pile, un cycle simple est découvert. Il se compose des éléments de la pile commençant par le successeur identifié et se terminant par le haut de la pile. La première traversée en profondeur de tous les chemins simples est similaire à la première recherche en profondeur, mais vous ne marquez / n'enregistrez pas les nœuds visités autres que ceux actuellement sur la pile comme points d'arrêt.
L'algorithme de force brute ci-dessus est terriblement inefficace et en plus de cela génère plusieurs copies des cycles. C'est cependant le point de départ de multiples algorithmes pratiques qui appliquent diverses améliorations afin d'améliorer les performances et d'éviter la duplication de cycle. J'ai été surpris de découvrir il y a quelque temps que ces algorithmes ne sont pas facilement disponibles dans les manuels et sur le Web. J'ai donc fait quelques recherches et implémenté 4 algorithmes de ce type et 1 algorithme pour les cycles dans des graphiques non dirigés dans une bibliothèque Java open source ici: http://code.google.com/p/niographs/ .
BTW, puisque j'ai mentionné des graphiques non orientés: l'algorithme pour ceux-ci est différent. Construisez un arbre couvrant, puis chaque bord qui ne fait pas partie de l'arbre forme un cycle simple avec quelques bords dans l'arbre. Les cycles ainsi trouvés forment une base dite de cycle. Tous les cycles simples peuvent ensuite être trouvés en combinant 2 cycles de base distincts ou plus. Pour plus de détails, voir par exemple ceci: http://dspace.mit.edu/bitstream/handle/1721.1/68106/FTL_R_1982_07.pdf .
la source
jgrapht
ce qui est utilisé danshttp://code.google.com/p/niographs/
vous pouvez prendre un exemple de github.com/jgrapht/jgrapht/wiki/DirectedGraphDemoLe choix le plus simple que j'ai trouvé pour résoudre ce problème était d'utiliser la bibliothèque python appelée
networkx
.Il implémente l'algorithme de Johnson mentionné dans la meilleure réponse à cette question mais il est assez simple à exécuter.
En bref, vous avez besoin des éléments suivants:
Réponse: [['a', 'b', 'd', 'e'], ['a', 'b', 'c']]
la source
nx.DiGraph({'a': ['b'], 'b': ['c','d'], 'c': ['a'], 'd': ['e'], 'e':['a']})
Clarifier:
Les composants fortement connectés trouveront tous les sous-graphiques qui contiennent au moins un cycle, pas tous les cycles possibles dans le graphique. Par exemple, si vous prenez tous les composants fortement connectés et réduisez / regroupez / fusionnez chacun d'eux en un seul nœud (c'est-à-dire un nœud par composant), vous obtiendrez un arbre sans cycles (un DAG en fait). Chaque composant (qui est essentiellement un sous-graphique contenant au moins un cycle) peut contenir de nombreux autres cycles possibles en interne, donc SCC ne trouvera PAS tous les cycles possibles, il trouvera tous les groupes possibles qui ont au moins un cycle, et si vous regroupez eux, alors le graphique n'aura pas de cycles.
pour trouver tous les cycles simples dans un graphique, comme d'autres l'ont mentionné, l'algorithme de Johnson est un candidat.
la source
On m'a donné cela comme une question d'entrevue une fois, je soupçonne que cela vous est arrivé et vous venez ici pour obtenir de l'aide. Divisez le problème en trois questions et cela devient plus facile.
Problème 1) Utilisez le modèle d'itérateur pour fournir un moyen d'itérer les résultats de l'itinéraire. Un bon endroit pour mettre la logique pour obtenir le prochain itinéraire est probablement le "moveNext" de votre itérateur. Pour trouver un itinéraire valide, cela dépend de votre structure de données. Pour moi, c'était une table sql pleine de possibilités de routes valides, j'ai donc dû créer une requête pour obtenir les destinations valides en fonction d'une source.
Problème 2) Poussez chaque nœud au fur et à mesure que vous les trouvez dans une collection au fur et à mesure que vous les obtenez, cela signifie que vous pouvez voir si vous "doublez" un point très facilement en interrogeant la collection que vous construisez à la volée.
Problème 3) Si, à tout moment, vous constatez que vous doublez, vous pouvez retirer des éléments de la collection et "sauvegarder". Ensuite, à partir de là, essayez à nouveau de "progresser".
Hack: si vous utilisez Sql Server 2008, il existe de nouvelles "hiérarchies" que vous pouvez utiliser pour résoudre rapidement ce problème si vous structurez vos données dans une arborescence.
la source
Les variantes basées sur DFS avec des bords arrière trouveront effectivement des cycles, mais dans de nombreux cas, ce ne sera PAS minimal cycles . En général, DFS vous donne l'indicateur qu'il y a un cycle mais il n'est pas assez bon pour réellement trouver des cycles. Par exemple, imaginez 5 cycles différents partageant deux bords. Il n'y a pas de moyen simple d'identifier les cycles en utilisant uniquement DFS (y compris les variantes de retour en arrière).
L'algorithme de Johnson donne en effet tous les cycles simples uniques et a une bonne complexité temporelle et spatiale.
Mais si vous voulez simplement trouver des cycles MINIMAUX (ce qui signifie qu'il peut y avoir plus d'un cycle passant par n'importe quel sommet et que nous sommes intéressés à en trouver un minimum) ET que votre graphique n'est pas très grand, vous pouvez essayer d'utiliser la méthode simple ci-dessous. C'est TRÈS simple mais plutôt lent par rapport à Johnson.
Ainsi, l' un des tout à fait moyen le plus facile de trouver des cycles MINIMAL est d'utiliser l'algorithme de Floyd pour trouver des chemins minimaux entre tous les sommets en utilisant la matrice de contiguïté. Cet algorithme est loin d'être aussi optimal que celui de Johnson, mais il est si simple et sa boucle interne est si serrée que pour des graphiques plus petits (<= 50-100 nœuds), il est absolument logique de l'utiliser. La complexité temporelle est O (n ^ 3), la complexité spatiale O (n ^ 2) si vous utilisez le suivi des parents et O (1) si vous ne l'utilisez pas. Tout d'abord, trouvons la réponse à la question s'il y a un cycle. L'algorithme est extrêmement simple. Ci-dessous, un extrait de Scala.
À l'origine, cet algorithme fonctionne sur un graphe à bords pondérés pour trouver tous les chemins les plus courts entre toutes les paires de nœuds (d'où l'argument des poids). Pour que cela fonctionne correctement, vous devez fournir 1 s'il y a un bord dirigé entre les nœuds ou NO_EDGE sinon. Après l'exécution de l'algorithme, vous pouvez vérifier la diagonale principale, s'il existe des valeurs inférieures à NO_EDGE à ce que ce nœud participe à un cycle de longueur égale à la valeur. Tous les autres nœuds du même cycle auront la même valeur (sur la diagonale principale).
Pour reconstruire le cycle lui-même, nous devons utiliser une version légèrement modifiée de l'algorithme avec suivi des parents.
La matrice des parents doit initialement contenir l'index du sommet source dans une cellule de bord s'il y a un bord entre les sommets et -1 sinon. Après le retour de la fonction, pour chaque bord, vous aurez une référence au nœud parent dans l'arborescence du chemin le plus court. Et puis, il est facile de récupérer des cycles réels.
Dans l'ensemble, nous avons le programme suivant pour trouver tous les cycles minimaux
et une petite méthode principale juste pour tester le résultat
et la sortie est
la source
Dans le cas du graphe non orienté, un article récemment publié ( Liste optimale des cycles et des chemins st dans les graphes non orientés ) offre une solution asymptotiquement optimale. Vous pouvez le lire ici http://arxiv.org/abs/1205.2766 ou ici http://dl.acm.org/citation.cfm?id=2627951 Je sais que cela ne répond pas à votre question, mais depuis le titre de votre question ne mentionne pas la direction, elle pourrait quand même être utile pour la recherche Google
la source
Commencez au nœud X et recherchez tous les nœuds enfants (les nœuds parent et enfant sont équivalents s'ils ne sont pas dirigés). Marquez ces nœuds enfants comme étant des enfants de X. À partir d'un tel nœud enfant A, marquez ses enfants d'être des enfants de A, X ', où X' est marqué comme étant à 2 pas.). Si vous frappez plus tard X et le marquez comme étant un enfant de X '', cela signifie que X est dans un cycle à 3 nœuds. Le retour en arrière vers son parent est facile (en l'état, l'algorithme ne prend pas en charge cela, vous trouverez donc le parent qui a X ').
Remarque: Si le graphique n'est pas orienté ou a des bords bidirectionnels, cet algorithme devient plus compliqué, en supposant que vous ne voulez pas traverser le même bord deux fois pour un cycle.
la source
Si vous voulez trouver tous les circuits élémentaires dans un graphique, vous pouvez utiliser l'algorithme EC, de JAMES C.TIERNAN, trouvé sur un papier depuis 1970.
L' algorithme EC très original que j'ai réussi à implémenter en php (espérons qu'il n'y a pas d'erreur est montré ci-dessous). Il peut également trouver des boucles s'il y en a. Les circuits de cette implémentation (qui essaie de cloner l'original) sont les éléments non nuls. Zéro signifie ici non-existence (nul comme nous le savons).
En dehors de celle ci-dessous, une autre implémentation donne à l'algorithme plus d'indépendance, cela signifie que les nœuds peuvent commencer de n'importe où, même à partir de nombres négatifs, par exemple -4, -3, -2, etc. etc.
Dans les deux cas, il est nécessaire que les nœuds soient séquentiels.
Vous pourriez avoir besoin d'étudier l'article original, James C. Tiernan Elementary Circuit Algorithm
alors c'est l'autre implémentation, plus indépendante du graphe, sans goto et sans valeurs de tableau, à la place il utilise des clés de tableau, le chemin, le graphe et les circuits sont stockés sous forme de clés de tableau (utilisez des valeurs de tableau si vous le souhaitez, changez simplement le requis lignes). Le graphique d'exemple commence à -4 pour montrer son indépendance.
J'ai analysé et documenté la CE, mais malheureusement, la documentation est en grec.
la source
Il y a deux étapes (algorithmes) impliquées dans la recherche de tous les cycles dans un DAG.
La première étape consiste à utiliser l'algorithme de Tarjan pour trouver l'ensemble des composants fortement connectés.
La deuxième étape consiste à trouver des cycles (chemins) au sein des composants connectés. Ma suggestion est d'utiliser une version modifiée de l'algorithme de Hierholzer.
L'idée est:
Voici le lien vers une implémentation Java avec un cas de test:
http://stones333.blogspot.com/2013/12/find-cycles-in-directed-graph-dag.html
la source
http://www.me.utexas.edu/~bard/IP/Handouts/cycles.pdf
la source
Je suis tombé sur l'algorithme suivant qui semble être plus efficace que l'algorithme de Johnson (au moins pour les graphiques plus grands). Je ne suis cependant pas sûr de ses performances par rapport à l'algorithme de Tarjan.
De plus, je ne l'ai vérifié que pour les triangles jusqu'à présent. Si vous êtes intéressé, veuillez consulter «Algorithmes de listage d'arboricité et de sous-graphiques» de Norishige Chiba et Takao Nishizeki ( http://dx.doi.org/10.1137/0214017 )
la source
Solution Javascript utilisant des listes chaînées disjointes. Peut être mis à niveau pour séparer les forêts définies pour des temps d'exécution plus rapides.
la source
DFS à partir du nœud de départ s, gardez une trace du chemin DFS pendant la traversée et enregistrez le chemin si vous trouvez un bord du nœud v dans le chemin vers s. (v, s) est un bord arrière de l'arborescence DFS et indique donc un cycle contenant s.
la source
Concernant votre question sur le cycle de permutation , lisez plus ici: https://www.codechef.com/problems/PCYCLE
Vous pouvez essayer ce code (entrez la taille et le nombre de chiffres):
la source
Version DFS c ++ pour le pseudo-code dans la réponse du deuxième étage:
la source