Zmanjšaj in vladaj

RAČUNALNIŠTVO IN INFORMACIJSKE TEHNOLOGIJE
OSNOVE ALGORITMOV
NIKOLA GUID
Fakulteta za elektrotehniko,
računalništvo in informatiko
Maribor, 2011
Kazalo
2 Zmanjšaj-in-vladaj
2-1
2.1 Urejanje z vrivanjem . . . . . . . . . . . . . . . . . . . . . . . . . . 2-3
2.2 Iskanje z razvijanjem v širino . . . . . . . . . . . . . . . . . . . . . 2-6
2.3 Iskanje z razvijanjem v globino . . . . . . . . . . . . . . . . . . . . 2-13
Poglavje
2
Zmanjšaj-in-vladaj
Strategija zmanjšaj-in-vladaj (decrease-and-conquer ) izkorišča odnos med rešitvijo danega primerka problema in rešitvijo manjšega primerka istega problema
[Levitin, 2007]. Po določitvi tega odnosa problem rešujemo bodisi od zgoraj
navzdol (rekurzivno) bodisi od spodaj navzgor (brez rekurzije). Obstajajo tri
glavne variante pristopa zmanjšaj-in-vladaj:
1. zmanjšaj za konstanto,
2. zmanjšaj za konstantni faktor in
3. zmanjšaj za spremenljivo velikost.
1. Zmanjšaj za konstanto
V varianti zmanšaj za konstanto se velikost primerka zmanjša za isto konstanto
pri vsaki iteracijo algoritma. Tipično je ta konstanta enaka 1.
Zgled 2.1. Problem je izračun potence an za n > 0. Odnos med rešitvijo primerka
velikosti n in primerka velikosti n − 1 je določen s formulo: an = an−1 · a, kar
pomeni, da velikost problema zmanjšamo za 1. Funkcijo f (n) = an lahko izračunamo bodisi od zgoraj navzdol s pomočjo rekurzivne enačbe:
½
f (n − 1) · a, ˇce n > 1
f (n) =
(2.1)
a,
ˇce n = 1
bodisi od spodaj navzgor, tako da množimo a med sabo (n − 1)-krat. ♦
2. Zmanjšaj za konstantni faktor
Varianta zmanšaj za konstantni faktor zmanjšuje problem pri vsaki iteraciji za
konstantni faktor. V večini aplikacij je ta faktor dva.
2-2
Zgled 2.2. Vrnimo se na problem izračuna potence an za n > 0. Če je primerek
velikosti n izračun an , je primerek polovične velikosti izračun an/2 , kar pomeni,
da velikost problema zmanjšamo za dvakrat. Med njima velja naslednja relacija:
an = (an/2 )2 . Slednja relacija velja samo, če je n sodo število. Če je n liho število,
moramo rezultat na desni strani enačbe pomnožiti še z a. Povzamimo celotno
formulo:
 n/2 2
ˇce je n sodo in pozitivno ˇstevilo
 (a ) ,
n
n/2
2
(2.2)
a =
(a ) · a, ˇce je n liho in veˇcje od 1

a,
ˇce n = 1
Če izračunamo an rekurzivno po formuli 2.2, lahko pričakujemo časovno zahtevnost O(log n), ker se pri vsaki iteraciji velikost reducira za najmanj polovico.
Izračunajmo npr. a16 . Najprej moramo izračunati a8 (saj je a16 = (a8 )2 ),
nato a4 (saj je a8 = (a4 )2 ), nato a2 (saj je a4 = (a2 )2 ) in na koncu še a1 (saj je
a2 = (a1 )2 ). Skupaj smo imeli 4 izračune, kar ustreza vrednosti log2 16 = 4. ♦
Po strategiji deli-in-vladaj se problem prevede na reševanje dveh podproblemov velikosti n/2:
½ bn/2c dn/2e
a
·a
, ˇce n > 1
n
a =
(2.3)
a,
ˇce n = 1
Algoritem, ki temelji na formuli 2.3, je mnogo počasnejši od algoritma, temelječega
na formuli 2.2.
3. Zmanjšaj za spremenljivo velikost
V varianti zmanjšaj za spremenljivo velikost se velikost zmanjšanja spreminja pri
vsaki iteraciji.
Zgled 2.3. Evklidov algoritem za izračun največjega skupnega delitelja je primer
te variante. Algoritem temelji na formuli
gcd(m, n) = gcd(n, m mod n).
Argumenti na desni strani so vedno manjši od tistih na levi strani. Manjši niso
niti za konstanto niti za konstantni faktor. ♦
2.1 Urejanje z vrivanjem
2.1
2-3
Urejanje z vrivanjem
Uporabimo strategijo zmanjšaj-in-vladaj (varianta zmanjšaj za konstanto, ki je
ena) pri urejanju zaporedja A[1..n]. Predpostavimo, da je manjše zaporedje
A[1..n − 1] že urejeno, tako da velja A[1] ≤ A[2] ≤ . . . ≤ A[n − 1]. Če hočemo
urediti zaporedje A[1..n], je potrebno samo postaviti element A[n] na pravo mesto
(pred prvim večjim elementom), saj so vsi drugi elementi že urejeni po velikosti.
Na tej ideji urejanja temelji algoritem urejanje z vrivanjem (insertion sort),
ki je učinkovit algoritem za urejanje manjšega števila elementov. Čeprav ideja urejanja z vrivanjem temelji na rekurziji, algoritem implementiramo lažje od spodaj
navzgor, tj. iterativno.
Delovanje algoritma lahko ponazorimo z urejanjem kart. Ko igralec prejme
sveženj kart, jih mora urediti običajno po številkah (po moči ali pomembnosti
posameznih kart). Igralec (desničar) drži karte v levi roki, z desno pa vriva karte
na pravilno mesto. Denimo, da se igralec odloči, da bo vrednost njegovih kart
tekla z leve proti desni. Tedaj vzame običajno v desno roko drugo karto, gledano
z leve, in jo pusti na mestu, če je ta večja od leve karte, sicer jo postavi pred levo
karto. Zatem vzame tretjo karto z leve in jo vrine v že urejeno zaporedje levo od
te karte. V splošnem j-to karto z leve vrine v urejeno zaporedje A[1..j − 1], pri
čemer je j = 2, 3, . . . , n, kjer je n velikost zaporedja (polja) A.
Delovanje algoritma opisuje naslednji psevdokod (Rezultat je urejeno zaporedje,
ki je postavljeno v isto polje A):
UREJANJE-Z-VRIVANJEM(A)
1 for j ← 2 to n
% vrini izbrano vrednost A[j] v urejeno zaporedje A[1..j − 1]
2
do kljuc ← A[j]
3
i←j−1
4
while i > 0 and A[i] > kljuc
5
do A[i + 1] ← A[i] % večjo številko prestavi desno
6
i←i−1
7
A[i + 1] ← kljuc
% ključ postavi na pravo mesto
Zgled 2.4. Dano je zaporedje A = h7, 1, 3, 8, 0, 2i. Kako deluje na njem
procedura UREJANJE-Z-VRIVANJEM, kaže slika 2.1.
1. Vrstica 1: Najprej vstopimo v prvi cikel zanke for, tako da postavimo j = 2.
2. Vrstica 2: Postavimo kljuc = A[j] = A[2] = 1.
3. Vrstica 3: Postavimo i = j − 1 = 1
2-4
2.1 Urejanje z vrivanjem
7
1
3
8
0
2
1
7
3
8
0
2
1
3
7
8
0
2
1
3
7
8
0
2
0
1
3
7
8
2
0
1
2
3
7
8
končano
Slika 2.1: Delovanje procedure UREDI-Z-VRIVANJEM
4. Vrstica 4: Vstopimo v zanko while. V prvi iteraciji te zanke sta oba pogoja
izpolnjena (i > 0 (1>0) in A[1] > kljuc (7>1).
5. Vrstica 5: Izvedi: A[2] = A[1] = 7.
6. Vrstica 6: Dekrementiraj i: i = 0.
7. Vrstica 4: Vstopimo v zanko while. Ker ne velja več i > 0, izstopimo iz
zanke.
8. Vrstica 7: Sedaj postavimo ključ na pravo mesto: A[1] = kljuc = 1.
Trenutni rezultat je zaporedje A = h1, 7, 3, 8, 0, 2i.
9. Vrstica 1: Vstopimo v drugi cikel zanke for, tako da postavimo j = 3.
10. Vrstica 2: Postavimo kljuc = A[j] = A[3] = 3.
11. Vrstica 3: Postavimo i = j − 1 = 2.
12. Vrstica 4: Vstopimo v zanko while. V prvi iteraciji te zanke sta oba pogoja
izpolnjena (i > 0 (2>0) in A[2] > kljuc (7>3)).
13. Vrstica 5: A[3] = A[2] = 7.
14. Vrstica 6: Dekrementiraj i: i = 1.
2-5
2.1 Urejanje z vrivanjem
15. Vrstica 4: Vstopimo v drugo iteracijo zanke while. Drugi pogoj ni izpolnjen
(A[1] ni večje od kljuc (ne velja 1>3)), zato izstopimo iz zanke.
16. Vrstica 7: Sedaj postavimo ključ na pravo mesto: A[2] = kljuc = 3.
Trenutni rezultat je zaporedje A = h1, 3, 7, 8, 0, 2i.
17. Vrstica 1: Vstopimo v tretji cikel zanke for, tako da postavimo j = 4.
18. Vrstica 2: Nadaljujemo na zgoraj opisan način do končne ureditve zaporedja.
Najprej postavimo na pravo mesto element 8, nato 0 in na koncu element
2, saj se algoritem izvede do j = 6. Rezultat algoritma je urejeno zaporedje
A = h0, 1, 2, 3, 7, 8i (slika 2.1). ♦
Časovna zahtevnost procedure UREJANJE-Z-VRIVANJEM
Zanka for se izvede ne glede na urejenost zaporedja (n − 1)-krat. Izvajanje notranje zanke while je odvisno od urejenosti zaporedja.
Če je zaporedje urejeno nepadajoče, se ne izvede nikoli. Tedaj dobimo najugodnejši primer oz. spodnjo mejo časovne zahtevnosti:
T (n) = Ω(n).
Če je zaporedje urejeno nerastoče, se notranja zanka izvede na vsakem koraku
(j − 1)-krat. Tedaj dobimo zgornjo mejo časovne zahtevnosti:
T (n) = O(n2 ).
V splošnem primeru urejenosti zaporedja se da določiti (precej zahtevno!)
poprečno časovno zahtevnost:
TA (n) = O(n2 ).
2.2 Iskanje z razvijanjem v širino
2.2
2-6
Iskanje z razvijanjem v širino
Iskanje z razvijanjem v širino (breadth-first search) je eden najpreprostejših
algoritmov za preiskovanje grafa. V danem grafu G = (V, E) podamo izhodišče
(source vertex ) ali začetno vozlišče s. Algoritem sistematično preiskuje povezave
v G in odkriva vsako vozlišče, ki je dosegljivo iz s. Izračuna razdaljo (najmanjše
število povezav) iz s do vseh dosegljivih vozlišč. Določa tudi drevo razvijanja v
širino s korenom s in z vsemi dosegljivimi vozlišči. Vsaka pot iz s do vozlišča v
je najkrajša pot iz s v v, če so uteži vseh povezav enake. Algoritem deluje na
neusmerjenem in usmerjenem grafu.
Osnovna računska operacija je razvoj (expansion) vozlišča, kar pomeni, da
tvorimo vse njegove naslednike (ki še niso bili tvorjeni), preden začnemo obravnavati drugo vozlišče. Pri napredovanju algoritma si pomagamo z barvami. Vozlišče
ima lahko belo, sivo ali črno barvo. Na začetku so vsa vozlišča bela, kasneje
postanejo siva in črna. Siva barva pomeni, da je vozlišče tvorjeno (generated
[Nilsson, 1980]) oz. odkrito (discovered [Cormen et al., 2007]). Črna barva nekega
vozlišča pomeni, da so bili tvorjeni in obravnavani vsi njegovi sosedi. V žargonu
teorije grafov to pomeni, da je bilo vozlišče razvito.
Dani algoritem lahko smatramo kot primer uporabe strategije zmanjšaj-invladaj z varianto zmanjšaj za konstanto, ki je ena (na vsakem koraku razvijamo
eno vozlišče). Začnemo z razvijanjem začetnega vozlišča. Algoritem se zaključi,
ko smo razvili vsa vozlišča grafa.
Barva vozlišča u je shranjena v spremenljivki barva[u]. Očeta od vozlišča u
označuje spremenljivka oce[u]. Razdaljo od začetnega vozlišča s do vozlišča u
hranimo v spremenljivki d[u]. Algoritem uporablja vrsto Q s strežnim pravilom
FIFO. Vrsta služi za upravljanje množice sivih (tvorjenih) vozlišč.
2.2 Iskanje z razvijanjem v širino
2-7
ISKANJE-Z-RAZVIJANJEM-V-SIRINO(G, s)
1 for vsako vozlišče u ∈ V − {s}
2
do barva[u] ←BELA
3
d[u] ← ∞
4
oce[u] ← NIL
5 barva[s] ←SIVA
6 d[s] ← 0
7 oce[s] ← NIL
8 Q←∅
9 VSTAVI-V-VRSTO(Q, s)
10 while Q 6= ∅
% dokler vrsta Q ni prazna
11
do u ← IZLOCI-IZ-VRSTE(Q) % izloči u iz vrste Q
% tvori vse sosede od vozlišča u, ki imajo belo barvo
12
for vsako vozlišče v ∈ Adj[u] % iz seznama sosedov Adj[u]
13
do if barva[v] = BELA
14
then barva[v] ← SIVA
15
d[v] ← d[u] + 1
16
oce[v] ← u
17
VSTAVI-V-VRSTO(Q, v)
18
barva[u] ← ČRNA
% vozlišče u je že razvito
Vrstice 1–4 v proceduri ISKANJE-Z-RAZVIJANJEM-V-SIRINO pobarvajo vsako
vozlišče u razen vozlišče s belo, postavijo d[u] v neskončnost in očeta od u (oce[u])
na NIL. V vrstici 5 se pobarva začetno vozlišče s sivo. Vrstica 6 postavi d[s] na
nič, vrstica 7 pa postavi očeta od s na NIL. Potem ko se v vrstici 8 izprazni vrsta
Q, se v vrstici 9 vstavi v vrsto Q vozlišče s. V vrsti Q so venomer samo siva
vozlišča.
Glavno zanko (while) programa predstavljajo vrstice od 10–18. Zanka se izvaja, dokler je še kakšno sivo vozlišče v vrsti Q. Vrstica 11 izbere sivo vozlišče iz
glave vrste Q. Zanka for v vrsticah 12–17 obravnava vsako vozlišče v iz seznama
sosedov vozlišča u. Če je vozlišče v belo, potem še ni bilo tvorjeno in algoritem ga
obdela v vrsticah od 14–17. Najprej vozlišče pobarva sivo, nato postavi razdaljo
d[v] na vrednost d[u] + 1, postavi očeta od v (oce[v] = u) in prenese v na rep vrste
Q. Ko so pregledana vsa vozlišča iz seznama sosedov vozlišča u, u pobarvamo
črno (vrstica 18).
2.2 Iskanje z razvijanjem v širino
2-8
Zgled 2.5. Dan je neusmerjen graf na sliki 2.2a, na katerem želimo izvesti algoritem ISKANJE-Z-RAZVIJANJEM-V-SIRINO. Predpostavimo, da na tekočem koraku
poiščemo najprej sinove, ki imajo manjšo številko vozlišča. Naj bo izhodišče vozlišče 2. Algoritem deluje na naslednji način:
1. Vrstice 1–4: Vsa vozlišča razen začetno vozlišče 2 pobarvaj belo, postavi
njihovo razdaljo na vrednost ∞ in njihove očete na NIL.
2. Vrstice 5–9: Pobarvaj vozlišče 2 s sivo barvo, postavi njegovo razdaljo na
vrednost 0, njegovega očeta na NIL in po izpraznitvi vrste Q postavi vozlišče
2 v vrsto Q. Stanje v tej točki prikazuje slika 2.2b.
3. Vrstica 10: Vstop v prvi cikel zanke while. Vozlišče 2 izloči iz vrste Q.
Vozlišče u postane vozlišče 2. V zanki for (vrstice 12–17) obravnavaj oba
soseda od 2, tj. 1 in 5, ki sta pobarvana belo (pobarvaj ju sivo, določi njuni
razdalji in njuna očeta). Postavi ju tudi v vrsto Q. V vrstici 18 pobarvaj
vozlišče 2 črno. Stanje na koncu tega cikla prikazuje slika 2.2c.
4. Vrstica 10: Vstop v drugi cikel zanke while. Vozlišče u postane vozlišče
1, ki je vozlišče v glavi vrste Q. V zanki for (vrstice 12–17) obravnavaj
soseda od 1, tj. vozlišče 4, ki je pobarvano belo (pobarvaj ga sivo, določi
njegovo razdaljo in očeta). Postavi vozlišče 4 v vrsto Q. Njegov sosed je
tudi vozlišče 2, ki pa je pobarvano črno, zato ga ne obravnavaj. V vrstici
18 pobarvaj vozlišče 1 črno. Stanje na koncu tega cikla prikazuje slika 2.2d.
5. Vrstica 10: Vstop v tretji cikel zanke while. Vozlišče u postane vozlišče
5, ki je vozlišče v glavi vrste Q. V zanki for (vrstice 12–17) obravnavaj
oba soseda od 5, tj. vozlišči 3 in 6, ki sta pobarvana belo (pobarvaj ju sivo,
določi njuno razdaljo, njuna očeta in ju postavi v vrsto Q). Njegov sosed
je tudi vozlišče 4, ki je pobarvano sivo, in vozlišče 2, ki pa je pobarvano
črno, zato ju ne obravnavaj. V vrstici 18 pobarvaj vozlišče 5 črno. Stanje
na koncu tega cikla prikazuje slika 2.2e.
6. Vrstica 10: Vstop v četrti cikel zanke while. Vozlišče u postane vozlišče 4,
ki je vozlišče v glavi vrste Q. Vstopi v zanko for (vrstice 12–17), ki pa se
ne izvede, saj vozlišče 4 nima belih sosedov. Vozlišče 4 ima dva soseda, 2
in 5, in oba sta črna. Zato takoj vstopi v vrstico 18 in pobarvaj vozlišče 4
črno. Stanje na koncu tega cikla prikazuje slika 2.2f.
7. Vrstica 10: Vstop v peti cikel zanke while. Vozlišče u postane vozlišče 3, ki
je vozlišče v glavi vrste Q. Vstopi v zanko for (vrstice 12–17), ki pa se ne
izvede, saj vozlišče 3 nima belih sosedov. Vozlišče 3 ima dva soseda: sosed
5 je črn, sosed 6 je siv. Zato takoj vstopi v vrstico 18 in pobarvaj vozlišče
3 črno. Stanje na koncu tega cikla prikazuje slika 2.2g.
2.2 Iskanje z razvijanjem v širino
2-9
8. Vrstica 10: Vstop v šesti cikel zanke while. Vozlišče u postane vozlišče 6,
ki je vozlišče v glavi vrste Q. Vstopi v zanko for (vrstice 12–17), ki pa
se ne izvede, saj vozlišče 6 nima belih sosedov. Vozlišče 6 ima dva soseda,
tj. vozlišče 3 in 5, in oba sta črna. Zato takoj vstopi v vrstico 18 in pobarvaj
vozlišče 6 črno. Stanje na koncu tega cikla prikazuje slika 2.2h.
9. Vrstica 10: Ker je vrsta Q sedaj prazna, se algoritem ISKANJE-Z-RAZVIJANJEM-V-SIRINO zaključi.
2.2 Iskanje z razvijanjem v širino
2-10
Slika 2.2: Delovanje algoritma ISKANJE-Z-RAZVIJANJEM-V-SIRINO na neusmerjenem grafu. Povezave drevesa so poudarjene. Znotraj kroga, ki predstavlja vozlišče, je prikazana številka vozlišča, zunaj ob vozlišču pa
razdalja d do izhodišča 2. a) neusmerjen graf, b) stanje po vrstici 9,
c)–h) stanje po koncu ustrezne iteracije zanke while.
2-11
2.2 Iskanje z razvijanjem v širino
Procedura ISKANJE-Z-RAZVIJANJEM-V-SIRINO določi vsakemu vozlišču u ∈ V
njegovega očeta oce[u] = v, pri čemer je tudi v ∈ V . Nadalje izračuna še razdaljo
d[u] od vozlišča s do vsakega ostalega vozlišča u ∈ G. Razdalja d[u] se meri v
številu povezav med vozliščem s in u. Iz danih očetov in razdalj (preglednica
2.1) lahko rekonstruiramo drevo razvijanja v širino (slika 2.3). Koren predstavlja
začetno vozlišče s. ♦
Preglednica 2.1: Rezultati procedure ISKANJE-Z-RAZVIJANJEM-V-SIRINO: vrstni
red razvoja vozlišč, očetje in razdalje vozlišč.
zap. št.
razvoja
1
2
3
4
5
6
vozlišče
u
2
1
5
4
3
6
oce[u]
d[u]
NIL
2
2
1
5
5
0
1
1
2
2
2
Slika 2.3: Drevo razvijanja v širino. Številka ob vozlišču predstavlja zaporedno
številko razvoja.
2-12
2.2 Iskanje z razvijanjem v širino
Časovna zahtevnost
Operacija vstavljanja in izločanja iz vrste za eno vozlišče zahteva O(1) časa,
za vsa vozlišča pa O(|V |) časa. Seznam sosedov za vsako vozlišče se prebira
samo enkrat. Ker je vsota dolžin vseh seznamov sosedov velikosti Θ(|E|), se
porabi za prebiranje seznamov sosedov skupaj O(|E|) časa. Inicializacija algoritma ISKANJE-Z-RAZVIJANJEM-V-SIRINO zahteva O(|V |) časa. Tako je skupna
časovna zahtevnost algoritma enaka:
T (n) = O(|V | + |E|).
Torej algoritem ISKANJE-Z-RAZVIJANJEM-V-SIRINO ima časovno zahtevnost
linearno odvisno od velikosti seznama sosedov.
Če je graf podan z matriko sosednosti, je časovna zahtevnost algoritma:
T (n) = O(|V |2 ).
2.3 Iskanje z razvijanjem v globino
2.3
2-13
Iskanje z razvijanjem v globino
Iskanje z razvijanjem v globino (depth-first search) preiskuje graf v globino,
dokler je možno. V danem grafu G = (V, E) podamo izhodišče (source vertex ) ali
začetno vozlišče s. Algoritem sistematično preiskuje povezave v G in odkriva
vsako vozlišče, ki je dosegljivo iz s. Izračuna razdaljo iz s do vseh dosegljivih
vozlišč. Določa tudi drevo razvijanja v globino s korenom s in z vsemi dosegljivimi
vozlišči. Algoritem deluje na neusmerjenem in usmerjenem grafu.
Osnovna računska operacija je razvoj (expansion) vozlišča, kar pomeni, da
tvorimo vse njegove naslednike (ki še niso bili tvorjeni), preden začnemo obravnavati drugo vozlišče. Pri napredovanju algoritma si pomagamo z barvami. Vozlišče
ima lahko belo, sivo ali črno barvo. Na začetku so vsa vozlišča bela, kasneje
postanejo siva in črna. Siva barva pomeni, da je vozlišče tvorjeno. Črna barva
nekega vozlišča pomeni, da so bili tvorjeni in obravnavani vsi njegovi sosedi. V
žargonu teorije grafov to pomeni, da je bilo vozlišče razvito.
Dani algoritem lahko obravnavamo kot primer uporabe strategije zmanjšaj-invladaj z varianto zmanjšaj za konstanto, ki je ena (na vsakem koraku razvijamo
eno vozlišče). Algoritem se zaključi, ko smo razvili vsa vozlišča grafa.
Barva vozlišča u je shranjena v spremenljivki barva[u]. Očeta od vozlišča u
označuje spremenljivka oce[u]. Razdaljo od začetnega vozlišča s do vozlišča u
hranimo v spremenljivki d[u]. Algoritem uporablja sklad S s strežnim pravilom
LIFO. Sklad služi za upravljanje množice sivih (tvorjenih) vozlišč.
2.3 Iskanje z razvijanjem v globino
2-14
ISKANJE-Z-RAZVIJANJEM-V-GLOBINO(G, s)
1 for vsako vozlišče u ∈ V − {s}
2
do barva[u] ←BELA
3
d[u] ← ∞
4
oce[u] ← NIL
5 barva[s] ←SIVA
6 d[s] ← 0
7 oce[s] ← NIL
8 S←∅
9 POTISNI-NA-SKLAD(S, s)
10 while S 6= ∅
% dokler sklad S ni prazen
11
do u ← POVLECI-IZ-SKLADA(S)
% izloči u iz sklada S
% tvori vse sosede od vozlišča u, ki imajo belo barvo
12
for vsako vozlišče v ∈ Adj[u] % iz seznama sosedov Adj[u]
13
do if barva[v] = BELA
14
then barva[v] ← SIVA
15
d[v] ← d[u] + 1
16
oce[v] ← u
17
POTISNI-NA-SKLAD(S, v)
18
barva[u] ← ČRNA
% vozlišče u je že razvito
Vrstice 1–4 v proceduri ISKANJE-Z-RAZVIJANJEM-V-GLOBINO pobarvajo vsako
vozlišče u razen vozlišče s belo, postavijo d[u] v neskončnost in očeta od u (oce[u])
na NIL. V vrstici 5 se pobarva začetno vozlišče s sivo. Vrstica 6 postavi d[s] na
nič, vrstica 7 pa postavi očeta od s na NIL. Ko izpraznimo sklad S (vrstica 8),
postavimo vozlišče s v sklad S (vrstica 9). V skladu S so samo siva vozlišča.
Glavno zanko (while) programa predstavljajo vrstice od 10–18. Zanka se izvaja, dokler je še kakšno sivo vozlišče v skladu S. Vrstica 11 izbere sivo vozlišče z
vrha sklada S. Zanka for v vrsticah 12–17 obravnava vsako vozlišče v iz seznama
sosedov vozlišča u. Če je vozlišče v belo, potem še ni bilo tvorjeno in algoritem
ga obdela v vrsticah od 14–17. Najprej vozlišče pobarva sivo, nato postavi razdaljo d[v] na vrednost d[u] + 1, postavi očeta od v (oce[v] = u) in prenese v na
vrh sklada S. Ko so pregledana vsa vozlišča iz seznama sosedov vozlišča u, se
pobarva vozlišče u črno (vrstica 18).
2.3 Iskanje z razvijanjem v globino
2-15
Zgled 2.6. Dan je neusmerjen graf na sliki 2.2a, na katerem želimo izvesti algoritem ISKANJE-Z-RAZVIJANJEM-V-GLOBINO. Predpostavimo, da na tekočem koraku poiščemo najprej sinove, ki imajo manjšo številko vozlišča. Naj bo izhodišče
vozlišče 2. Slika 2.4 prikazuje celotno delovanje algoritma:
1. Vrstice 1–4: Vsa vozlišča razen začetno vozlišče 2 pobarvaj belo, postavi
njihovo razdaljo na vrednost ∞ in njihove očete na NIL.
2. Vrstice 5–9: Pobarvaj vozlišče 2 sivo, postavi njegovo razdaljo na vrednost
0, njegovega očeta na NIL in postavi 2 v sklad S. Stanje v tej točki prikazuje
slika 2.4a.
3. Vrstica 10: Vstop v prvi cikel zanke while. Vozlišče u postane vozlišče 2. V
zanki for (vrstice 12–17) obravnavaj oba soseda od 2, tj. vozlišče 1 in 5, ki
sta pobarvana belo (pobarvaj ju sivo, določi njuni razdalji in njuna očeta).
Postavi ju tudi v sklad S. V vrstici 18 pobarvaj vozlišče 2 črno. Stanje na
koncu tega cikla prikazuje slika 2.4b.
4. Vrstica 10: Vstop v drugi cikel zanke while. Vozlišče u postane vozlišče 5, ki
je vozlišče na vrhu sklada S. V zanki for (vrstice 12–17) obravnavaj sosede
od 5, tj. 3, 4 in 6, ki so pobarvani belo (pobarvaj jih sivo, določi njihove
razdalje in očeta). Njegov sosed je tudi vozlišče 2, ki pa je pobarvano črno,
zato ga ne obravnavaj. Postavi vozlišča 2, 3 in 6 na vrh sklada S. V vrstici
18 pobarvaj vozlišče 5 črno. Stanje na koncu tega cikla prikazuje slika 2.4c.
5. Vrstica 10: Vstop v tretji cikel zanke while. Vozlišče u postane vozlišče 6,
ki je vozlišče na vrhu sklada S. Zanka for (vrstice 12–17) se ne izvede, ker
vozlišče 6 nima belih sosedov. Sosed 3 je siv, sosed 5 pa je črn, zato ju ne
obravnavaj. V vrstici 18 pobarvaj vozlišče 6 črno. Stanje na koncu tega
cikla prikazuje slika 2.4d.
6. Vrstica 10: Vstop v četrti cikel zanke while. Vozlišče u postane vozlišče
4, ki je vozlišče na vrhu sklada S. Zanka for (vrstice 12–17) se ne izvede,
ker vozlišče 4 nima belih sosedov. Sosed 1 je siv, sosed 5 pa je črn, zato ju
ne obravnavaj. V vrstici 18 pobarvaj vozlišče 4 črno. Stanje na koncu tega
cikla prikazuje slika 2.4e.
7. Vrstica 10: Vstop v peti cikel zanke while. Vozlišče u postane vozlišče 3,
ki je vozlišče na vrhu sklada S. Zanka for (vrstice 12–17) se ne izvede, ker
vozlišče 3 nima belih sosedov. Soseda 5 in 6 sta črna, zato ju ne obravnavaj.
V vrstici 18 pobarvaj vozlišče 3 črno. Stanje na koncu tega cikla prikazuje
slika 2.4f.
8. Vrstica 10: Vstop v šesti cikel zanke while. Vozlišče u postane vozlišče 1,
ki je edino vozlišče v skladu S. Zanka for (vrstice 12–17) se ne izvede, ker
2.3 Iskanje z razvijanjem v globino
2-16
vozlišče 1 nima belih sosedov. Soseda 2 in 4 sta črna, zato ju ne obravnavaj.
V vrstici 18 pobarvaj vozlišče 1 črno. Stanje na koncu tega cikla prikazuje
slika 2.4g.
9. Vrstica 10: Ker je sklad S sedaj prazen, se algoritem ISKANJE-Z-RAZVIJANJEM-V-GLOBINO zaključi.
2-17
2.3 Iskanje z razvijanjem v globino
0
¥
0
¥
3
2
1
1
1
1
5
S
5
¥
2
4
¥
¥
3
2
4
¥
6
¥
6
¥
b)
a)
0
1
0
2
2
3
11
6
4
S 3
1
1
5
2 4
S 5
1
2
2
3
1 1
1
4
S 3
1
1
5
2 4
6
2
6
2
d)
c)
0
11
0
2
2
1
3
1
1
2
2
3
11
1
5
2
2 4
S
1 5
S 3
1
1
2 4
6
6
2
f)
e)
0
2
2
3
1
1
1
5
S
2
2 4
6
g)
Slika 2.4: Delovanje algoritma ISKANJE-Z-RAZVIJANJEM-V-GLOBINO na neusmerjenem grafu. Znotraj kroga, ki predstavlja vozlišče, je prikazana
številka vozlišča, zunaj ob vozlišču pa razdalja d do izhodišča 2. a)
stanje po vrstici 9, b)–g) stanje po koncu ustrezne iteracije zanke
while.
2-18
2.3 Iskanje z razvijanjem v globino
Procedura ISKANJE-Z-RAZVIJANJEM-V-GLOBINO določi vsakemu vozlišču u ∈
V njegovega očeta oce[u] = v, pri čemer je tudi v ∈ V . Nadalje izračuna še
razdaljo d[u] od vozlišča s do vsakega ostalega vozlišča u ∈ G. Razdalja d[u] se
meri v številu povezav med vozliščem s in u. Iz danih očetov in razdalj (preglednica 2.2) lahko rekonstruiramo drevo razvijanja v globino (slika 2.5). Koren
predstavlja začetno vozlišče s. ♦
Preglednica 2.2:
Rezultati procedure ISKANJE-Z-RAZVIJANJEM-V-GLOBINO:
vrstni red razvoja vozlišč, očetje in razdalje vozlišč d[u].
zap. št.
razvoja
1
2
3
4
5
6
vozlišče
u
2
5
6
4
3
1
oce[u]
d[u]
NIL
2
5
5
5
2
0
1
2
2
2
1
Slika 2.5: Drevo razvijanja v globino. Številka ob vozlišču predstavlja zaporedno
številko razvoja.
2-19
2.3 Iskanje z razvijanjem v globino
Časovna zahtevnost
Operaciji POTISNI-NA-SKLAD in POVLECI-IZ-SKLADA za eno vozlišče zahtevata
O(1) časa, za vsa vozlišča pa O(|V |) časa. Vse ostalo je enako kot pri algoritmu ISKANJE-Z-RAZVIJANJEM-V-SIRINO. Tako je skupna časovna zahtevnost
algoritma enaka, če je graf podan s seznamom sosedov:
T (n) = O(|V | + |E|),
kar pomeni linearno odvisnost od velikosti seznama sosedov.
Če je graf podan z matriko sosednosti, je časovna zahtevnost algoritma:
T (n) = O(|V |2 ).
Literatura
Aho, A. V., Hopcroft, J. E., and Ullman, J. D. (1974). The Design and Analysis
of Computer Algorithms. Addison-Wesley, Reading.
Cormen, T. H., Leiserson, C. E., and Rivest, R. L. (2007). Introduction to Algorithms. Druga izdaja, MIT Press, Cambridge.
Horowitz, E., Sahni, S., and Rajasekaran, S. (1998). Computer Algorithms. Computer Science Press, New York.
Kleinberg, J. and Tardos, E. (2006). Algorithm Design. Parson Education, Inc.,
New York.
Kononenko, I. (1996). Načrtovanje podatkovnih struktur in algoritmov. Fakulteta
za računalništvo in informatiko, Ljubljana.
Kozak, J. (1997). Podatkovne strukture in algoritmi. Društvo matematikov, fizikov
in astronomov Slovenije, Ljubljana.
Levitin, A. (2007). The Design and Analysis of Algorithms. Druga izdaja, Pearson
Education, Inc., Boston.
Manber, U. (1989). Introduction to Algorithms. A Creative Approach. AddisonWesley, Reading.
Nilsson, N. J. (1980). Principles of Artificial Intelligence. Tioga.
Sedgewick, R. (2003).
Boston.
Algorithms in Java.
Third Edition. Addison-Wesley,
Vilfan, B. (1998). Osnovni algoritmi. Fakulteta za računalništvo in informatiko,
Ljubljana.