Jak mogę znaleźć (powtórzyć) WSZYSTKIE cykle na ukierunkowanym wykresie z / do danego węzła?
Na przykład chcę coś takiego:
A->B->A
A->B->C->A
ale nie: B-> C-> B
algorithm
graph-theory
graph-algorithm
użytkownik7305
źródło
źródło
Odpowiedzi:
Znalazłem tę stronę podczas wyszukiwania, a ponieważ cykle nie są takie same jak silnie połączone komponenty, szukałem dalej i wreszcie znalazłem skuteczny algorytm, który wyświetla wszystkie (elementarne) cykle ukierunkowanego wykresu. Pochodzi od Donalda B. Johnsona, a artykuł można znaleźć pod następującym linkiem:
http://www.cs.tufts.edu/comp/150GA/homeworks/hw1/Johnson%2075.PDF
Implementację Java można znaleźć w:
http://normalisiert.de/code/java/elementaryCycles.zip
Mathematica demonstracja Algorytm Johnsona można znaleźć tutaj , realizacja można pobrać z prawej strony ( „Pobierz kod autor” ).
Uwaga: w rzeczywistości istnieje wiele algorytmów dla tego problemu. Niektóre z nich są wymienione w tym artykule:
http://dx.doi.org/10.1137/0205007
Według artykułu algorytm Johnsona jest najszybszy.
źródło
A->B->C->A
też elementarne?simple_cycle
w networkx.Tu powinno działać pierwsze wyszukiwanie głębokości z cofaniem. Zachowaj tablicę wartości logicznych, aby śledzić, czy odwiedziłeś wcześniej węzeł. Jeśli zabraknie Ci nowych węzłów, do których chcesz się udać (bez uderzania w węzeł, w którym już byłeś), po prostu cofnij się i wypróbuj inną gałąź.
System plików DFS jest łatwy do wdrożenia, jeśli masz listę sąsiadów do reprezentowania wykresu. Na przykład przym. [A] = {B, C} wskazuje, że B i C są dziećmi A.
Na przykład pseudo-kod poniżej. „start” to węzeł, od którego zaczynasz.
Wywołaj powyższą funkcję z węzłem początkowym:
źródło
if (node == start):
- co jestnode and start
w pierwszym wezwaniustart
). Zaczyna się od tego wierzchołka i wykonuje DFS, dopóki nie wróci do tego wierzchołka, a następnie wie, że znalazł cykl. Ale tak naprawdę nie generuje cykli, tylko ich liczbę (ale modyfikowanie go w tym celu nie powinno być zbyt trudne).start
. Naprawdę nie musisz usuwać odwiedzanych flag, ponieważ każda odwiedzana flaga zostanie usunięta z powoduvisited[node]=NO;
. Pamiętaj jednak, że jeśli masz cyklA->B->C->A
, wykryjesz go 3 razy, podobnie jakstart
3 z nich. Jednym z pomysłów, aby temu zapobiec, jest kolejna odwiedzana tablica, w którejstart
ustawiony jest każdy węzeł, który był węzłem w pewnym momencie, a następnie nie odwiedzasz ich ponownie.Po pierwsze - tak naprawdę nie chcesz próbować znaleźć dosłownie wszystkich cykli, ponieważ jeśli jest 1, to jest ich nieskończona liczba. Na przykład ABA, ABABA itp. Lub może być możliwe połączenie 2 cykli w 8-podobny cykl itp. Itp. Znaczącym podejściem jest poszukiwanie wszystkich tak zwanych prostych cykli - tych, które się nie krzyżują, z wyjątkiem w punkcie początkowym / końcowym. Następnie, jeśli chcesz, możesz wygenerować kombinację prostych cykli.
Jednym z podstawowych algorytmów znajdowania wszystkich prostych cykli na ukierunkowanym wykresie jest: Przeprowadź najpierw głębokość wszystkich prostych ścieżek (tych, które się nie krzyżują) na wykresie. Za każdym razem, gdy bieżący węzeł ma następcę na stosie, wykrywany jest prosty cykl. Składa się z elementów na stosie, zaczynając od zidentyfikowanego następcy i kończąc na górze stosu. Pierwsze przejście głębokości wszystkich prostych ścieżek jest podobne do pierwszego wyszukiwania głębokości, ale nie oznacza się / nie zapisuje odwiedzonych węzłów innych niż aktualnie na stosie jako punktów zatrzymania.
Powyższy algorytm brutalnej siły jest wyjątkowo nieefektywny, a ponadto generuje wiele kopii cykli. Jest to jednak punkt wyjścia wielu praktycznych algorytmów, które stosują różne ulepszenia w celu poprawy wydajności i uniknięcia powielania cyklu. Byłem zaskoczony, gdy jakiś czas temu dowiedziałem się, że te algorytmy nie są łatwo dostępne w podręcznikach i Internecie. Zrobiłem więc badania i wdrożyłem 4 takie algorytmy i 1 algorytm dla cykli w niekierowanych grafach w bibliotece Java typu open source tutaj: http://code.google.com/p/niographs/ .
BTW, ponieważ wspomniałem o grafach bezkierunkowych: algorytm dla nich jest inny. Zbuduj drzewo rozpinające, a następnie każda krawędź, która nie jest częścią drzewa, tworzy prosty cykl wraz z pewnymi krawędziami w drzewie. Znalezione w ten sposób cykle tworzą tzw. Podstawę cyklu. Wszystkie proste cykle można następnie znaleźć, łącząc 2 lub więcej odrębnych cykli podstawowych. Aby uzyskać więcej informacji, patrz np. To: http://dspace.mit.edu/bitstream/handle/1721.1/68106/FTL_R_1982_07.pdf .
źródło
jgrapht
, który jest używany whttp://code.google.com/p/niographs/
was może wziąć przykład z github.com/jgrapht/jgrapht/wiki/DirectedGraphDemoNajprostszym wyborem, jaki znalazłem, aby rozwiązać ten problem, było użycie biblioteki python o nazwie
networkx
.Implementuje algorytm Johnsona wymieniony w najlepszej odpowiedzi na to pytanie, ale jego wykonanie jest dość proste.
W skrócie potrzebujesz:
Odpowiedź: [[„a”, „b”, „d”, „e”], [„a”, „b”, „c”]]
źródło
nx.DiGraph({'a': ['b'], 'b': ['c','d'], 'c': ['a'], 'd': ['e'], 'e':['a']})
W celu wyjaśnienia:
Silnie połączone komponenty znajdą wszystkie podgrafy, które mają co najmniej jeden cykl, a nie wszystkie możliwe cykle na wykresie. np. jeśli weźmiesz wszystkie silnie połączone komponenty i zwiniesz / grupujesz / scalisz każdy z nich w jeden węzeł (tj. węzeł na komponent), otrzymasz drzewo bez cykli (właściwie DAG). Każdy element (który jest zasadniczo podgraphem z co najmniej jednym cyklem) może zawierać o wiele więcej możliwych cykli wewnętrznie, więc SCC NIE znajdzie wszystkich możliwych cykli, znajdzie wszystkie możliwe grupy, które mają co najmniej jeden cykl, a jeśli zgrupujesz je, wtedy wykres nie będzie miał cykli.
aby znaleźć wszystkie proste cykle na wykresie, jak wspomniano inni, algorytm Johnsona jest kandydatem.
źródło
Raz otrzymałem to pytanie do wywiadu. Podejrzewam, że tak się stało. Przybywasz tutaj po pomoc. Podziel problem na trzy pytania, a stanie się to łatwiejsze.
Problem 1) Użyj wzorca iteratora, aby zapewnić sposób iteracji wyników trasy. Dobrym miejscem na umieszczenie logiki w celu uzyskania następnej trasy jest prawdopodobnie „moveNext” iteratora. Aby znaleźć prawidłową trasę, zależy to od struktury danych. Dla mnie była to tabela sql pełna ważnych możliwości tras, więc musiałem zbudować zapytanie, aby uzyskać prawidłowe miejsca docelowe podane jako źródło.
Problem 2) Wciśnij każdy węzeł, gdy znajdziesz je w kolekcję, gdy je dostaniesz, oznacza to, że możesz bardzo łatwo „podwoić się” o jeden punkt, sprawdzając kolekcję, którą budujesz w locie.
Problem 3) Jeśli w którymkolwiek momencie zauważysz, że podwajasz się, możesz usunąć rzeczy z kolekcji i „wykonać kopię zapasową”. Następnie od tego momentu spróbuj ponownie „przejść do przodu”.
Hack: jeśli używasz Sql Server 2008, istnieje kilka nowych „hierarchii” rzeczy, których możesz użyć, aby szybko rozwiązać ten problem, jeśli ustrukturyzujesz swoje dane w drzewie.
źródło
Warianty oparte na DFS z tylnymi krawędziami rzeczywiście znajdą cykle, ale w wielu przypadkach NIE będą minimalne cykle . Ogólnie DFS daje ci flagę informującą, że istnieje cykl, ale nie jest wystarczająco dobry, aby znaleźć cykle. Na przykład wyobraź sobie 5 różnych cykli dzielących dwie krawędzie. Nie ma prostego sposobu na identyfikację cykli przy użyciu samego DFS (w tym wariantów cofania).
Algorytm Johnsona faktycznie daje wszystkie unikalne proste cykle i ma dobrą złożoność czasu i przestrzeni.
Ale jeśli chcesz po prostu znaleźć cykle MINIMALNE (co oznacza, że może być więcej niż jeden cykl przechodzący przez dowolny wierzchołek, a my jesteśmy zainteresowani znalezieniem minimalnych cykli) ORAZ twój wykres nie jest bardzo duży, możesz spróbować użyć prostej metody poniżej. Jest to BARDZO proste, ale raczej powolne w porównaniu do Johnsona.
Więc jeden z absolutnie najłatwiejszych sposobów na znalezienie cykli MINIMALNYCH jest użycie algorytmu Floyda do znalezienia minimalnych ścieżek między wszystkimi wierzchołkami za pomocą macierzy przyległości. Algorytm ten nie jest tak optymalny jak Johnson, ale jest tak prosty, a jego wewnętrzna pętla jest tak ścisła, że w przypadku mniejszych wykresów (<= 50-100 węzłów) absolutnie sensowne jest jego użycie. Złożoność czasu wynosi O (n ^ 3), złożoność przestrzeni O (n ^ 2), jeśli korzystasz ze śledzenia rodzica, i O (1), jeśli nie. Przede wszystkim znajdźmy odpowiedź na pytanie, czy istnieje cykl. Algorytm jest bardzo prosty. Poniżej znajduje się fragment kodu w Scali.
Pierwotnie ten algorytm działa na wykresie ważonym, aby znaleźć wszystkie najkrótsze ścieżki między wszystkimi parami węzłów (stąd argument wag). Aby działało poprawnie, musisz podać 1, jeśli między węzłami istnieje ukierunkowana krawędź lub w przeciwnym razie NIE_EDGE. Po wykonaniu algorytmu można sprawdzić główną przekątną, jeśli istnieją wartości mniejsze niż NO_EDGE, niż ten węzeł uczestniczy w cyklu o długości równej wartości. Każdy inny węzeł tego samego cyklu będzie miał tę samą wartość (na głównej przekątnej).
Aby zrekonstruować sam cykl, musimy użyć nieco zmodyfikowanej wersji algorytmu ze śledzeniem rodziców.
Macierz rodzicielska początkowo powinna zawierać źródłowy indeks wierzchołków w komórce krawędzi, jeśli istnieje krawędź między wierzchołkami, a w przeciwnym razie -1. Po powrocie funkcji dla każdej krawędzi będziesz mieć odniesienie do węzła nadrzędnego w najkrótszym drzewie ścieżek. A potem łatwo jest odzyskać rzeczywiste cykle.
W sumie mamy następujący program, aby znaleźć wszystkie minimalne cykle
i mała główna metoda tylko po to, by przetestować wynik
i wynik jest
źródło
W przypadku niekierowanego wykresu niedawno opublikowany artykuł ( Optymalna lista cykli i ścieżek st w niekierowanych grafach ) oferuje optymalne asymptotycznie rozwiązanie. Możesz go przeczytać tutaj http://arxiv.org/abs/1205.2766 lub tutaj http://dl.acm.org/citation.cfm?id=2627951 Wiem, że to nie odpowiada na twoje pytanie, ale od tytułu Twoje pytanie nie wspomina o kierunku, może być nadal przydatne w wyszukiwarce Google
źródło
Zacznij od węzła X i sprawdź wszystkie węzły podrzędne (węzły nadrzędne i podrzędne są równoważne, jeśli nie są przekierowane). Zaznacz te węzły potomne jako dzieci X. Z dowolnego takiego węzła potomnego A zaznacz, że są dziećmi potomków A, X ', gdzie X' jest oznaczony jako 2 kroki dalej.). Jeśli później naciśniesz X i oznaczysz go jako dziecko X '', oznacza to, że X jest w cyklu 3-węzłowym. Cofanie do rodzica jest łatwe (tak jak jest, algorytm nie obsługuje tego, więc można znaleźć którykolwiek rodzic ma X ').
Uwaga: Jeśli wykres nie jest przekierowany lub ma jakiekolwiek dwukierunkowe krawędzie, algorytm ten staje się bardziej skomplikowany, zakładając, że nie chcesz przesuwać tej samej krawędzi dwa razy w cyklu.
źródło
Jeśli chcesz znaleźć wszystkie obwody elementarne na wykresie, możesz użyć algorytmu EC autorstwa JAMES C. TIERNAN, znalezionego na papierze od 1970 roku.
Bardzo oryginalny algorytm WE jak udało mi się wdrożyć go w php (nadzieja nie ma żadnych błędów znajduje się poniżej). Może również znaleźć pętle, jeśli takie istnieją. Obwody w tej implementacji (które próbują sklonować oryginał) są elementami niezerowymi. Zero tutaj oznacza nieistnienie (zero, jakie znamy).
Poza tym poniżej podano inną implementację, która daje algorytmowi większą niezależność, co oznacza, że węzły mogą zaczynać się z dowolnego miejsca, nawet od liczb ujemnych, np. -4, -3, -2, itp.
W obu przypadkach wymagane jest, aby węzły były sekwencyjne.
Być może będziesz musiał przestudiować oryginalny artykuł, James C. Tiernan Elementary Circuit Algorytm
to jest druga implementacja, bardziej niezależna od wykresu, bez goto i bez wartości tablic, zamiast tego używa kluczy tablic, ścieżka, wykres i obwody są przechowywane jako klucze tablic (użyj wartości tablic, jeśli chcesz, po prostu zmień wymagane linie). Przykładowy wykres zaczyna się od -4, aby pokazać jego niezależność.
Przeanalizowałem i udokumentowałem EC, ale niestety dokumentacja jest w języku greckim.
źródło
Istnieją dwa etapy (algorytmy) związane ze znalezieniem wszystkich cykli w DAG.
Pierwszym krokiem jest użycie algorytmu Tarjana do znalezienia zestawu silnie połączonych komponentów.
Drugim krokiem jest znalezienie cykli (ścieżek) w połączonych komponentach. Moją sugestią jest użycie zmodyfikowanej wersji algorytmu Hierholzera.
Chodzi o to:
Oto link do implementacji Java z przypadkiem testowym:
http://stones333.blogspot.com/2013/12/find-cycles-in-directed-graph-dag.html
źródło
http://www.me.utexas.edu/~bard/IP/Handouts/cycles.pdf
źródło
Natknąłem się na następujący algorytm, który wydaje się być bardziej wydajny niż algorytm Johnsona (przynajmniej w przypadku większych wykresów). Nie jestem jednak pewien jego wydajności w porównaniu z algorytmem Tarjana.
Dodatkowo, jak dotąd sprawdziłem tylko trójkąty. W razie zainteresowania zobacz „Arboricity and Subgraph Listing Algorytmy” Norishige Chiby i Takao Nishizeki ( http://dx.doi.org/10.1137/0214017 )
źródło
Rozwiązanie JavaScript wykorzystujące listy rozłączne połączone. Można zaktualizować do rozłącznych lasów, aby skrócić czas działania.
źródło
DFS z węzłów początkowych, śledź ścieżkę DFS podczas przechodzenia i zapisz ścieżkę, jeśli znajdziesz krawędź z węzła v w ścieżce do s. (v, s) jest back-edge w drzewie DFS, a zatem wskazuje cykl zawierający s.
źródło
Jeśli chodzi o pytanie dotyczące cyklu permutacji , przeczytaj więcej tutaj: https://www.codechef.com/problems/PCYCLE
Możesz wypróbować ten kod (wprowadź rozmiar i liczbę cyfr):
źródło
Wersja DFS c ++ dla pseudokodu w odpowiedzi drugiego piętra:
źródło