Wyznaczanie minimalnego drzewa rozpinającego. Algorytm Kruskala Michał Kasiński 19 marca 2013 Drzewa rozpinające Definicja 1. Drzewem rozpinającym T spójnego grafu G = (V, E) nazywamy drzewo takie, że V (T ) = V oraz E(T ) ⊆ E. Przykład 1. Graf i dwa z jego rozpinających drzew Fakt 1. Każdy graf spójny zawiera co najmniej jedno drzewo rozpinające. Definicja 2. Niech T będzie zbiorem wszystkich drzew rozpinających w grafie ważonym G = (V, E, w). Drzewo T ∈ T , dla którego wartość: X w(e) e∈E(T ) jest najmniejsza nazywamy minimalnym drzewem rozpinającym. Przykład 2. Graf i jego minimalne drzewo rozpinające 1 Algorytm Kruskala Jeden z popularnych algorytmów wyznaczania minimalnego drzewa rozpinającego został opracowany przez Josepha Kruskala. W tej metodzie wszystkie krawędzie są porządkowane według ich wag, następnie algorytm analizuje każdą z krawędzi tej uporządkowanej sekwencji, sprawdzając, czy może się ona stać częścią tworzonego drzewa. Krawędź jest dodawana do drzewa, jesli jej dołączenie nie spowoduje powstania żadnego cyklu. Ten prosty algorytm można opisać w następujący sposób: A ← ∅; for każdy wierzchołek v ∈ V (G) do M AKE − SET (v); end posortuj krawędzie z E niemalejąco względem wag w; for każda krawędź(u, v) ∈ E, w kolejności niemalejących wag do if F IN D(u)! = F IN D(v) then A ← A ∪ {(u, v)}; U N ION (u, v); end end return A; Algorytm 1: Algorytm Kruskala Przy implementacji algorytmu Kruskala korzystamy ze struktury zbiorów rozłącznych, zwanej lasem zbiorów rozłącznych - każdy zbiór interpretujemy jako drzewo, stąd określenie lasu. Poświęćmy chwilę na analizę budowy i złożoności takich struktur. 2 Algorytmy Union-Find Analiza problemu • Obiekty. 0123456789 • Zbiory rozłączne. {0} {1} {2 3 4} {5 6} {7} {8 9} • Zapytanie FIND: Czy obiekty 2 i 9 należą do tego samego zbioru? {0} {1} {2 3 4} {5 6} {7} {8 9} • Operacja UNION: Scal zbiory zawierające elementy 3 i 8 {0} {1} {2 3 4 8 9} {5 6} {7} Cel: Zaprojektować efektywną strukturę danych dla operacji Union-Find • Zapytania FIND i UNION mogą następować w losowej kolejności. • Liczba operacji M może być duża. • Liczba obiektów N może być duża. Quick Find (QF) - podejście zachłanne Struktura danych Tablica liczb całkowitych id[] rozmiaru N.Gdy p i q są połączone mają taką samą wartość id. i id[i] 0 0 1 1 2 9 3 9 4 9 5 6 6 6 7 7 8 8 9 9 5 i 6 są połączone 2,3,4 i 9 są połączone FIND - sprawdź czy p i q mają taką samą wartość id. UNION - zamień wszystkie wystąpienia id[p] na id[q]. i id[i] 0 0 1 1 2 6 3 6 4 6 UNION(3,6) - 2,3,4,5,6 i 9 są połączone Uwaga! Zmieniamy dużo wartości. 3 5 6 6 6 7 7 8 8 9 6 Quick Union (QU) - podejście leniwe Struktura danych Tablica liczb całkowitych id[] rozmiaru N. id[i] jest rodzicem i. Korzeniem obiektu i jest id[id[id[...id[i]...]]] (kontynuuj aż do id[i]==i) 0 0 i id[i] 1 1 2 9 3 4 4 9 5 6 6 6 7 7 8 8 9 9 korzeniem dla 3 jest 9;korzeniem dla 5 jest 6 3 i 5 nie są połączone FIND - sprawdź czy p i q mają taki sam korzeń. UNION - zamień id korzenia obiektu q na id korzenia obiektu p. 0 0 i id[i] 1 1 2 9 3 4 4 9 5 6 6 9 7 7 8 8 9 9 UNION(3,5) Uwaga! Zmieniła się tylko jedna wartość. Quick Union jest zbyt wolny Problemy z Quick Find: • Operacja UNION zbyt kosztowna (N kroków). • Drzewa są płaskie, ale zbyt duży koszt utrzymania ich w takiej postaci. Problemy z Quick Union: • Drzewa mogą być zbyt wysokie. • Zapytania FIND są zbyt kosztowne (może być N kroków). • Trzeba wykonać FIND aby wykonać UNION. Struktura UNION FIND QF N 1 QU N∗ N ∗ uwzględniając koszt FIND 4 Ulepszenie 1: ważenie (WQU) Weighted Quick-Union • Modyfikacja Quick-Union aby zapobiec wysokim drzewom. • Zapamiętanie rozmiaru każdego zbioru. • Utrzymanie zbalansowanego drzewa poprzez dołączanie mniejszych drzew do większych. Przykład 3. UNION(5,3) • Quick-Union: dołącz 9 do 6. • Ważony Quick-Union: dołącz 6 do 9. Implementacja • Prawie identyczna jak Quick-Union • Dodatkowa tablica sz[] dla liczby elementów w poddrzewie o korzeniu i. FIND - tak samo jak w QU UNION - porównanie rozmiarów, aktualizacja wagi Analiza • FIND: czas proporcjonalny do głębokości p i q. • UNION: złożoność obliczeniowa stała przy danych korzeniach. • Fakt: głębokość conajwyżej log2 N 5 Struktura UNION FIND QF N 1 QU N∗ N WQU log2 N ∗ log2 N ∗ uwzględniając koszt FIND Ulepszenie 2: kompresja ścieżek(WQUPC) Przy szukaniu korzenia dla i ustawmy obiekty tak, by każdy mijany przez nas obiekt wskazywał na swojego dziadka. Wystarczy zmienić jedną linijkę w kodzie: public int r o o t ( int i ) { while ( i != i d [ i ] ) { id[i] = id[id[i]] ; i=i d [ i ] ; } return i ; } Podsumowanie Dla M operacji Union-Find na zbiorze N obiektów mamy: Algorytm Quick-Find Quick-Union Ważony QU Kompresja ścieżki Ważony QU + PC Złożoność pesymistyczna MN MN N + M log2 N N + M log2 N (M + N )lg ∗ N lg ∗ N czyli logarytm iterowany to funkcja o wartości mówiącej ile razy trzeba wykonać log2 na liczbie N aż do uzyskania 1. W praktyce jest to bardzo wolno rosnąca funkcja. lg ∗ N 0 1 2 3 4 5 N 1 2 4 16 65536 265536 Tarjan pokazał, że złożoność jest w jeszcze niższa. Odpowiada funkcji odwrotnej do funckji Ackermanna. Dowód przekracza moje kompetencje. 6 Złożoność Dzięki zastosowaniu algorytmu WQUPC oraz mając posortowane krawędzie algorytm Kruskala osiąga złożoność obliczeniową O(|E|lg ∗ |V |). Zastosowania Algorytm Kruskala znajduje zastosowanie tam, gdzie znajdują zastosowanie minimalne drzewa rozpinające, m.in. w: • Taksonomii • Segmentacji obrazów • Sieciach komputerowych (protokół STP) • Rozpoznawaniu (pisma) wyrażeń matematycznych • Projektowaniu obwodów 7