Algorytmy i struktury danych

Podobne dokumenty
Struktury danych i złożoność obliczeniowa Wykład 5. Prof. dr hab. inż. Jan Magott

Przykłady grafów. Graf prosty, to graf bez pętli i bez krawędzi wielokrotnych.

Ogólne wiadomości o grafach

Grafem nazywamy strukturę G = (V, E): V zbiór węzłów lub wierzchołków, Grafy dzielimy na grafy skierowane i nieskierowane:

Algorytmy grafowe. Wykład 1 Podstawy teorii grafów Reprezentacje grafów. Tomasz Tyksiński CDV

Programowanie obiektowe

Porównanie algorytmów wyszukiwania najkrótszych ścieżek międz. grafu. Daniel Golubiewski. 22 listopada Instytut Informatyki

Reprezentacje grafów nieskierowanych Reprezentacje grafów skierowanych. Wykład 2. Reprezentacja komputerowa grafów

Matematyczne Podstawy Informatyki

Programowanie obiektowe

Algorytmiczna teoria grafów

a) 7 b) 19 c) 21 d) 34

Digraf. 13 maja 2017

G. Wybrane elementy teorii grafów

Zofia Kruczkiewicz, Algorytmu i struktury danych, Wykład 14, 1

Teoria grafów dla małolatów. Andrzej Przemysław Urbański Instytut Informatyki Politechnika Poznańska

Matematyka dyskretna

Suma dwóch grafów. Zespolenie dwóch grafów

Matematyczne Podstawy Informatyki

Wykład 10 Grafy, algorytmy grafowe

MATEMATYKA DYSKRETNA - MATERIAŁY DO WYKŁADU GRAFY

Algorytmy i Struktury Danych.

Algorytmy i Struktury Danych.

Matematyczne Podstawy Informatyki

Algorytmy i Struktury Danych.

Temat: Struktury danych do reprezentacji grafów. Wybrane algorytmy grafowe.

Matematyka dyskretna. Andrzej Łachwa, UJ, /14

Drzewa spinające MST dla grafów ważonych Maksymalne drzewo spinające Drzewo Steinera. Wykład 6. Drzewa cz. II

Wykład 8. Drzewo rozpinające (minimum spanning tree)

Algorytmy i struktury danych

Podstawy Programowania 2 Grafy i ich reprezentacje. Plan. Wstęp. Teoria grafów Graf skierowany. Notatki. Notatki. Notatki. Notatki.

. Podstawy Programowania 2. Grafy i ich reprezentacje. Arkadiusz Chrobot. 9 czerwca 2016

Algorytmy Równoległe i Rozproszone Część V - Model PRAM II

Przypomnij sobie krótki wstęp do teorii grafów przedstawiony na początku semestru.

Wykład 7. Algorytmy grafowe

Algorytm Dijkstry znajdowania najkrótszej ścieżki w grafie

Rozwiązywanie problemów metodą przeszukiwania

Graf. Definicja marca / 1

Złożoność obliczeniowa klasycznych problemów grafowych

AiSD zadanie trzecie

Algorytmy grafowe. Wykład 2 Przeszukiwanie grafów. Tomasz Tyksiński CDV

Marek Miszczyński KBO UŁ. Wybrane elementy teorii grafów 1

Podstawowe własności grafów. Wykład 3. Własności grafów


Algorytmy z powracaniem

Wstęp do Programowania potok funkcyjny

Matematyka dyskretna. Andrzej Łachwa, UJ, /15

Sortowanie topologiczne skierowanych grafów acyklicznych

Matematyka dyskretna. Andrzej Łachwa, UJ, B/14

Matematyka dyskretna. Andrzej Łachwa, UJ, /14

Teoria grafów podstawy. Materiały pomocnicze do wykładu. wykładowca: dr Magdalena Kacprzak

Podstawowe pojęcia dotyczące drzew Podstawowe pojęcia dotyczące grafów Przykłady drzew i grafów

Grafy dla każdego. dr Krzysztof Bryś. Wydział Matematyki i Nauk Informacyjnych Politechnika Warszawska.

. Podstawy Programowania 2. Algorytmy dfs i bfs. Arkadiusz Chrobot. 2 czerwca 2019

TEORIA GRAFÓW I SIECI

Elementy teorii grafów Elementy teorii grafów

Algorytm DFS Wprowadzenie teoretyczne. Algorytm DFS Wprowadzenie teoretyczne. Algorytm DFS Animacja. Algorytm DFS Animacja. Notatki. Notatki.

Teoria grafów II. Materiały pomocnicze do wykładu. wykładowca: dr Magdalena Kacprzak

TEORIA GRAFÓW I SIECI

Algorytmy i Struktury Danych.

Opracowanie prof. J. Domsta 1

ĆWICZENIE 1: Przeszukiwanie grafów cz. 1 strategie ślepe

WYŻSZA SZKOŁA INFORMATYKI STOSOWANEJ I ZARZĄDZANIA

Wstęp do programowania

Wstęp do Programowania potok funkcyjny

Uniwersytet Zielonogórski Wydział Elektrotechniki, Informatyki i Telekomunikacji Instytut Sterowania i Systemów Informatycznych

Algorytmy i Struktury Danych.

Kolorowanie wierzchołków Kolorowanie krawędzi Kolorowanie regionów i map. Wykład 8. Kolorowanie

Języki programowania, wtorek , 12:15-13:45 Zadanie 11 - ostatnie

TEORIA GRAFÓW I SIECI

Grafy w MATLABie. LABORKA Piotr Ciskowski

0. ELEMENTY LOGIKI. ALGEBRA BOOLE A

1 Automaty niedeterministyczne

Algorytmy i struktury danych. Drzewa: BST, kopce. Letnie Warsztaty Matematyczno-Informatyczne

Sztuczna Inteligencja i Systemy Doradcze

Dynamiczne struktury danych

Algorytmy Grafowe. dr hab. Bożena Woźna-Szcześniak, prof. UJD. Wykład 1,2,3. Uniwersytet Humanistyczno-Przyrodniczy im. Jana Długosza w Częstochowie

WSTĘP DO INFORMATYKI. Grafy i struktury grafowe

Digraf o V wierzchołkach posiada V 2 krawędzi, zatem liczba różnych digrafów o V wierzchołkach wynosi 2 VxV

Wprowadzenie do Sztucznej Inteligencji

Wprowadzenie do Sztucznej Inteligencji

E: Rekonstrukcja ewolucji. Algorytmy filogenetyczne

5c. Sieci i przepływy

Algorytmiczna teoria grafów

1. Algorytmy przeszukiwania. Przeszukiwanie wszerz i w głąb.

Grafy i Zastosowania. 9: Digrafy (grafy skierowane) c Marcin Sydow

Egzaminy i inne zadania. Semestr II.

Struktury danych i złożoność obliczeniowa Wykład 7. Prof. dr hab. inż. Jan Magott

Wrocław, Wstęp do informatyki i programowania: liczby pierwsze. Wydział Matematyki Politechniki Wrocławskiej.

Zadanie 1 Przygotuj algorytm programu - sortowanie przez wstawianie.

Wstęp do programowania. Listy. Piotr Chrząstowski-Wachtel

Wprowadzenie do Sztucznej Inteligencji

Algorytmy równoległe. Rafał Walkowiak Politechnika Poznańska Studia inżynierskie Informatyka 2010

Lista, Stos, Kolejka, Tablica Asocjacyjna

Droga i cykl Eulera Przykłady zastosowania drogi i cyku Eulera Droga i cykl Hamiltona. Wykład 4. Droga i cykl Eulera i Hamiltona

Badania operacyjne: Wykład Zastosowanie kolorowania grafów w planowaniu produkcji typu no-idle

Drzewa czerwono-czarne.

Algorytmy wyznaczania centralności w sieci Szymon Szylko

Wstęp do sieci neuronowych, wykład 12 Wykorzystanie sieci rekurencyjnych w optymalizacji grafowej

Transkrypt:

Algorytmy i struktury danych Wykład 10 - Grafy i podstawowe algorytmy grafowe Janusz Szwabiński Plan wykładu: Zagadnienie mostów królewieckich Podstawowe pojęcia Zastosowania grafów Graf jako abstrakcyjny typ danych Reprezentacje grafu macierz sąsiedztwa lista sąsiedztwa lista krawędzi macierz incydencji Implementacja grafu jako typu danych Wyszukiwanie wszerz Studium przypadku - drabina słowna Algorytm wyszukiwania Źródła: większość ilustracji i przykładów pochodzi z "Problem Solving with Algorithms and Data Structures using Python", http://interactivepython.org/runestone/static/pythonds/index.html

Zagadnienie mostów królewieckich zagadnienie, nad którym rzekomo głowili się mieszkańcy Królewca rozwiązane przez Leonharda Eulera (w Solutio problematis ad geometriam situs pertinentis w Commentarii academiae scientiarum Petropolitanae, 1741) pierwsza praca z teorii grafów czy można przejść kolejno przez wszystkie mosty tak, żeby każdy przekroczyć tylko raz? odpowiedź brzmi: nie decyduje o tym nieparzysta liczba wylotów mostów zarówno na każdą z wysp, jak i na oba brzegi rzeki graf eulerowski - graf spójny, który można opisać linią ciągłą w taki sposób, aby każda krawędź tego grafu była obwiedziona tylko raz (tzw. cykl Eulera) możliwe tylko wtedy, gdy liczba wierzchołków tego grafu, w których spotyka się nieparzysta liczba krawędzi, wynosi 0 lub 2 aby przejść wszystkie krawędzie grafu i wrócić do punktu wyjścia, nie może on zawierać żadnych węzłów, w których spotyka się nieparzysta liczba krawędzi Pojęcia podstawowe Węzeł (wierzchołek) podstawowy element składowy grafu w teorii grafów - punkt pewnej przestrzeni (zbioru) V, nad którą zbudowany jest graf reprezentuje pewien obiekt może posiadać nazwę (klucz) oraz dodatkowe dane Krawędź podstawowy element składowy grafu w teorii grafów - para wyróżnionych wierzchołków grafu, czyli takich, które są ze sobą połączone (sąsiednie) reprezentuje relacje między obiektami może być jedno- lub dwukierunkowa Graf (graf prosty lub nieskierowany) Graf nieskierowany G składa się z dwóch zbiorów V E, przy czym V jest niepustym zbiorem, którego elementy nazywane są wierzchołkami, a E jest rodziną dwuelementowych podzbiorów zbioru wierzchołków V, zwanych krawędziami, E {{u, v}: u, v V, u v} Graf skierowany Graf skierowany (digraf) składa się z dwóch zbiorów niepustego zbioru wierzchołków V oraz rodziny A par uporządkowanych elementów zbioru V, zwanych krawędziami lub łukami grafu skierowanego. Kolejność wierzchołków w parze wyznacza kierunek

krawędzi w przypadku pary v, u łuk biegnie z wierzchołka v do wierzchołka u. Waga wartości przypisane krawędziom mogą określać np.: koszt przejścia między wierzchołkami przepustowość połączenia częstotliwość kontaktów V = v0, v1, v2, v3, v4, v5 (v0, v1, 5), (v1, v2, 4), (v2, v3, 9), (v3, v4, 7), (v4, v0, 1), E = { } (v0, v5, 2), (v5, v4, 8), (v3, v5, 3), (v5, v2, 1) Ścieżka sekwencja wierzchołków połączonych krawędziami w 1, w 2,..., w n, (w i, w i + 1 ) E, 1 i n 1 nieważona długość ścieżki to po prostu liczba krawędzi ważona długość ścieżki to suma wag wszystkich krawędzi składających się na ścieżkę Droga ścieżka, w której wierzchołki są różne (z wyjątkiem ewentualnej równości wierzchołków pierwszego i ostatniego mamy wtedy do czynienia z tzw. cyklem) Cykl droga zamknięta, czyli taka, której koniec (ostatni wierzchołek) jest identyczny z początkiem (pierwszym wierzchołkiem) Graf planarny graf, którego wierzchołki można rozmieścić na płaszczyźnie w taki sposób, aby łączące je krawędzie nie przecinały się In [1]: import planarity edgelist = [('a', 'b'), ('a', 'c'), ('a', 'd'), ('a', 'e'), ('b', 'c'),('b', 'd'),('b', 'e'), ('c', 'd'), ('c', 'e'), ('d', 'e')] print(planarity.is_planar(edgelist)) False

In [2]: edgelist.remove(('a','b')) print(planarity.is_planar(edgelist)) True In [3]: print(planarity.ascii(edgelist)) ----1---- --5-- --2-- --3-- ---4---- Graf pełny graf prosty, w którym dla każdej pary węzłów istnieje krawędź je łącząca In [4]: %matplotlib inline import networkx as nx G = nx.complete_graph(10) nx.draw_circular(g) Stopień wierzchołka w grafie nieskierowanym liczba kończących się w nim krawędzi w grafie skierowanym: stopień wejściowy - liczba krawędzi kończących się w wierzchołku stopień wyjściowy - liczba krawędzi zaczynających się w wierzchołku stopień to suma stopnia wejściowego i wyjściowego Graf regularny każdy wierzchołek ma taki sam stopień

Zastosowania Informatyka reprezentacja struktury programów modelowanie systemów komputerowych modelowanie danych bazy danych kryptografia sieci komputerowe Technika płytki drukowane sieci komunikacyjne Fizyka i chemia modelowanie złożonych cząsteczek modelowanie sieci krystalicznych reprezentacja lokalnych oddziaływań między cząstkami reprezentacja mikrokanałów w ośrodkach porowatych Nauki społeczne grafy wpływu, popularności, współpracy, przyjaźni itp. dynamika opinii epidemie Biologia sieci pokarmowe sieci regulacji genowej sieci metaboliczne oddziaływania między róznymi rejonami mózgu komunikacja międzykomórkowa Kartografia mapy połączeń wyszukiwanie najkrótszych dróg Lingwistyka grafy składniowe grafy semantyczne TextGraphs, http://www.textgraphs.org/ WordNet, https://wordnet.princeton.edu/ Słowosieć, http://plwordnet.pwr.wroc.pl/wordnet/ i wiele innych... Graf jako abstrakcyjny typ danych Graph() - tworzy nowy pusty graf addvertex(vert) - dodaje węzeł do grafu addedge(fromvert, tovert) - dodaje krawędź (skierowaną) do grafu addedge(fromvert, tovert, weight) - dodaje krawędź ważoną getvertex(vertkey) - znajduje wierzchołek o podanym kluczu getvertices() - lista wszystkich wierzchołków w grafie getedges() - lista wszystkich krawędzi w grafie in - sprawdza przynależność wierzchołka do grafu Reprezentacje grafu istnieje wiele możliwych sposobów reprezentacji grafu najbardziej znane to: macierz sąsiedztwa lista sąsiedztwa lista krawędzi macierz incydencji różnią się one między sobą zajętością pamięci oraz złożonością typowych operacji Macierz sąsiedztwa implementacja w postaci macierzy n n, gdzie n to liczba wierzchołków w grafie każdy wiersz i każda kolumna w macierzy odpowiada jakiemuś wierzchołkowi

jeżeli na przecięciu wiersza v z kolumną w zapisana jest jakaś wartość (różna od zera), oznacza to, że istnieje krawędź (v, w) w naturalny sposób uwzględnia wagi krawędzi dla małych grafów duża przejrzystość - od razu widać, które wierzchołki są ze sobą połączone w powyższym przykładzie większość komórek jest pusta: niezbyt wydajne zarządzanie pamięcią w przypadku grafów "rzadkich" rozsądne dla grafów zbliżonych do pełnych złożoność pamięciowa: O(n 2 ) złożoność typowych operacji: wstawianie krawędzi: O(1) usuwanie krawędzi: O(1) sprawdzanie krawędzi: O(1) Lista sąsiedztwa lista wszystkich wierzchołków każdy z wierzchołków zawiera listę swoich sąsiadów złożoność pamięciowa: O(n + m), gdzie m to liczba krawędzi typowe operacje: wstawianie krawędzi: O(1) usuwanie krawędzi: czas wyszukiwania na liście uporządkowanej sprawdzanie krawędzi: czas wyszukiwania na liście uporządkowanej Lista krawędzi lista wszystkich krawędzi występujących w grafie

złożoność pamięciowa: O(m) typowe operacje: wstawianie krawędzi: O(1) usuwanie krawędzi: O(m) sprawdzanie krawędzi: O(m) (w przypadku list nieuporządkowanych) Macierz incydencji macierz M o rozmiarze n m niektórzy autorzy używają odwrotnej konwencji znaków w przypadku grafów nieskierowanych oba wierzchołki oznaczamy jako początek krawędzi! macierz incydencji pozwala również reprezentować pętle (m ij = 2), ale komplikuje to przetwarzanie macierzy bez problemu pozwala reprezentować krawędzie wielokrotne złożoność pamięciowa: O(n + m) typowe operacje: sprawdzanie, czy wierzchołek należy do krawędzi: O(1) sprawdzanie krawędzi: O(m) wstawianie krawędzi: O(1) usuwanie krawędzi: O(m)

Implementacja grafu jako typu danych w Pythonie najlepszym kandydatem do zaimplementowania listy sąsiedztwa jest... słownik stworzymy dwie klasy: Vertex - reprezentacja wierzchołka w grafie Graph - lista wierzchołków In [5]: class Vertex: def init (self,key): self.id = key self.connectedto = {} #lista sąsiedztwa z wagami def addneighbor(self,nbr,weight=0): self.connectedto[nbr] = weight def str (self): return str(self.id) + ' connectedto: ' + str([x.id for x in self.connectedto]) def getconnections(self): return self.connectedto.keys() def getid(self): return self.id def getweight(self,nbr): return self.connectedto[nbr] In [6]: class Graph: def init (self): self.vertlist = {} self.numvertices = 0 def addvertex(self,key): self.numvertices = self.numvertices + 1 newvertex = Vertex(key) self.vertlist[key] = newvertex return newvertex def getvertex(self,n): if n in self.vertlist: return self.vertlist[n] else: return None def contains (self,n): return n in self.vertlist def addedge(self,f,t,cost=0): if f not in self.vertlist: nv = self.addvertex(f) if t not in self.vertlist: nv = self.addvertex(t) self.vertlist[f].addneighbor(self.vertlist[t], cost) def getvertices(self): return self.vertlist.keys() def iter (self): return iter(self.vertlist.values()) Przykładowa sesja: In [7]: g = Graph() for i in range(6): g.addvertex(i)

In [8]: g.vertlist Out[8]: {0: < main.vertex at 0x7f00c2ceb358>, 1: < main.vertex at 0x7f00c2ceb400>, 2: < main.vertex at 0x7f00c2ceb438>, 3: < main.vertex at 0x7f00c2ceb470>, 4: < main.vertex at 0x7f00c2ceb4a8>, 5: < main.vertex at 0x7f00c2ceb4e0>} In [9]: g.addedge(0,1,5) g.addedge(0,5,2) g.addedge(1,2,4) g.addedge(2,3,9) g.addedge(3,4,7) g.addedge(3,5,3) g.addedge(4,0,1) g.addedge(5,4,8) g.addedge(5,2,1) In [10]: for v in g: for w in v.getconnections(): print("( %s, %s )" % (v.getid(), w.getid())) ( 0, 1 ) ( 0, 5 ) ( 1, 2 ) ( 2, 3 ) ( 3, 4 ) ( 3, 5 ) ( 4, 0 ) ( 5, 4 ) ( 5, 2 ) Wyszukiwanie wszerz Studium przypadku - drabina słowna (ang. word ladder problem) zabawa słowna, wymyślona w 1878 roku przez Lewisa Carolla (autora "Alicji w krainie czarów") trzeba przekształcić podane słowo, np. FOOL w inne słowo, np. SAGE przekształceń dokonuje się krok po kroku w każdym kroku można zmienić tylko jedną literę dopuszczalne są jedynie zmiany w istniejące słowa, np. FOOL POOL POLL POLE PALE SALE SAGE istnieje wiele wariantów, np. transformacja: w określonej liczbie kroków w najmniejszej liczbie kroków przy użyciu określonych słów nasz cel - transformacja w najmniejszej liczbie kroków sposób postępowania: reprezentacja powiązań między słowami w formie grafu skorzystanie z algorytmu wyszukiwania wszerz (ang. breadth first search, BFS) do znalezienia najkrótszej ścieżki w grafie

Budowanie grafu krawędź między każdą parą słów, które różnią się tylko jedną literą dysponując takim grafem, każda ścieżka między FOOL a SAGE jest rozwiązaniem zagadki najkrótsza z nich rozwiązuje dodatkowo nasze zadanie załóżmy, że wszystkie słowa na liście są tej samej długości podejście pierwsze: możemy porównać każde słowo na liście ze wszystkimi pozostałymi i znaleźć w ten sposób te różniące się tylko jedną literą operacja klasy O(n 2 ) w porządku dla niewielkich zbiorów słów jeśli słów mamy np. 3130, trzeba wykonać prawie 10 milionów porównań mało wydajne podejście drugie załóżmy, że mamy wiele kubełków, których nazwy powstały ze słów, w których jedna litera została zastąpiona znakiem podkreślenia traktując ten znak jako dziką kartę, możemy sprawdzać, które ze słów pasuje do odpowiedniego kubełka wszystkie słowa w jednym kubełku są ze sobą połączone operacja klasy O(n)

In [11]: def buildgraph(wordfile): d = {} g = Graph() wfile = open(wordfile,'r') # create buckets of words that differ by one letter for line in wfile: word = line[:-1] for i in range(len(word)): bucket = word[:i] + '_' + word[i+1:] if bucket in d: d[bucket].append(word) else: d[bucket] = [word] # add vertices and edges for words in the same bucket for bucket in d.keys(): for word1 in d[bucket]: for word2 in d[bucket]: if word1!= word2: g.addedge(word1,word2) return g In [17]: g = buildgraph("data/wordfile") print("number of vertices: ", len(g.getvertices())) Number of vertices: 3034 In [18]: counter = 0 for v in g: for w in v.getconnections(): counter = counter + 1 print("number of edges: ", counter) Number of edges: 29634 W przypadku macierzy sąsiedztwa mielibyśmy In [19]: 3034**2 Out[19]: 9205156 elementów. Zapełnienie komórek tej macierzy byłoby na poziomie In [20]: 100*29634/(3034**2) #w procentach Out[20]: 0.32192827584888295 Innymi słowy graf słów jest bardzo rzadki.

Algorytm wyszukiwania wszerz jeden z prostszych algorytmów przeszukiwania grafu stanowi prototyp dla innych algorytmów przeszukiwania pozwala znaleźć ścieżkę między dowolnymi wierzchołkami grafu spójnego postępowanie: trzy kolory wierzchołków: biały (nieodwiedzony), szary (odwiedzony, ale niesprawdzony), czarny (sprawdzony) początkowo kolorujemy wszystkie węzły na biało zacznij od zadanego wierzchołka s - zmień jego kolor na szary dla każdego wierzchołka w jego liście sąsiedztwa: zmień kolor na szary jeśli wierzchołek startowy nie będzie już miał białych sąsiadów, zmień jego kolor na czarny powtórz całą procedurę dla każdego szarego sąsiada algorytm znajduje wszystkie wierzchołki w odległości k od startowego, zanim zacznie odwiedzać wierzchołki w odległości k + 1 na potrzeby algorytmu skorzystamy z omawianej wcześniej kolejki (klasa Queue): In [21]: from asd import Queue ponadto zmodyfikujemy klasę Vertex:

In [28]: import sys class Vertex: def init (self,num): self.id = num self.connectedto = {} self.color = 'white' self.dist = sys.maxsize self.pred = None self.disc = 0 self.fin = 0 #new: color of node #new: distance from beginning (will be used later) #new: predecessor #new: discovery time #new: end-of-processing time def addneighbor(self,nbr,weight=0): self.connectedto[nbr] = weight def setcolor(self,color): self.color = color def setdistance(self,d): self.dist = d def setpred(self,p): self.pred = p def setdiscovery(self,dtime): self.disc = dtime def setfinish(self,ftime): self.fin = ftime def getfinish(self): return self.fin def getdiscovery(self): return self.disc def getpred(self): return self.pred def getdistance(self): return self.dist def getcolor(self): return self.color def getconnections(self): return self.connectedto.keys() def getweight(self,nbr): return self.connectedto[nbr] def str (self): return str(self.id) + ":color " + self.color + ":disc " + str(self.disc) + ":fin " + str(self.fin) + ":dist " + str(self.dist) + ":pred \n\t[" + str(self.pred)+ "]\n" def getid(self): return self.id jeżeli algorytm natrafi na biały węzeł, powinny wydarzyć się 4 rzeczy: 1. jego kolor zmienia się na szary 2. aktualny węzeł staje się jego poprzednikiem 3. odległość zwiększa się o 1 względem poprzednika 4. węzeł trafia na koniec kolejki kandydatów do przetworzenia

In [25]: def bfs(g,start): start.setdistance(0) #distance 0 indicates it is a start node start.setpred(none) #no predecessor at start vertqueue = Queue() vertqueue.enqueue(start) #add start to processing queue while (vertqueue.size() > 0): currentvert = vertqueue.dequeue() #pop next node to process -> current node for nbr in currentvert.getconnections(): #check all neighbors of the current node if (nbr.getcolor() == 'white'): #if the neighbor is white nbr.setcolor('gray') #change its color to grey nbr.setdistance(currentvert.getdistance() + 1) #set its distance nbr.setpred(currentvert) #current node is its predecessor vertqueue.enqueue(nbr) #add it to the queue currentvert.setcolor('black') #change current node to black after visiting all of its neigh. prześledźmy działanie algorytmu krok po kroku startujemy od słowa FOOL słowo to ma w grafie 4 sąsiadów: POOL, FOIL, FOUL i COOL odwiedzamy każdego z nich, kolorujemy na szaro, ustawiamy odległość i wstawiamy do kolejki wreszcie zmieniamy kolor węzła startowego na czarny w kolejnym kroku funkcja bfs ściąga pierwsze słowo z kolejki - POOL sąsiedzi tego węzła są sprawdzani w pewnym momencie procedura trafia na słowo COOL ono jest już szare (zostało już odwiedzone) istnieje zatem jakaś krótsza ścieżka do COOL można zatem pominąć teraz ten węzeł pozostali sąsiedzi (tzn. POLL) są dodani do kolejki, mają zmieniony kolor i ustawioną odległość kolejne słowo w kolejce to FOIL jedyny nowy sąsiad to FAIL

jednak po przetworzeniu kolejki okaże się, że FOUL, COOL i FAIL nie dodadzą nic nowego kontynuując, uzyskamy coś podobnego do: In [33]: g = buildgraph("data/wordfile") for v in g: if v.getid()=='fool': print(v) fool:color white:disc 0:fin 0:dist 9223372036854775807:pred [None] In [36]: s = g.getvertex('fool')

In [37]: bfs(g,s) po wykonaniu funkcji bfs otrzymujemy lekko zmodyfikowany graf węzły są pokolorowane: jeżeli jakieś węzły w grafie pozostaną białe, będzie to informacja o tym, że graf jest niespójny węzły zawierają informację o długości najkrótszej ścieżki do węzła startowego s węzły zawierają informację o poprzednikach na ścieżce można wykorzystać te informacje do odtworzenia drabiny słów: In [38]: def traverse(y): x = y while (x.getpred()): print(x.getid()) x = x.getpred() print(x.getid()) traverse(g.getvertex('sage')) sage sale tale tall toll tool fool Złożoność obliczeniowa funkcji bfs petla while wykonuje się co najwyżej raz dla każdego węzła: O(n) operacji wewnętrzna pętla for wykonuje się co najwyżej raz dla każdej krawędzi: O(m) operacji całość jest zatem klasy O(n + m) Złożoność zagadnienia znalezienia drabiny słów Dodatkowo: czas potrzebny na wygenerowanie grafu czas potrzebny na odtworzenie drabiny