Skojarzenia...
Ten numer Delty jest zdominowany przez tematykę skojarzeń w grafach. Dla przypomnienia: graf nieskierowany to zbiór wierzchołków połączonych krawędziami, skojarzenie zaś w tym grafie to taki podzbiór krawędzi że każdy wierzchołek grafu jest incydentny z co najwyżej jedną krawędzią z
W wielu zastosowaniach interesować nas będzie wyznaczenie najliczniejszego skojarzenia w grafie, tzn. skojarzenia, które zawiera możliwie najwięcej krawędzi. W tym artykule pokażemy, jak znajdować takie skojarzenia.
Nasze algorytmy będą działały metodą przyrostową: dla danego grafu i wyróżnionego w nim skojarzenia będą znajdować skojarzenie o jedną krawędź większe bądź też będą stwierdzać, że jest już najliczniejszym skojarzeniem. W jaki sposób można powiększyć skojarzenie? Zauważmy, że jeśli w grafie istnieją dwa nieskojarzone wierzchołki połączone krawędzią, to można tę krawędź dodać do skojarzenia. Pomysł ten można uogólnić: jeśli w grafie istnieją dwa nieskojarzone wierzchołki połączone ścieżką, na której dokładnie co druga krawędź jest skojarzona, tzn. ścieżką
to można wyrzucić ze skojarzenia krawędzi dla nieparzystego i dorzucić do niego krawędzi dla parzystego (Rys. 2). Taką ścieżkę, wzdłuż której powiększamy skojarzenie, nazywamy ścieżką powiększającą to skojarzenie. Jasne jest, że jeśli taka ścieżka w grafie istnieje, to możemy powiększyć skojarzenie. Okazuje się, że implikacja w przeciwną stronę również zachodzi i jest tezą twierdzenia sformułowanego i udowodnionego przez francuskiego matematyka Claude’a Berge’a:
Twierdzenie. Skojarzenie w grafie jest najliczniejsze wtedy i tylko wtedy, gdy nie istnieje ścieżka powiększająca to skojarzenie.
Tak więc to, czego potrzebujemy, to procedura znajdowania ścieżki powiększającej lub stwierdzania, że takowa nie istnieje. Dla ustalenia uwagi możemy skupić się na poszukiwaniu ścieżki powiększającej zaczynającej się w ustalonym nieskojarzonym wierzchołku – wystarczy przejrzeć wszystkie takie wierzchołki. Oczywiście, moglibyśmy wyznaczyć wszystkie ścieżki wychodzące z tego wierzchołka, korzystając z przeszukiwania z nawrotami. Taka metoda będzie jednak wymagać czasu wykładniczego, zatem potrzebujemy czegoś sprytniejszego.
Na początek zajmiemy się grafami dwudzielnymi, tzn. takimi, których zbiór wierzchołków możemy podzielić na takie dwa podzbiory że każda krawędź łączy wierzchołki należące do różnych podzbiorów. Spróbujmy usprawnić przeszukiwanie z nawrotami, tak aby odwiedzić każdy wierzchołek co najwyżej raz. A konkretnie: zaczynając z wierzchołka wykonujemy przeszukiwanie grafu w głąb, z tym że w każdym wierzchołku na głębokości parzystej przeglądamy nieskojarzone krawędzie wychodzące z tego wierzchołka, w każdym zaś wierzchołku na głębokości nieparzystej idziemy krawędzią skojarzoną (może być co najwyżej jedna). Jeśli trafimy na wierzchołek nieskojarzony, to znaczy, że znaleźliśmy ścieżkę powiększającą (Rys. 3). Co ciekawe, jeśli na niego nie trafiliśmy, to znaczy, że ścieżki powiększającej nie ma. Jak to udowodnić?
Rozważmy graf skierowany o zbiorze wierzchołków Dla każdej krawędzi z naszego dodajmy do skierowaną krawędź: jeśli albo jeśli Zauważmy, że w grafie istnieje ścieżka powiększająca wtedy i tylko wtedy, gdy w grafie da się dojść (zgodnie ze skierowaniem krawędzi) z wierzchołka do innego wierzchołka nieskojarzonego. A powyższy algorytm to nic innego jak zwykłe przeszukiwanie w głąb grafu
Przeszukiwanie to działa w czasie zatem znalezienie ścieżki powiększającej zajmie czas jeśli zaczynamy przeszukiwanie z każdego wierzchołka nieskojarzonego od nowa, lub jeśli zaczynamy naraz z wszystkich wierzchołków nieskojarzonych z (dlaczego możemy tak zrobić?). Skojarzenie może być powiększone co najwyżej razy, zatem cały algorytm działa w czasie Dodajmy, że w praktyce nie opłaca się zaczynać z pustego skojarzenia, ale z jakiegoś skojarzenia znalezionego prostszą metodą (przykładowo, algorytm zachłannie dodający kolejne krawędzie do skojarzenia zawsze znajdzie skojarzenie o liczności równej co najmniej połowie liczności najliczniejszego skojarzenia).
Okazuje się, że w przypadku grafów, które nie są dwudzielne, sprawa się komplikuje, a nasz algorytm nie działa. Istotnie, spójrzmy na graf z rysunku 4 Pomimo tego, że istnieje w nim ścieżka powiększająca z wierzchołka do wierzchołka to algorytm nigdy jej nie znajdzie, jeśli zacznie obchodzić cykl od lewej strony (w szczególności nigdy nie odwiedzi wierzchołka ).
Cały kłopot jest powodowany przez cykle, które znajdujemy, przeszukując graf. Problematyczne okazują się mianowicie cykle nieparzystej długości, które są połączone z wierzchołkiem ścieżką o parzystej długości. Taki cykl nazwiemy kielichem (ang. blossom), a ścieżkę łodygą (ang. stem). Kanadyjski matematyk Jack Edmonds podał następujący sposób, w jaki można sobie z nimi radzić: ściągnąć, czyli zastąpić jednym wierzchołkiem, a następnie rekurencyjnie poszukać ścieżki powiększającej w mniejszym grafie. Przyjrzyjmy się temu pomysłowi bliżej.
Operacja ściągnięcia kielicha wygląda następująco (Rys. 5): zastępujemy wszystkie wierzchołki kielicha jednym wierzchołkiem który jest połączony krawędziami z wszystkimi sąsiadami kielicha, otrzymując graf Zauważmy, że na rysunku wierzchołek który łączył kielich z łodygą, był jedynym wierzchołkiem spośród wierzchołków kielicha skojarzonym z wierzchołkiem poza kielichem, zatem wierzchołek będzie również skojarzony z dokładnie jednym wierzchołkiem. Wynika z tego, że po ściągnięciu nowo utworzone skojarzenie nadal jest poprawne. Okazuje się, że prawdziwy jest następujący lemat:
Lemat. W grafie istnieje ścieżka powiększająca skojarzenie wtedy i tylko wtedy, gdy w grafie istnieje ścieżka powiększająca skojarzenie
Dla dowodu lematu załóżmy, że w grafie znaleźliśmy ścieżkę powiększającą Pokażemy, jak dzięki niej znaleźć ścieżkę w grafie Jeśli ścieżka nie zawiera wierzchołka to w oczywisty sposób jest ona również ścieżką powiększającą w W przeciwnym przypadku można ją uzupełnić krawędziami z kielicha, przechodząc go w lewo lub w prawo, w zależności od położenia wierzchołka którym wchodzi druga krawędź ścieżki (Rys. 5). Ponadto każda ścieżka powiększająca w musi być takiej postaci (tzn. jeśli przecina kielich, to wchodzi do niego krawędzią łodygi), zatem jest również ścieżką powiększającą w
Jako zadanie dla Czytelnika pozostawiamy wykazanie, że jeśli w istnieje ścieżka powiększająca, to nasz algorytm wyszukujący ścieżki powiększające albo ją znajdzie, albo znajdzie kielich, który można ściągnąć.
Znalezienie ścieżki powiększającej z ustalonego wierzchołka zajmuje czas gdyż potencjalnie aż razy będziemy musieli wykonać ściągnięcie kielicha. Zatem cały algorytm działa w czasie
Na koniec zaznaczmy, że opisane algorytmy nie są najlepszymi znanymi algorytmami znajdującymi najliczniejsze skojarzenia w grafach, są za to niezbyt skomplikowane. Przykładowo algorytm Hopcrofta–Karpa dla grafów dwudzielnych oraz algorytm Micalego–Vaziraniego dla grafów dowolnych działają w czasie zaś randomizowany algorytm Muchy–Sankowskiego sprowadza znajdowanie skojarzenia w grafie dowolnym do problemu mnożenia macierzy i działa w czasie