1. Algorytmy przeszukiwania. Przeszukiwanie wszerz iw głąb.

Transkrypt

1. Algorytmy przeszukiwania. Przeszukiwanie wszerz iw głąb.
1. Algorytmy przeszukiwania.
Przeszukiwanie wszerz i w głąb.
Algorytmy przeszukiwania w głąb i wszerz są najczęściej stosowanymi algorytmami przeszukiwania. Wykorzystuje się je do zbadania istnienia połączenie między
dwoma wierzchołkami w grafie. Aby zbadać istnienie połączenia w grafie podajemy dwa wierzchołki: pierwszy - zwany stanem początkowym, oraz drugi - zwany
stanem końcowym. W przypadku przeszukiwania grafów skończonych omawiane algorytmy są równoważne, tzn zbieżność algorytmu przeszukiwania w głąb pociąga za
sobą zbieżność algorytmu przeszukiwania wszerz i odwrotnie. Jeżeli przeszukujemy
graf nieskończony, algorytm przeszukiwania wszerz jest zbieżny, natomiast algorytm
przeszukiwania w głąb nie. Brak zbieżności ma miejsce wówczas, gdy przeszukujemy
krawiędź nieskończoną, która nie prowadzi do wierzchołka końcowego.
Omawiając algorytm posłużymi się przykładem. Zadamy konkretny graf oraz
stany: początkowy i końcowy i opiszemy przebieg działania algorytmu. Niech dany
będzie graf:
Rysunek 1: Graf do przeszukiwania.
Dowolny graf można opisać w postaci listy połączeń. Lista zawiera n linii, gdzie
n oznacza liczbę wierzchołków grafu. W i−tej linii są umiszczone numery wierzchołków, z którymi i−ty wierzchołek jest połączony. Lista połączeń dla rozważanego
grafu wygląda następująco:
1. 2, 6, 9
2. 1, 3
3. 2, 4
4. 3, 5
5. 4, 8, 11
6. 1, 7
1
7. 6, 8
8. 5, 7
9. 1, 10
10. 9, 11
11. 5, 10
Przeszukiwanie wszerz
Przeszukiwanie grafu wszerz polega na odwiedzaniu wszystkich wierzchołków
grafu sąsiadujących z wierzchołkiem początkowym, następnie wierzchołków w odległośi 2 od wierzchołka początkowego i tak kolejno. Przy każdym odwiedzeniu należy
sprawidzić, czy stan, w którym się znajdujemy jest stanem końcowym. Odwiedzając
kolejne wierzchołki należy pamiętać, żeby nie odwiedzać wierzchołków wcześniej odwiedzonych, tzn. każdy wierzchołek możemy odwiedzić dokładnie raz. W przypadku,
gdy odwiedzimy wszystkie możliwe wierzchołki i nie znajdziemy stanu końcowego,
nie istnieje droga miedzy szukanymi wierzchołkami. Zaletą algorytmu przeszukiwania wszerz jest to, że na pewno nie pominiemy żadnego wierzchołka, zwykle jednak
odwiedzamy za dużo wierzchołków, co jest wadą algorytmu.
Przeszukiwanie wszerz odbywa się przy użyciu kolejki FIFO (first in first out).
Algorytm przebiega następująco:
1. Utwórz kolejkę FIFO
2. Zapisz do kolejki stan początkowy
3. Pobierz z kolejki stan i nazwij go S
4. Jesli
(a) S jest poszukiwanym stanem końcowym zwróć SUKCES i zakończ algorytm
(b) S=NULL (lista jest pusta) zwróć BRAK ROZWIAZANIA i zakończ algorytm
(c) S nie jest poszukiwanym stanem końcowym to generuj wszystkie możliwe
stany następujące po S (które można wyprowadzić z S zgodnie z wcześniej
ustalonymi regułami a które nie były już rozważane) i zapisz je do kolejki
5. Idz do 3
Zadanie polega na stwierdzeniu, czy w wyżej podanym grafie wierzchołki 1 i 5
są połączone.
Wykorzystywane funkcje:
make fifo() - funkcja tworzy listę typu FIFO
put fifo(x) - funkcja dodaje element x do listy
get fifo() - funkcja pobiera elemet z listy
Oto przebieg wykonywania algorytmu:
2
Numer
etapu
1
2
3
Krok
algorytmu
1
2
3
4
4
5
6
7
5
3
4
8
9
10
5
3
4
11
12
13
5
3
4
14
15
16
5
3
4
17
18
19
5
3
4
20
21
22
5
3
4
23
24
25
5
3
4
26
27
28
5
3
4
29
30
31
5
3
4
32
33
34
5
3
4
Wykonywana
operacja
make fifo()
put fifo(1)
S:=get fifo() (S=1)
Wykonywanie punktu (c)
put fifo(2), put fifo(6),
put fifo(9)
Powrót do 3
S:=get fifo() (S=2)
Wykonywanie punktu (c)
put fifo(3)
Powrót do3
S:=get fifo() (S=6)
Wykonywanie punktu (c)
put fifo(7)
Powrót do 3
S:=get fifo() (S=9)
Wykonywanie punktu (c)
put fifo(10)
Powrót do 3
S:=get fifo() (S=3)
Wykonywanie punktu (c)
put fifo(4)
Powrót do 3
S:=get fifo() (S=7)
Wykonywanie punktu (c)
put fifo(8)
Powrót do 3
S:=get fifo() (S=10)
Wykonywanie punktu (c)
put fifo(11)
Powrót do 3
S:=get fifo() (S=4)
Wykonywanie punktu (c)
put fifo(5)
Powrót do 3
S:=get fifo() (S=8)
Wykonywanie punktu (c)
nic
Powrót do 3
S:=get fifo() (S=11)
Wykonywanie punktu (c)
nic
Powrót do 3
S:=get fifo() (S=5)
Wykonywanie punktu (c)
return(SUKCES)
3
Stan
kolejki
NULL
1
NULL
Odwiedzone
wierzchołki
NULL
1
1
269
1269
269
69
693
1269
1269
12369
693
93
937
12369
12369
123679
937
37
3 7 10
123679
123679
1 2 3 6 7 9 10
3 7 10
7 10
7 10 4
1 2 3 6 7 9 10
1 2 3 6 7 9 10
1 2 3 4 6 7 9 10
7 10 4
10 4
10 4 8
1 2 3 4 6 7 9 10
1 2 3 4 6 7 9 10
1 2 3 4 6 7 8 9 10
10 4 8
48
4 8 11
1 2 3 4 6 7 8 9 10
1 2 3 4 6 7 8 9 10
1 2 3 4 6 7 8 9 10 11
4 8 11
8 11
8 11 5
1 2 3 4 6 7 8 9 10 11
1 2 3 4 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9 10 11
8 11 5
11 5
11 5
1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9 10 11
11 5
5
5
1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9 10 11
5
NULL
NULL
1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9 10 11
Przeszukiwanie w głąb
Przeszukiwanie grafu w głąb polega na przeszukiwaniu poszczególnych krawędzi
grafu. Przechodzimy krawędz najdalej ja się da, jeżeli dana ścieżka nie doprowadziła
nas do wierzchołka końcowego wówczas cofamy sie do momentu, z którego możemy
pójść inną scieżką. Podobnie jak w przypadku przeszukiwania wszerz, przeszukując
graf metodą w głąb pojedynczy wierzchołek może być odwiedziny dokładnie jeden
raz. Przy każdym odwiedzeniu należy sprawidzić, czy nie znajdujemy się w stanie końcowym. W przypadku, gdy odwiedzimy wszystkie możliwe wierzchołki i nie
znajdziemy stanu końcowego, nie istnieje droga miedzy szukanymi wierzchołkami.
Zaletą algorytmu przeszukiwania w głąb jest to, że nie przeszukujemy wszystkich
wierzchołków grafu, dodatkowo przeszukując ścieżką prowadzącą bezpośrednio do
wierzchołka końcowego możemy odwiedzić minimalną ilość wierzchołków łączących
stan początkowy z końcowym. Wadą jest to, że zwykle przszukiwanie odbywa się
niewłaściwą scieżką co prowadzi do zabrnięcia w ”ślepą uliczkę”, z której należy się
wycofać do wierzchołka, z którego istnieje możliwość pójścia dalej.
Przeszukiwanie w głąb odbywa się przy użyciu kolejki LIFO (last in first out)
zwanej inaczej STOS. Algorytm przebiega następująco:
1. Utwórz STOS
2. Zapisz na stos stan początkowy
3. Pobierz ze stosu stan i nazwij go S
4. Jesli
(a) S jest poszukiwanym stanem końcowym zwróć SUKCES i zakończ algorytm
(b) S=NULL (lista jest pusta) zwróć BRAK ROZWIAZANIA i zakończ algorytm
(c) S nie jest poszukiwanym stanem końcowym to generuj wszystkie możliwe
stany następujące po S (które można wyprowadzić z S zgodnie z wcześniej
ustalonymi regułami a które nie były już rozważane) i zapisz je do kolejki
5. Idz do 3
Zadanie polega na stwierdzeniu, czy w wyżej podanym grafie wierzchołki 1 i 5
są połączone.
Wykorzystywane funkcje:
make stos() - funkcja tworzy STOS
put stos(x) - funkcja dodaje element x na stos
get stos() - funkcja pobiera elemet ze stosu
Oto przebieg wykonywania algorytmu:
4
Numer
Krok
etapu
1
2
3
algorytmu
1
2
3
4
4
5
6
5
3
7
8
9
4
5
3
10
11
12
4
5
3
13
14
15
16
4
5
3
4
Wykonywana
Stan
Odwiedzone
operacja
make stos()
put stos(1)
S:=get stos() (S=1)
Wykonywanie punktu (c)
put stos(2), put stos(6),
put stos(9)
Powrót do 3
S:=get stos() (S=9)
Wykonywanie punktu (c)
put stos(10)
Powrót do 3
S:=get stos() (S=10)
Wykonywanie punktu (c)
put stos(11)
Powrót do 3
S:=get stos() (S=11)
Wykonywanie punktu (c)
put stos(5)
Powrót do 3
S:=get stos() (S=5)
Wykonywanie punktu (a)
return(SUKCES)
kolejki
NULL
1
NULL
wierzchołki
NULL
1
1
269
1269
269
26
1269
1269
2 6 10
2 6 10
26
1 2 6 9 10
1 2 6 9 10
1 2 6 9 10
2 6 11
2 6 11
26
1 2 6 9 10 11
1 2 6 9 10 11
1 2 6 9 10 11
265
265
26
1 2 5 6 9 10 11
1 2 5 6 9 10 11
1 2 5 6 9 10 11
26
1 2 5 6 9 10 11
W tak realizowanym algorytmie poruszamy się wzdłuż jednej krawędzi ale dla
danego wierzchołka odwiedzamy wszystkich jego sąsiadów. Możemy skonstruować
algorytm tak, aby z danego wierzchołka generować tylko jeden (wybrany) stan następującey po nim. Wówczas algorytm będzie przebiegał następująco:
1. Utwórz STOS
2. Przyjmij S stan początkowy i zapisz na stos
3. Jesli
(a) S jest poszukiwanym stanem końcowym zwróć SUKCES i zakończ algorytm
(b) S=NULL (lista jest pusta) zwróć BRAK ROZWIAZANIA i zakończ algorytm
(c) S nie jest poszukiwanym stanem końcowym to:
i. jeśli istnieje możliwy stan następujący po S to S przyjmij ten stan i
zapisz na stos
ii. w przeciwnym przypadku zdejmij element ze stosu, następnie S przyjmij stan ze stosu (nie zdejmując elementu ze stosu)
4. Idz do 3
5
Zadanie polega na swierdzeniu, czy wierzchołki 1 i 11 są połączone.
Oto przebieg wykonywania algorytmu:
Numer
etapu
1
2
3
Krok
algorytmu
1
2
3
4
5
4
3
6
7
4
3
8
9
4
3
10
11
4
3
12
13
4
3
14
15
4
3
16
17
4
3
18
19
4
3
20
21
4
3
22
23
4
3
24
25
4
3
Wykonywana
operacja
make stos()
S=1; put stos(S)
Wykonywanie punktu i
S=2; put stos(S)
Powrót do 3
Wykonywanie punktu i
S=3; put stos(S)
Powrót do 3
Wykonywanie punktu i
S=4; put stos(S)
Powrót do 3
Wykonywanie punktu i
S=5; put stos(S)
Powrót do 3
Wykonywanie punktu i
S=8; put stos(S)
Powrót do 3
Wykonywanie punktu i
S=7; put stos(S)
Powrót do 3
Wykonywanie punktu i
S=6; put stos(S)
Powrót do 3
Wykonywanie punktu ii
S=get stos() put stos(S) (S=7)
Powrót do 3
Wykonywanie punktu ii
S=get stos() put stos(S) (S=8)
Powrót do 3
Wykonywanie punktu ii
S=get stos(); put stos(S) (S=5)
Powrót do 3
Wykonywanie punktu i
S=11; put stos(S)
Powrót do 3
Wykonywanie punktu a
return(SUKCES)
Stan
stosu
NULL
1
12
Odwiedzone
wierzchołki
NULL
1
12
12
123
12
123
123
1234
123
1234
1234
12345
1234
12345
12345
123458
12345
123458
123458
1234587
123458
1234587
1234587
12345876
1234587
12345876
12345876
1234587
12345876
12345876
1234587
123458
12345876
12345876
123458
12345
12345876
12345876
12345
1 2 3 4 5 11
12345876
1 2 3 4 5 8 7 6 11
1 2 3 4 5 11
1 2 3 4 5 11
1 2 3 4 5 8 7 6 11
1 2 3 4 5 8 7 6 11
Taki przebieg algorytmu oprócz odpowiedzi na pytanie czy dwa wierzchołki są ze
sobą połączone, generuje drogę prowadzacą od wierzchołka początkowego do wierzchołka końcowego (zwykle nie jest to optymalna droga). Jest to stan stosu w momencie zakończenia działania algorytmu.
6
Zadania
Zadanie będą polegały na zastosowaniu powyższych algorytmów do sprawdzenia,
czy dwa wierzchołki w grafie są ze sobą połączone.
Zadanie 1
Napisać program, w oparciu o padane algorytmy, sprawdzającey, czy dwa wierzchołki w grafie są połączone. Zakładamy, ze program wczytuje graf z pliku o podanej w linii poleceń nazwie. Następnie pyta o numery wierzchołków do sprawdzenia.
Wierzchołki numerujemy liczbami naturalnymi z przedziału [1, 100]. W pliku pierwsza linia zawiera liczbę wierzchołków, kolejne są listowym opisem grafu. Tak więc
linia 2 zawiera sopis wierzchołków, z którymi łączy się wierzchołek 1, linia 3 - spis
wierzchołków, z którymi łączy się wierzchołek 2, itd. Kolejne wierzchołki w linii
rozdzielone są spacją. Każda linia na końcu zawiera liczbę 0, która oznacza koniec
listy wierzchołków sąsiadujących z danym wierzchołkiem. Oto przykładowy plik z
danymi dla rozważanego w przykładach grafu:
11
2 6 9 0
1 3 0
2 4 0
3 5 0
4 8 11 0
1 7 0
6 8 0
5 7 0
1 10 0
9 11 0
5 10 0
Zadanie 2
Napisać program, w oparciu o podany algorytm, sprawdzający czy możliwe jest
przejście w labiryncie od jednego miejsca do drugiego. Zakładamy że program wczytuje labirynt z pliku o podanej w linii poleceń nazwie. Następnie pyta się o współrzędne pola startowego i końcowego. Maksymalny rozmiar planszy to 100 wierszy
i 100 kolumn. Każde pole na planszy ma numer z przedziału [0, 15]. Numer ten
oznacza jekiego typu jest pole, to znaczy gdzie możemy się z niego przemieścić. Oto
dostępne pola (lewe górne ma numer 0, prawe dolne - 15, numeracja wierszami):
Rysunek 2: Pola labryntu.
W pliku pierwsza linia zawiera liczbę wierszy, druga - liczbę kolumn, kolejne
natomiast to opis pól w danym wierszu. Tak więc linia 3 zawiera opis pól wiersza
7
1, linia 4 - opis pól wiersza drugiego, itd. Kolejne pola w wierszu rozdzielone są
spacjami. Oto przykładowy wygląd labiryntu i odpowiadającego mu pliku z danymi:
Rysunek 3: Plansza labiryntu.
4
5
8 9 12 8 5
4 5 14 2 13
4 3 9 0 5
13 15 15 7 6
Ilustrowany na ekranie - na przykład pola odwiedzone niech mają inny kolor. W
realizacji tekstowej program powinien wyświetlać (lub zapisywać do pliku) współrzędne odwiedzanych pól.
Uwaga
Jak łatwo zauważyć labirynt można utożsamiać z grafem. Poszczególne pola labiryntu są wierzchołkami, a rodzaj pola jednoznacznie definiuje listę wieszchołków
sąsiadujących z rozważanym. Dla powyższego labiryntu plik opisujący go jako graf
wygląda następująco:
20
2 6 0
1 3 0
2 0
5 9 0
4 10 0
1 7 11 0
6 12 0
9 0
4 8 14 0
5 0
6 12 16 0
7 11 13 0
12 14 0
9 13 15 19 0
14 20 0
11 0
0
0
14 20 0
15 19 0
8