wersja do druku

Transkrypt

wersja do druku
Podstawowe algorytmy grafowe
i ich zastosowania
dr Andrzej Mróz (UMK w Toruniu)
2013
Projekt wspóªnansowany ze ±rodków Unii Europejskiej w ramach Europejskiego Funduszu Spoªecznego
Projekt pn. Wzmocnienie potencjaªu dydaktycznego UMK w Toruniu w dziedzinach
matematyczno-przyrodniczych
Poddziaªanie 4.1.1 Programu Operacyjnego Kapitaª Ludzki
2
Spis tre±ci
3
1
Wst¦p
1.1 Przeszukiwanie grafu
Przeszukiwanie (przegl¡danie) grafu = systematyczne przechodzenie wzdªu» jego kraw¦dzi w celu
odwiedzenia wszystkich wierzchoªków.
•
Sªu»y m.in. do zbierania informacji o strukturze grafu.
•
Algorytmy grafowe cz¦sto zaczyna si¦ od przeszukania wej±ciowego grafu.
•
Wiele bardziej zaawansowanych algorytmów grafowych jest modykacj¡ podstawowych
algorytmów przeszukiwania.
Dwie metody przeszukiwania grafu:
• w gª¡b
(ang.
• wszerz
(ang.
depth-rst search, DFS),
breadth-rst search, BFS).
W obu metodach b¦dziemy iterowa¢ po s¡siadach danego wierzchoªka. Dlatego najwygodniejsz¡ reprezentacj¡ grafu s¡ listy s¡siedztwa.
Oznaczenia:
dla
u∈V,
Adj = tablica list s¡siedztwa;
Adj[u] = lista s¡siadów
Uwaga.
u
w grae
G.
Iteracj¦ po s¡siadach danego wierzchoªka mo»na ªatwo zrealizowa¢ równie» na
macierzy s¡siedztwa. Jest to jednak bardziej kosztowne w sensie zªo»ono±ci obliczeniowej.
2
DFS
2.1 Przeszukiwanie grafu w gª¡b
Ustalmy graf niezorientowany
Z ustalonego wierzchoªka
G = (V, E).
¹ródªowego w
si¦gamy coraz gª¦biej w graf, je»eli jest to tylko
mo»liwe.
•
badamy wszystkie niezbadane dot¡d kraw¦dzie ostatnio odwiedzonego wierzchoªka
•
gdy wszystkie kraw¦dzie
v
s¡ zbadane, wracamy do wierzchoªka, z którego
v
v,
zostaª
odwiedzony,
•
proces kontynuujemy dopóki wszystkie wierzchoªki osi¡galne z wierzchoªka ¹ródªowego
w
nie zostan¡ odwiedzone.
Je»eli po powy»szym procesie pozostanie jakikolwiek nie odwiedzony wierzchoªek
u, kontynu-
ujemy przeszukiwanie traktuj¡c go jako nowy wierzchoªek ¹ródªowy.
Caªy proces powtarzamy, a» wszystkie wierzchoªki w grae zostan¡ odwiedzone.
Nale»y zatem zapami¦tywa¢ stany, w jakich znajduj¡ si¦ w danej chwili wierzchoªki.
Do zapisania aktualnego stanu wierzchoªka u»ywamy jednego z trzech kolorów:
• biaªy
wszystkie wierzchoªki na pocz¡tku;
szaro;
•
wierzchoªek odwiedzany po raz pierwszy kolorujemy na
•
wierzchoªek przetworzony (= lista jego s¡siadów jest caªkowicie zbadana) kolorujemy na
czarno.
4
Uwaga.
Tak naprawd¦ w najprostszej implementacji wystarcz¡ dwa stany: nieodwiedzony
i odwiedzony. Jednak dokªadniejsze rozró»nienie stanów pozwala lepiej zrozumie¢ ide¦ DFS oraz
jest wykorzystywane w niektórych zastosowaniach.
DFS-Visit(G, u)
1
begin
2
kolor(u) := szary;
3
for ka»dy v
4
5
6
∈
Adj[u] do
if kolor(v) = biaªy then DFS-Visit(G, v);
kolor(u) := czarny;
end;
Przebieg algorytmu dla u = 1
1
g
w
@
@
g2
w
g3
w
@
4
g
w
1
g
w
@
@
@
@
5
@w
g
g2
w
g6
w
g3
w
@
4
g
w
1
g
w
@
@
@
@
5
g
@w
g2
w
g6
w
g3
w
@
4
g
w
1
g
w
@
@
@
@
5
g
@w
g2
w
g6
w
g3
w
@
4
g
w
1
g
w
@
@
@
@
5
@w
g
g2
w
g6
w
g3
w
@
4
g
w
@
@
5
@w
g
g6
w
5
1
g
w
@
@
g2
w
g3
w
@
4
g
w
1
w
g
@
@
@
@
5
@w
g
g2
w
g6
w
g3
w
@
4
g
w
1
w
g
@
@
@
@
5
@w
g
g2
w
g6
w
g3
w
@
4
g
w
1
w
g
@
@
@
@
5
g
@w
g2
w
g6
w
g3
w
@
4
g
w
1
w
g
@
@
@
@
5
@w
g
g2
w
g6
w
g3
w
@
4
g
w
1
w
g
@
@
@
@
5
g
@w
g2
w
g6
w
g3
w
@
4
g
w
1
w
g
@
@
@
@
5
@w
g
g2
w
g6
w
g3
w
@
4
g
w
@
@
5
@w
g
g6
w
6
1
g
w
@
@
g2
w
g3
w
@
4
g
w
1
g
w
@
@
@
@
5
@w
g
g2
w
g6
w
g3
w
@
4
g
w
1
g
w
@
@
@
@
5
@w
g
g2
w
g6
w
g3
w
@
4
g
w
1
g
w
@
@
@
@
5
g
@w
g2
w
g6
w
g3
w
@
4
g
w
1
g
w
@
@
@
@
5
g
@w
g2
w
g6
w
g3
w
@
4
g
w
1
g
w
@
@
@
@
5
g
@w
g2
w
g6
w
g3
w
@
4
g
w
@
@
5
@w
g
Zauwa»my, »e po wywoªaniu DFS-Visit dla wierzchoªka
z
u
g6
w
u
wierzchoªki, które nie s¡ osi¡galne
pozostan¡ nieodwiedzone (biaªe). Aby zatem przejrze¢ caªy graf, nale»y wywoªywa¢ DFS-
Visit dopóki b¦d¡ biaªe wierzchoªki.
Peªen przebieg algorytmu DFS jest realizowany przez poni»sz¡ procedur¦:
7
DFS(G)
1
begin
2
for ka»dy u
3
∈
V(G) do
kolor(u) := biaªy;
4
for ka»dy u
5
∈
V(G) do
if kolor(u) = biaªy then
6
DFS-Visit(G, u);
7
end;
• Zªo»ono±¢ czasowa: O(|V | + |E|):
odwiedzamy ka»dy wierzchoªek i (dwukrotnie!)
ka»d¡ kraw¦d¹.
• Zªo»ono±¢ pami¦ciowa: O(|V |):
przechowywanie kolorów, przechowywanie wierz-
choªków na stosie rekurencji.
3
Zastosowania DFS
3.1 Badanie spójno±ci
Podstawowe zastosowania:
•
Sprawdzanie spójno±ci grafu.
•
Wyznaczanie skªadowych spójno±ci grafu.
Zauwa»my, »e w procedurze DFS wywoªujemy DFS-Visit dokªadnie tyle razy, ile jest skªadowych spójno±ci w grae
G.
W szczególno±ci, gdy graf jest spójny, DFS-Visit zostanie wywoªana dokªadnie raz przez
procedur¦ DFS.
Nietrudno uzupeªni¢ procedur¦ DFS o kod zliczaj¡cy skªadowe spójno±ci, jak równie» przypisuj¡cy wierzchoªkom numer skªadowej.
3.2 DFS - inne zastosowania
Inne zastosowania:
•
Wyznaczanie silnie spójnych skªadowych (w wersji dla grafu skierowanego).
•
Sortowanie topologiczne grafu zorientowanego (bez zorientowanych cykli).
•
Generowanie labiryntów.
•
Znajdowanie drogi w labiryncie.
Zainteresowanych odsyªamy do literatury (patrz te» dodatkowe materiaªy do wykªadu i zaj¦¢
laboratoryjnych).
4
BFS
4.1 Przeszukiwanie grafu wszerz
Z ustalonego wierzchoªka
•
¹ródªowego s
przegl¡damy kolejne wierzchoªki z niego osi¡galne.
wierzchoªki w odlegªo±ci (=najmniejszej liczbie kraw¦dzi)
przed wierzchoªkami w odlegªo±ci
k + 1,
k
od ¹ródªa s¡ odwiedzane
8
•
granica mi¦dzy wierzchoªkami odwiedzonymi i nieodwiedzonymi jest przekraczana jednocze±nie na caªej jej szeroko±ci.
Je»eli po powy»szym procesie pozostanie jakikolwiek nie odwiedzony wierzchoªek
u, kontynu-
ujemy przeszukiwanie traktuj¡c go jako nowy wierzchoªek ¹ródªowy.
Caªy proces powtarzamy, a» wszystkie wierzchoªki w grae zostan¡ odwiedzone.
Ka»dy wierzchoªek posiada jeden z 3 kolorów: biaªy, szary lub czarny (podobnie jak w DFS).
Na pocz¡tku wszystkie wierzchoªki s¡ biaªe.
Wierzchoªki szare s¡ przechowywane w kolejce FIFO.
Po jej opuszczeniu kolorujemy je na czarno.
4.2 Kolejka FIFO
Kolejka FIFO
Q jest dynamiczn¡ struktur¡ danych w formie ci¡gu, do której mo»na doª¡czy¢
skªadnik tylko w jednym ko«cu (na ko«cu kolejki -
head).
pocz¡tku kolejki -
tail),
a usun¡¢ tylko w drugim ko«cu (na
Mamy zatem dwie podstawowe operacje:
•
Enqueue(Q, v) = dodanie elementu v na ko«cu kolejki,
•
Dequeue(Q) = usuni¦cie elementu z pocz¡tku kolejki.
Dodatkowo wykorzystamy operacj¦
•
Head(Q) = zwrócenie elementu z pocz¡tku kolejki (bez usuwania go).
Uwaga.
Kolejk¦ implementujemy przy u»yciu struktur wska¹nikowych. Mo»na te» zasy-
mulowa¢ jej dziaªanie na zwykªej (statycznej) tablicy, albo wykorzysta¢ gotowe struktury biblioteczne (np. queue biblioteki STL w C++).
4.3 Algorytm
BFS-Visit(G, s)
1
begin
2
kolor(s) := szary;
3
Q := {s};
4
while Q <>
∅
do begin
5
u := Head(Q);
6
for ka»dy v
7
8
Enqueue(Q, v)
10
end;
11
Dequeue(Q);
12
14
Adj[u] do
kolor(v) := szary;
9
13
∈
if kolor(v) = biaªy then begin
kolor(u) := czarny
end
end
Przebieg algorytmu dla s = 1
1
4
f
v
@
f
v
@
@
f2
v
f3
v
5
f6
v
@v
f
Q:
9
1
f
v
@
4
f
v
1
f
v
@
4
f
v
1
f
v
@
4
f
v
1
f
v
@
4
f
v
1
f
v
@
4
f
v
1
f
v
@
4
f
v
1
v
f
@
4
f
v
1
v
f
@
4
f
v
1
v
f
@
4
f
v
1
v
f
@
4
f
v
1
v
f
@
4
f
v
@
@
@
@
@
@
@
@
@
@
@
@
@
@
@
@
@
@
@
@
@
@
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
@v
f
@v
f
@v
f
f
@v
f
@v
@v
f
@v
f
f
@v
@v
f
@v
f
@v
f
Q: 1
Q: 1 2
Q: 1 2 5
Q: 1 2 5 4
Q: 2 5 4
Q: 5 4
Q: 5 4 3
Q: 5 4 3 6
Q: 4 3 6
Q: 3 6
Q: 6
10
1
4
v
f
@
f
v
@
@
f2
v
f3
v
5
f6
v
@v
f
Q:
Zauwa»my, »e po wywoªaniu BFS-Visit dla wierzchoªka
z
u
u
wierzchoªki, które nie s¡ osi¡galne
pozostan¡ nieodwiedzone (biaªe). Aby zatem przejrze¢ caªy graf, nale»y wywoªywa¢ BFS-
Visit dopóki b¦d¡ biaªe wierzchoªki.
Peªen przebieg algorytmu BFS jest realizowany przez poni»sz¡ procedur¦:
BFS(G)
1
begin
2
for ka»dy u
3
∈
V(G) do
kolor(u) := biaªy;
4
for ka»dy u
5
∈
V(G) do
if kolor(u) = biaªy then
6
BFS-Visit(G, u);
7
end;
• Zªo»ono±¢ czasowa: O(|V | + |E|)
• Zªo»ono±¢ pami¦ciowa: O(|V |)
5
(analogicznie jak dla DFS).
(przechowywanie kolorów, kolejka).
Zastosowania BFS
5.1 Najkrótsza droga
Zastosowanie:
znajdowanie najkrótszej drogi pomi¦dzy wierzchoªkami
Modykacja procedury BFS-Visit: przechowywanie
Tj. poprzednikiem wierzchoªka
v
π[v]
poprzednikiem wierzchoªka
Przygotowanie:
for ka»dy u
∈
V(G) do
begin
kolor(u) := biaªy;
π [u]
:=
∞
end;
5.2 Zmodykowany BFS
BFS-Visit(G, s)
1
begin
2
kolor(s) := szary;
3
Q := {s};
4
while Q <>
∅
do begin
5
u := Head(Q);
6
for ka»dy v
7
∈
Adj[u] do
if kolor(v) = biaªy then begin
8
kolor(v) := szary;
9
π [v]
10
end;
12
Dequeue(Q);
13
14
15
:= u;
Enqueue(Q, v)
11
kolor(u) := czarny
end
end
wektora poprzedników π .
na najkrótszej drodze z
jest wierzchoªek
s i v.
π[π[v]]...
s
do
v
jest wierzchoªek
π[v];
11
Przebieg algorytmu dla s = 1
w
1
2
3
4
5
6
π[w]
∞
∞
∞
∞
∞
∞
w
1
2
3
4
5
6
π[w]
∞
∞
∞
∞
∞
∞
w
1
2
3
4
5
6
π[w]
∞
1
∞
∞
∞
∞
w
1
2
3
4
5
6
π[w]
∞
1
∞
∞
1
∞
w
1
2
3
4
5
6
π[w]
∞
1
∞
1
1
∞
w
1
2
3
4
5
6
π[w]
∞
1
∞
1
1
∞
w
1
2
3
4
5
6
π[w]
∞
1
∞
1
1
∞
w
1
2
3
4
5
6
π[w]
∞
1
5
1
1
∞
1
v
f
@
4
f
v
1
v
f
@
4
f
v
1
v
f
@
4
f
v
1
v
f
@
4
f
v
1
v
f
@
4
f
v
1
v
f
@
4
f
v
1
v
f
@
4
f
v
1
v
f
@
4
f
v
@
@
@
@
@
@
@
@
@
@
@
@
@
@
@
@
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
@v
f
@v
f
f
@v
f
@v
f
@v
f
@v
@v
f
@v
f
Q:
Q: 1
Q: 1 2
Q: 1 2 5
Q: 1 2 5 4
Q: 2 5 4
Q: 5 4
Q: 5 4 3
12
w
1
2
3
4
5
6
π[w]
∞
1
5
1
1
5
w
1
2
3
4
5
6
π[w]
∞
1
5
1
1
5
w
1
2
3
4
5
6
π[w]
∞
1
5
1
1
5
w
1
2
3
4
5
6
π[w]
∞
1
5
1
1
5
w
1
2
3
4
5
6
π[w]
∞
1
5
1
1
5
1
v
f
@
4
f
v
1
v
f
@
4
f
v
1
v
f
@
4
f
v
1
v
f
@
4
f
v
1
v
f
@
4
f
v
@
@
@
@
@
@
@
@
@
@
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
f2
v
f3
v
5
f6
v
@v
f
@v
f
@v
f
f
@v
f
@v
Q: 5 4 3 6
Q: 4 3 6
Q: 3 6
Q: 6
Q:
5.3 Odczytanie dróg
π
Po wykonaniu BFS-Visit(G, s) w wektorze
¹ródªowego
s
do wszystkich wierzchoªków
w
1
w
1
2
3
4
5
6
Np. najkrótsza droga z
6 ← π[6] ← π[π[6]],
6 ← 5 ← 1.
s=1
f2
v
W naszym przykªadzie
s = 1:
f3
v
@
4
do
6
f
v
@ 5
@v
f
f6
v
to:
s=1
do
3
to:
czyli
Odczytanie i wypisanie drogi z
pseudokod:
v
f
@
s.
czyli
A najkrótsza droga z
3 ← π[3] ← π[π[3]],
3 ← 5 ← 1.
π[w]
∞
1
5
1
1
5
zakodowane s¡ najkrótsze drogi z wierzchoªka
osi¡galnych z
s
do
v
od ko«ca mo»e by¢ zrealizowane przez poni»szy
13
PrintPathRev(s, v)
1
begin
if s = v then wypisz(v)
2
π [v]
3
else if
4
else begin
5
=
∞
then wypisz('Nie ma drogi')
while v <> s do begin
6
wypisz(v);
7
v :=
8
π [v]
end;
wypisz(s)
9
10
end
11
end;
Jednak bardziej naturalnym byªoby wypisanie drogi z
s
do
v
od pocz¡tku. Do tego mo»e
posªu»y¢ poni»sza elegancka procedura rekurencyjna:
PrintPath(s, v)
1
begin
2
if s = v then wypisz(v)
3
else if
4
else begin
π [v]
=
∞
5
PrintPath(s,
6
wypisz(v)
7
then wypisz('Nie ma drogi')
π [v]);
end
8
end;
5.4 Podsumowanie
Je»eli interesuje nas najkrótsza droga pomi¦dzy dwoma ustalonymi wierzchoªkami
•
uruchamiamy BFS-Visit dla wierzchoªka
•
odczytujemy drog¦ z
Wektor
π
s
do
t
z wektora
s
(nie tylko
t!).
to:
s,
π.
b¦dzie zawieraª, jako skutek uboczny, najkrótsze drogi z
choªków osi¡galnych z
s i t,
s
do wszystkich wierz-
Wyliczania tej nadmiarowej informacji nie da si¦ tu
unikn¡¢.
Zauwa»my, »e tu przez najkrótsz¡ drog¦ rozumieli±my drog¦ o najmniejszej liczbie kraw¦dzi.
Mo»na rozwa»a¢ grafy, w których kraw¦dzie maj¡ dªugo±¢ (lub inny koszt) i poszukiwa¢
najkrótszych dróg wzgl¦dem tego parametru.
Tym zagadnieniem zajmiemy si¦ na nast¦pnym wykªadzie.