Požrešna metoda

Transcription

Požrešna metoda
RAČUNALNIŠTVO IN INFORMACIJSKE TEHNOLOGIJE
OSNOVE ALGORITMOV
NIKOLA GUID
Fakulteta za elektrotehniko,
računalništvo in informatiko
Maribor, 2011
Kazalo
3 Požrešna metoda
3.1 Splošna metoda . . . . . . . .
3.2 Preprosti problem nahrbtnika
3.3 Primov algoritem . . . . . . .
3.4 Dijkstrin algoritem . . . . . .
3.5 Bellman-Fordov algoritem . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3-1
3-1
3-2
3-4
3-9
3-18
Poglavje
3
Požrešna metoda
Požrešna metoda (greedy method ) je gotovo najbolj neposredna metoda načrtovanja, ki jo obravnavamo. Večina problemov, ki jih rešujemo s to metodo, ima
n vhodov in zahteva od nas, da določimo podmnožico, ki izpolnjuje določene
omejitve.
Kakršnikoli podmnožici, ki izpolnjuje določene omejitve, pravimo dopustna
rešitev (feasible solution). Zahtevamo, da poiščemo tako dopustno rešitev, ki
bodisi minimizira bodisi maksimizira dano kriterijsko funkcijo (objective function). Dopustna rešitev, ki optimizira kriterijsko funkcijo, je optimalna rešitev
(optimal solution).
3.1
Splošna metoda
Pri strategiji požrešna metoda rešitev gradimo postopoma. Na tekočem koraku
poiščemo element, ki prinese največ h kriterijski funkciji. Sprejmemo ga samo, če
s tem elementom razširjena množica ostane dopustna.
Požrešno metodo lahko opišemo z naslednjim psevdokodom:
POZRESNA(n, a, resitev)
1 resitev ← 0
2 for i ← 1 to n
3
do x ← izberi(n, a, resitev) % Izberi naslednji element, ki še ni
% v rešitvi in pride na vrsto
% po kriteriju optimalnosti.
4
if dopustna(x, resitev)
5
then resitev ← resitev ∪ x
% Če je tekoči element dopusten x, ga vključimo
% v celotno rešitev.
Oblika procedur izberi in dopustna je odvisna od zgleda.
3-2
3.2 Preprosti problem nahrbtnika
3.2
Preprosti problem nahrbtnika
Na razpolago imamo n predmetov. Za vsak predmet i (i = 1, 2, . . . , n) poznamo njegovo prostornino v[i] (0 < v[i] ≤ V ). Poznamo tudi vrednost predmeta
c[i] (c[i] > 0). Predpostavimo, da predmete lahko poljubno režemo. x[i] naj predstavlja del predmeta i, ki ga odrežemo in damo v nahrbtnik (0 ≤ x[i] ≤ 1). V
nahrbtnik s prostornino V želimo vstaviti deleže predmetov, tako da je izraz
n
X
c[i]x[i]
(3.1)
v[i]x[i] ≤ V.
(3.2)
i=1
maksimalen pri pogojih
n
X
i=1
Dopustna rešitev je kakršnakoli množica (x[1], . . . , x[n]), ki izpolnjuje pogoj
3.2. Optimalna rešitev je dopustna rešitev, pri kateri ima izraz 3.1 maksimalno
vrednost.
Nahrbtnik bo dosegel maksimalno vrednost, če bomo v nahrbtnik vlagali najprej predmete, ki imajo največjo vrednost glede na prostornino. To pomeni, da
moramo urediti predmete po relativni vrednosti c[i]/v[i], tako da velja:
c[i + 1]
c[i]
≥
,
v[i]
v[i + 1]
i = 1, 2, . . . , n − 1
(3.3)
V začetku v nahrbtnik vlagamo cele predmete. Praviloma moramo odrezati samo
zadnji predmet, ki ga še damo v nahrbtnik. Delovanje procedure kaže naslednji
psevdokod:
PREPROSTI-NAHRBTNIK(V, n,v,c,x)
1 for i ← 1 to n
2
do x[i] ← 0
% inicializiraj vrednosti x[i]
3 y←V
% y je prostor, ki je še na voljo
4 for i ← 1 to n
5
do if v[i] > y
% ali je rešitev dopustna?
6
then exit(for)
7
else x[i] ← 1
8
y ← y − v[i]
9 if i ≤ n
10
then x[i] ← y/v[i]
% delež predmeta, ki napolni nahrbtnik
Iz procedure vidimo, da rešitev x gradimo postopoma. Najprej določimo
vrednost x[1], nato x[2], itd.
3.2 Preprosti problem nahrbtnika
3-3
Zgled 3.1. Imejmo tri predmete z naslednjimi vrednostmi: c = [10 14 20] in v =
[4 7 5]. Prostornina nahrbtnika naj bo V = 8. Procedura PREPROSTI-NAHRBTNIK
zahteva ureditev predmetov glede na vrednost na prostorsko enoto. Izračunajmo
c/v = [2.5 2 4]. To pomeni, da tretji predmet postane prvi, prvi drugi in drugi
tretji. Preuredimo vektor c in v: c = [20 10 14] in v = [5 4 7].
Opišimo delovanje procedure PREPROSTI-NAHRBTNIK:
1. Vrstici 1–2: x[1] = 0, x[2] = 0, x[3] = 0.
2. Vrstica 3: y = 8.
3. Vrstice 4–8: 1. iteracija zanke for: Ker je v[1] < y (5<8), dobimo x[1] = 1
in y = 8 − 5 = 3.
4. Vrstice 4–8: 2. iteracija zanke for: Ker je v[2] > y (4<3), izstopimo iz
zanke for.
5. Vrstica 9: Ker je i ≤ n (2 ≤ 3), izračunamo delež drugega predmeta, ki
ga postavimo v nahrbtnik: x[2] = 3/4 = 0.75. To pomeni, da je rešitev
problema vektor x = [1 0.75 0]. ♦
Časovna zahtevnost procedure PREPROSTI-NAHRBTNIK
Procedura PREPROSTI-NAHRBTNIK največ dela opravi z drugo zanko for, ki se
izvede v najslabšem primeru n-krat. Zato je časovna zahtevnost v najslabšem
primeru:
T (n) = O(n).
3-4
3.3 Primov algoritem
3.3
Primov algoritem
Imejmo n kontaktov, ki jih moramo povezati med sabo, tako da uporabimo n − 1
žičk. Vsaka žička povezuje dva kontakta. Naš cilj je, da porabimo čim manj žice.
Vemo, da električna napetost pride do kontakta, če je le-ta povezan z eno žičko.
V problemu ožičenja kontaktov lahko uporabimo model neusmerjenega grafa G = (V, E), kjer je V množica vozlišč (vertices) in E množica povezav
(edges). Vozlišča ustrezajo kontaktom, žičke pa povezavam.
0
0
0
V danem grafu želimo najti aciklični podgraf G = (V, E ), tako da je E ⊆ E,
0
ki povezuje vsa vozlišča grafa G. Graf G imenujemo vpeto drevo (spanning
tree).
V danem grafu (slika 3.1a) je možno določiti veliko vpetih dreves. Slike 3.1b,
3.1c in 3.1d kažejo samo tri primere vpetega drevesa, teh pa je še več.
Slika 3.1: a) Neusmerjeni graf, b) Vpeto drevo 1, c) Vpeto drevo 2, d) Vpeto
drevo 3
Uvedimo tak graf G, v katerem je za vsako povezavo (u, v) ∈ E, pri čemer sta
u in v vozlišči iz grafa G (u, v ∈ V ), dana vrednost c(u, v), ki predstavlja strošek
(cost) povezave. Sedaj lahko določimo strošek vseh povezav v vpetem drevesu T :
X
c(u, v).
(3.4)
c(T ) =
(u,v)∈T
Nekateri avtorji [Cormen et al., 2007] uporabljajo izraz utež (weight) in oznako w(u, v). Drevo z minimalnim stroškom povezav c(T ) imenujemo minimalno
vpeto drevo (minimum-spanning-tree), ki ga je mogoče določiti z različnimi algoritmi. Najprej bomo spoznali Primov algoritem.
Minimalno vpeto drevo bomo gradili postopoma. Najprej bomo vključili v
rešitev eno vejo drevesa, zatem drugo, itd. Tekoča podatkovna struktura je
venomer drevo. Na vsakem koraku bomo našli eno vejo končnega minimalnega
vpetega drevesa.
Najprej izberemo povezavo z minimalno vrednostjo v grafu G. Označimo jo s
(k, l), pri čemer sta k in l vozlišči grafa G.
3.3 Primov algoritem
3-5
K vsakemu vozlišču j, ki še ni vključeno v minimalno vpeto drevo, priredimo
vrednost r[j], ki predstavlja indeks najbližjega vozlišča, ki je že v minimalnem
vpetem drevesu. V drevo vključimo takšno vozlišče j, katerega povezava s poljubnim že vključenim vozliščem je minimalna (torej c(j, r[j])=min). Brž ko je novo
vozlišče j vključeno v rešitev (oz. v drevo), postane vrednost indeksa r[j] = 0. To
pomeni, da moramo pri vključitvi novega vozlišča j izpolniti dva pogoja:
1. r[j] 6= 0, kar pomeni, da vozlišče j še ni vključeno v drevo, in
2. c(j, r[j])=min, izbrati tako nevključeno vozlišče j, ki minimizira ta izraz.
Zapišimo proceduro Primovega algoritma:
PRIM(G, C, vr, T )
1 izberi (k, l) kot povezavo, ki ima najmanjšo ceno c(u, v) (u, v ∈ V )
2 (k, l) ∈ T
% vključi povezavo (k, l) v rešitev
3 vr ← c(k, l)
% izračunaj vrednost vpetega drevesa
4 for i ← 1 to n
% zanka za določitev indeksov r[i]
5
do if c(i, l) < c(i, k)
6
then r[i] ← l
7
else r[i] ← k
8 r[k] = r[l] ← 0
% vozlišči k in l sta že vključeni v drevo T
9 for i ← 2 to n − 1
10
do poišči j, tak da je r[j] 6= 0 in c(j, r[j])=min
11
(j, r[j]) ∈ T
% vključi povezavo (j, r[j]) v drevo T
12
vr ← vr + c(j, r[j])
% osveži vrednost vr vpetega drevesa
13
r[j] = 0
% spremeni indeks vključenosti za vozlišče j
14
for h ← 1 to n
% zanka za osvežitev indeksov r[j]
15
do if r[h] 6= 0 and c(h, r[h]) > c(h, j)
% stara povezava ima strošek večji od nove
16
then r[h] ← j
Procedura PRIM(G, C, vr, T ) najde vse povezave minimalnega vpetega drevesa
in vrednost drevesa.
3-6
3.3 Primov algoritem
Zgled 3.2. Poglejmo kako deluje procedura PRIM na primeru grafa na sliki 3.2.
1
30
15
25
2
4
3
6
14
4
20
12
5
Slika 3.2: Primer neusmerjenega grafa
Iz grafa na sliki 3.2 lahko zapišemo naslednjo

0 30 15 6
 30 0 25 ∞

C=
 15 25 0 14
 6 ∞ 14 0
∞ 4 20 12
matriko povezav:

∞
4 

20 

12 
0
1. Vrstica 1: Izberi povezavo (2, 5) kot povezavo, ki ima najmanjšo ceno c(i, j).
2. Vrstica 2: Vključi povezavo (2, 5) v drevo T (slika 3.3a).
3. Vrstica 3: Izračunaj vrednost vpetega drevesa (vr = 4).
4. Vrstice 4–7: Vsem vozliščem izračunaj indeks najbližjega vozlišča, ki je že
v minimalnem vpetem drevesu. Ti so:
r[1] = 2, r[2] = 2, r[3] = 5, r[4] = 5, r[5] = 5.
5. Vrstica 8: Vozliščema 2 in 5 postavimo indeks r na nič, saj sta obe vozlišči
že vključeni v drevo T (r[2] = 0, r[5] = 0).
6. Vrstica 9: Vstop v prvo iteracijo druge zanke for.
7. Vrstica 10: Določi vozlišče j, ki ga vključimo v drevo T . Vključili bomo
vozlišče 4, saj ima le-to c(j, r[j])=min.
8. Vrstica 11: Vključi povezavo (4, 5) v drevo T (slika 3.3b).
9. Vrstica 12: Osveži vrednost vr vpetega drevesa. Nova vrednost je:
vr = 4 + 12 = 16.
3-7
3.3 Primov algoritem
2
2
4
4
4
12
5
5
a)
b)
1
1
6
2
6
4
4
12
3
2
4
14
4
12
5
5
c)
d)
Slika 3.3: Gradnja minimalnega vpetega drevesa T : a) po izvršeni prvi zanki
for, b) po izvršeni prvi iteraciji druge zanke for, c) po izvršeni drugi
iteraciji druge zanke for, d) po izvršeni tretji iteraciji druge zanke for
10. Vrstica 13: Spremeni indeks vključenosti za vozlišče 4 (r[4] = 0).
11. Vrstice 14–16: Vstop v tretjo zanko for, ki osveži vrednosti nevključenim
vozliščem. V našem primeru se spremeni r[1] na 4 (saj je c[1, 2] > c[1, 4]
oz. 30 > 6) in r[3] na 4 (saj je c[3, 5] > c[3, 4] oz. 20 > 14).
12. Vrstica 9: Vstop v drugo iteracijo druge zanke for.
13. Vrstica 10: Določi vozlišče j, ki ga vključimo v rešitev. Od še nevključenih
vozlišč ima vozlišče 1 minimalno vrednost izraza c(j, r[j]).
14. Vrstica 11: Vključi povezavo (1, 4) v drevo T (slika 3.3c).
15. Vrstica 12: Osveži vrednost vpetega drevesa. Nova vrednost je:
vr = 16 + 6 = 22.
16. Vrstica 13: Spremeni indeks vključenosti za vozlišče 1 (r[1] = 0).
3-8
3.3 Primov algoritem
17. Vrstice 14–16: Vstop v tretjo zanko for, ki osveži vrednosti nevključenim
vozliščem. V našem primeru se r[3] ne spremeni (saj je c[3, 4] < c[3, 1]
oz. 14 < 15).
18. Vrstica 9: Vstop v tretjo (zadnjo) iteracijo druge zanke for.
19. Vrstica 10: Določi vozlišče j, ki ga vključimo v rešitev. Vozlišče 3 ima
c(j, r[j])=min. Sicer pa je to vozlišče zadnjo nevključeno vozlišče.
20. Vrstica 11: Vključi povezavo (3, 4) v drevo T (slika 3.3d). To je že končno
minimalno vpeto drevo.
21. Vrstica 12: Osveži vrednost vpetega drevesa. Nova vrednost je:
vr = 22 + 14 = 36.
22. Vrstica 13: Spremeni indeks vključenosti za vozlišče 3 (r[3] = 0).
23. Vrstice 14–16: Vstop v tretjo zanko for, ki osveži vrednosti nevključenim
vozliščem. V našem primeru zanka ne osvežuje več, saj ni več izpolnjen
pogoj r[j] 6= 0.
24. Preglednica 3.1 povzema delovanje algoritma PRIM. ♦
Preglednica 3.1: Rezultati delovanja procedure PRIM
zap. št.
vključene
povezave
1
2
3
4
j
vključena
povezava
4
1
3
(2,5)
(4,5)
(1,4)
(3,4)
vrednost
drevesa
vr
4
16
22
36
r[h]
12345
20550
4 40
0
0
Časovna zahtevnost procedure PRIM
Procedura PRIM ima tri zanke for. Prva zanka for se izvede n-krat, druga (n − 2)krat in tretja n-krat. Tretja zanka for je vgnezdena v drugo zanko. Vse zanke se
brezpogojno izvedejo do konca, zato je skupna časovna zahtevnost:
T (n) = Θ(n) + Θ(n2 ) = Θ(n2 ),
kar pomeni, da so zgornja, spodnja in poprečna časovna zahtevnost enake.
(3.5)
3-9
3.4 Dijkstrin algoritem
3.4
Dijkstrin algoritem
V tem razdelku bomo spoznali problem najkrajše poti iz enega vozlišča do vseh
preostalih vozlišč v grafu. Preden se lotimo obravnave algoritma razložimo nekaj
novih pojmov.
V problemih najkrajše poti je dan utežen usmerjen graf G = (V, E) z utežno
funkcijo w : E → R, ki preslika povezave v uteži (realna števila).
Definicija 3.1. Utež poti p = hv0 , v1 , . . . , vk i je vsota uteži njenih povezav:
w(p) =
k
X
w(vi−1 , vi ).
(3.6)
i=1
Definicija 3.2. Utež najkrajše poti ( shortest-path weight) je definirana kot:
½
min{w(p) : u → v}, ˇce je pot od u do v,
δ(u, v) =
(3.7)
∞,
drugaˇce.
Najkrajša pot iz vozlišča u do vozlišča v je definirana kot katerakoli pot p z utežjo
w(p) = δ(u, v).
Uteži predstavljajo razdalje (problem zemljevida), čas, stroške, kazni ipd. Algoritem iskanja z razvijanjem v širino je algoritem najkrajših poti na neuteženih
grafih, kjer ima vsaka povezava enotsko utež. Precej konceptov iz tega algoritma
se uporablja tudi v uteženih grafih.
V tem razdelku se bomo osredotočili na problem najkrajših poti iz enega
vozlišča: za dani graf G = (V, E) želimo določiti najkrajše poti iz izhodišča s ∈ V
k vsakemu vozlišču v ∈ V .
V nekaterih problemih najkrajših poti iz enega vozlišča lahko imamo povezave
z negativnimi utežmi. Če graf G = (V, E) ne vsebuje ciklov z negativno utežjo,
dosegljivih iz izhodišča s, potem so najkrajše poti δ(s, v) do vozlišč v ∈ V dobro definirane, čeprav imajo celo negativno utež. Če obstaja cikel z negativnimi
utežmi, dosegljiv iz s, potem dobi najkrajša pot vrednost −∞ (δ(s, v) = −∞).
Slika 3.4 kaže učinek negativnih uteži na uteži najkrajših poti. Iz s do a vodi
samo ena pot (pot hs, ai), zato je δ(s, a) = w(s, a) = 6. Podobno velja za pot
iz s do b (pot hs, a, bi): δ(s, b) = w(s, a) + w(a, b) = 6 + (−2) = 4. Na drugi
strani pa imamo iz s do c neskončno mnogo poti: hs, ci, hs, c, d, ci, hs, c, d, c, d, ci
itd. Ker ima cikel hc, d, ci utež 5 + (−4) = 1 > 0, je najkrajša pot iz s v c
hs, ci z utežjo δ(s, c) = 3. Podobno je najkrajša pot iz s v d hs, c, di z utežjo
δ(s, d) = w(s, c) + w(c, d) = 3 + 5 = 8.
Analogno imamo neskončno poti iz s v e: hs, ei, hs, e, f, ei. hs, e, f, e, f, ei itd.
Ker ima cikel he, f, ei utež 2 + (−5) = −3 < 0, ne obstaja najkrajša pot iz s
v e. Če se sprehodimo po ciklu z negativno utežjo he, f, ei poljubno krat, lahko
3-10
3.4 Dijkstrin algoritem
najdemo poti iz s v e s poljubno velikimi negativnimi utežmi, zato je δ(s, e) = −∞.
Podobno velja za δ(s, f ) = −∞. Ker je g dosegljivo iz f , lahko najdemo poti s
poljubni velikimi negativnimi utežmi iz s do g in δ(s, g) = −∞.
6
a
-2
4
b
10
6
3
0 s
3
c
5
8
d
11
g -¥
-4
7
6
2
e
-¥
-5
f
-¥
Slika 3.4: Negativne uteži v usmerjenem grafu. Ob vsakem vozlišču je prikazana
njegova najkrajša pot iz izhodišča.
Da bi razumeli algoritme najkrajše poti iz enega vozlišča, je koristno poznati
tehnike, ki jih ti algoritmi uporabljajo, in lastnosti najkrajših poti, ki jih le-ti
izkoriščajo. Glavna tehnika, ki jo uporabljajo algoritmi, je relaksacija (relaxation), tj. metoda, ki ponavljajoče zmanjšuje zgornjo mejo uteži najkrajše poti,
dokler ne postane enaka uteži najkrajše poti.
Algoritmi najkrajših poti izkoriščajo lastnost, da najkrajša pot med dvema
vozliščema vsebuje druge najkrajše poti znotraj te poti. To je princip optimalnosti, ki je značilen tako za požrešno metodo kot dinamično programiranje.
Principu optimalnosti pravijo nekateri tudi lastnost optimalne podstrukture
(optimal-substructure property).
Izrek 3.1. Dan je utežni usmerjen graf G = (V, E) z utežno funkcijo w : E → R.
Bodi p = hv1 , v2 , . . . , vk i najkrajša pot iz vozlišča v1 do vozlišča vk in za poljubni
i in j (1 ≤ i ≤ j ≤ k) bodi pij = hvi , vi+1 , . . . , vj i delna pot ( subpath) iz vozlišča
vi do vozlišča vj . Potem je pij najkrajša pot iz vi do vj .
Izrek 3.2. Dan je utežni usmerjen graf G = (V, E) z utežno funkcijo w : E → R.
Predpostavljamo, da lahko najkrajšo pot p iz izhodišča s do vozlišča v razcepimo
0
v pot p od s do u in pot od u direktno v vozlišče v (torej med vozliščema u in v
obstaja neposredna povezava). Potem je utež najkrajše poti iz s do u enaka
δ(s, v) = δ(s, u) + w(u, v).
3-11
3.4 Dijkstrin algoritem
Izrek 3.3. Dan je utežni usmerjen graf G = (V, E) z utežno funkcijo w : E → R.
Za vse povezave (u, v) ∈ E velja:
δ(s, v) ≤ δ(s, u) + w(u, v).
V izreku 3.2 je vozlišče u vključeno v najkrajšo pot med s in v, v izreku 3.3
pa vozlišče u ni nujno vključeno v najkrajšo pot med s in v.
Za vsako vozlišče v ∈ V vzdržujemo atribut d[v], ki je zgornja meja uteži najkrajše poti od izhodišča s do v. d[v] imenujemo ocena najkrajše poti (shortestpath estimate). Ocene najkrajše poti in prednike inicializiramo z naslednjo proceduro:
INICIALIZACIJA(G, s)
1 for vsako vozlišče v ∈ V
2
do d[v] ← ∞
3
oce[v] ← NIL
4 d[s] ← 0
Proces relaksacije povezave (u, v) sestoji iz testiranja, če lahko izboljšamo
najkrajšo pot do v, tako da gremo skozi u in če nam to uspe, osvežimo d[v] in
oce[v]. Relaksacijo izvedemo z naslednjo kodo:
RELAKSACIJA(u, v, w)
1 if d[v] > d[u] + w(u, v)
2
then d[v] ← d[u] + w(u, v)
3
oce[v] ← u
Slika 3.5 prikazuje dva primera relaksacije povezave. V primeru na sliki 3.5a
se ocena najkrajše poti zmanjša, v primeru na sliki 3.5b pa se ocena ne spremeni.
3-12
3.4 Dijkstrin algoritem
10
u
15
v
3
10
u
RELAKSACIJA(u, v, w)
10
u
3
13
v
11
v
3
RELAKSACIJA(u, v, w)
10
u
a)
3
11
v
b)
Slika 3.5: Relaksacija povezave (u, v). Ocena najkrajše povezave d je označena
ob vozlišču. a) Ker je pred relaksacijo d[v] > d[u]+w(u, v), se vrednost
d[v] zmanjša. b) Ker je pred relaksacijo d[v] ≤ d[u] + w(u, v), se d[v]
ne spremeni.
Izrek 3.4. Dan je utežni usmerjen graf G = (V, E) z utežno funkcijo w : E → R
in bodi poljubna povezava (u, v) ∈ E. Po relaksaciji povezave (u, v) s proceduro
RELAKSACIJA(u, v, w) velja d[v] ≤ d[u] + w(u, v).
Dijkstrin algoritem rešuje problem najkrajše poti iz enega vozlišča na utežnem
usmerjenem grafu, ko so vse uteži nenegativne. Algoritem vzdržuje množico S
vozlišč, ki imajo že določeno najkrajšo pot iz izhodišča s. Za vsa vozlišča v ∈ S
velja d[v] = δ(s, v). Algoritem izbere tako vozlišče u (u ∈ V −S), ki ima minimalno
oceno najkrajše poti, vstavi u v S in relaksira vse povezave, ki izhajajo iz u. V
pričujoči aplikaciji vzdržujemo prednostno vrsto Q, ki vsebuje vsa vozlišča (iz
V − S), ki hranijo v ključih vrednost d. Implementacija predpostavlja, da je graf
podan s seznami sosedov.
DIJKSTRA(G, w, s)
1 INICIALIZACIJA(G, s)
2 S←∅
3 Q←V
4 while Q 6= 0
5
do u ← IZLOCI-MINIMUM(Q)
6
S ← S ∪ {u}
7
for za vsako vozlišče v ∈ Adj[u]
8
do RELAKSACIJA(u, v, w)
3.4 Dijkstrin algoritem
3-13
V vrstici 1 postavimo začetne vrednosti za d[v] in oce[v] za vsako vozlišče v
iz G. Vrstica 2 napravi množico S prazno. V vrstici 3 postavimo v prednostno
vrsto Q vsa vozlišča V . V iteraciji zanke while v vrsticah od 4–8 se izloči vozlišče
u, ki ima najmanjšo oceno najkrajše poti v V − S, in ga vloži v množico S ( V
prvi iteraciji se izloči izhodišče s.). V vrsticah od 7–8 se relaksira vsaka povezava
(u, v), ki zapušča u, in se osvežijo ocene d[v] in oce[v].
Zgled 3.3. Delovanje Dijkstrinega algoritma na usmerjenem grafu kaže slika 3.6.
Ocene najkrajših poti so zapisane ob krogcih, ki predstavljajo vozlišča. S pomočjo
poudarjene povezave lahko določimo očeta ustreznega vozlišča (oče je vozlišče
pri repu puščice). Črna vozlišča so v množici S, bela pa v prednostni vrsti
Q = V − S. Vozlišče, ki je pobarvano sivo, bo izbrano v naslednji iteraciji zanke
while. Bodi izhodišče vozlišče 1. Podrobno ponazorimo delovanje algoritma:
1. Vrstica 1: Po izvršitvi procedure INICIALIZACIJA(G, 1) imamo d[2] =
d[3] = d[4] = d[5] = ∞, d[1] = 0 in oce[1] = oce[2] = oce[3] = oce[4] =
oce[5] = NIL.
2. Vrstica 2: S = {}.
3. Vrstica 3: Q = {1, 2, 3, 4, 5}. V prvi iteraciji while bo izbrano vozlišče 1, ki
ima najmanjšo vrednost d izmed vseh elementov v vrsti Q. Zato vozlišče 1
pobarvamo sivo. Stanje po tej izbiri kaže slika 3.6a.
4. Vrstice 4–8: 1. iteracija while: Prednostna vrsta Q ni prazna. Iz nje
izločimo vozlišče 1, ki ima najmanjšo vrednost d (novo stanje je Q =
{2, 3, 4, 5}). V množico S vključimo vozlišče 1 (S = {1}) in ga označimo s
črno barvo.
1. iteracija for: 1. sosed od 1 je v = 2 in izvedemo RELAKSACIJA(1, 2, w).
Ker je d[2] > d[1] + w(1, 2) (∞ > 0 + 9), postavimo d[2] = d[1] + w(1, 2) =
0 + 9 = 9 in oce[2] = 1.
2. iteracija for: 2. sosed od 1 je v = 5, izvedemo RELAKSACIJA(1, 5, w). Ker
je d[5] > d[1]+w(1, 5) (∞ > 0+4), postavimo d[5] = d[1]+w(1, 5) = 0+4 =
4 in oce[5] = 1. Stanje po zaključku 1. iteracije while kaže slika 3.6b, kjer
je označeno vozlišče 1 kot črno (je tudi že razvito v celoti). Vozlišče 5 je
pobarvano sivo, saj ima trenutno najmanjšo vrednost d v vrsti Q.
5. Vrstice 4–8: 2. iteracija while: Prednostna vrsta Q ni prazna. Iz nje
izločimo vozlišče 5, ki ima najmanjšo vrednost d (novo stanje je Q =
{2, 3, 4}). V množico S vključimo vozlišče 5 (S = {1, 5}) in ga pobarvamo
črno.
1. iteracija for: Sosed od 5 je v = 2 in izvedemo RELAKSACIJA(5, 2, w). Ker
je d[2] > d[5]+w(5, 2) (9 > 4+1), postavimo d[2] = d[5]+w(5, 2) = 4+1 = 5
3.4 Dijkstrin algoritem
3-14
in oce[2] = 5. Vozlišče 2 dobi novega očeta, tj. 5. Stanje po zaključku
2. iteracije while kaže slika 3.6c. Na sliki je vozlišče 5 pobarvano črno (je
tudi že razvito) in vozlišče 2 kot sivo, saj ima trenutno najmanjšo vrednost
d v vrsti Q.
6. Vrstice 4–8: 3. iteracija while: Prednostna vrsta Q ni prazna. Iz nje
izločimo vozlišče 2, ki ima najmanjšo vrednost d (novo stanje je Q = {3, 4}).
V množico S vključimo vozlišče 2 (S = {1, 5, 2}) in ga pobarvamo črno.
1. iteracija for: 1. sosed od 2 je v = 3 in izvedemo RELAKSACIJA(2, 3, w).
Ker je d[3] > d[2] + w(2, 3) ( ∞ > 5 + 5), postavimo d[3] = d[2] + w(2, 3) =
5 + 5 = 10 in oce[3] = 2.
2. iteracija for: 2. sosed od 2 je v = 4 in izvedemo RELAKSACIJA(2, 4, w).
Ker je d[4] > d[2] + w(2, 4) (∞ > 5 + 4), postavimo d[4] = d[2] + w(2, 4) =
5 + 4 = 9 in oce[4] = 2. Stanje po zaključku 3. iteracije while kaže slika
3.6d. Na sliki je vozlišče 2 kot črno (je že razvito) in vozlišče 4 kot sivo, saj
ima trenutno najmanjšo vrednost d v vrsti Q.
7. Vrstice 4–8: 4. iteracija while: Prednostna vrsta Q ni prazna. Iz nje
izločimo vozlišče 4, ki ima najmanjšo vrednost d (novo stanje je Q = {3}).
V množico S vključimo vozlišče 4 (S = {1, 5, 2, 4}) in ga pobarvamo črno.
1. iteracija for: 1. sosed od 4 je v = 1 in izvedemo RELAKSACIJA(4, 1, w).
Ker ni d[1] > d[4] + w(4, 1) (ne velja 0 > 9 + 6), zaključimo obravnavo tega
soseda.
2. iteracija for: 2. sosed od 4 je v = 5, izvedemo RELAKSACIJA(4, 5, w).
Ker ni d[5] > d[4] + w(4, 5) (ne velja 4 > 9 + 7), zaključimo obravnavo
tega soseda. Stanje po zaključku 4. iteracije while kaže slika 3.6e. Na
sliki je označeno kot črno vozlišče 4 in kot sivo vozlišče 3, saj ima trenutno
najmanjšo vrednost d v vrsti Q.
8. Vrstice 4–8: 5. iteracija while: Prednostna vrsta Q ni prazna. Iz nje
izločimo vozlišče 3, ki ima najmanjšo vrednost d (novo stanje je Q = {}). V
množico S vključimo vozlišče 3 (S = {1, 5, 2, 4, 3}) in ga pobarvamo črno.
1. iteracija for: 1. sosed od 3 je v = 2 in izvedemo RELAKSACIJA(3, 2, w).
Ker ni d[2] > d[3] + w(3, 2) (ne velja 5 > 10 + 3), zaključimo obravnavo tega
soseda.
2. iteracija for: 2. sosed od 3 je v = 4 in izvedemo RELAKSACIJA(3, 4, w).
Ker ni d[4] > d[3] + w(3, 4) (ne velja 9 > 10 + 2), zaključimo obravnavo tega
soseda. Stanje po zaključku 5. iteracije while kaže slika 3.6f. Na sliki so
vsa vozlišča označena črno. Ker je množica Q izčrpana (prazna), zaključimo
proceduro.
3-15
3.4 Dijkstrin algoritem
Preglednica 3.2: Rezultati delovanja procedure DIJKSTRA
zap. št.
iteracije
vrstica 3
1
2
3
4
5
izbrano
vozlišče
1
5
2
4
3
oče v
drevesu
NIL
1
5
2
2
najkrajša
pot
1
1, 5
1, 5, 2
1, 5, 2, 4
1, 5, 2, 3
d[1]
0!
-
d[2]
∞
9
5!
-
d[3]
∞
∞
∞
10
10!
-
d[4]
∞
∞
∞
9!
-
d[5]
∞
4!
-
Če narišemo samo poudarjene povezave v grafu na sliki 3.6f, dobimo drevo
najkrajših poti iz vozlišča 1 do vseh ostalih vozlišč v grafu (slika 3.7). Zunaj ob
vozlišču je označena dolžina najkrajše poti iz vozlišča 1. ♦
Časovna zahtevnost
Procedura IZLOCI-MINIMUM zahteva O(|V |) časa, ker pa jo izvedemo |V |-krat, je
celoten čas za IZLOCI-MINIMUM velikosti O(|V |2 ). Zanka for v vrsticah 7–8 se
izvede tolikokrat, kolikor imamo povezav, tj. |E|-krat, medtem ko vsaka iteracija
zahteva O(1) časa. Skupni čas algoritma je torej
T (n) = O(|V |2 + |E|) = O(|V |2 ),
saj velja, da je v polnem usmerjenem grafu |E| = |V |(|V | − 1) povezav in je
|V |2 > |E|.
3-16
3.4 Dijkstrin algoritem
0
1
0
1
4
4
9
1
¥ 5
6
4
7
4
¥
2 ¥
5
0
1
4
4
2 5
5
1
4 5
4
9
d)
0
1
0
1
4
9
1
6
4
2
e)
2 5
5
3
10
3
2 5
5
1
6
4
9
3
9
4 5
7
3
3
10
2
c)
4
4 5
4
7
3
¥
2
5
9
6
3
2 9
3
¥
2
0
1
6
4
9
4
¥
b)
1
4
¥
4
7
9
4 5
7
6
3
a)
4
7
1
4 5
3
¥
2
9
4
2
2 5
5
3
3
10
f)
Slika 3.6: Delovanje Dijkstrinega algoritma na usmerjenem grafu: a) Stanje po
izvršitvi vrstice 3, b) Stanje po izvršitvi 1. iteracije while, c) Stanje po
izvršitvi 2. iteracije while, d) Stanje po izvršitvi 3. iteracije while, e)
Stanje po izvršitvi 4. iteracije while, f) Stanje po izvršitvi 5. (zadnje)
iteracije while.
3.4 Dijkstrin algoritem
3-17
Slika 3.7: Drevo najkrajših poti iz vozlišča 1 do vseh ostalih vozlišč v grafu na
sliki 3.6a
3.5 Bellman-Fordov algoritem
3.5
3-18
Bellman-Fordov algoritem
Bellman-Fordov algoritem rešuje problem najkrajših poti iz enega vozlišča bolj
splošno, torej tudi v primeru negativnih uteži za povezave. Za dani uteženi usmerjeni graf G = (V, E) z izhodiščem s in utežno funkcijo w : E → R vrne
Bellman-Fordov algoritem Boolovo vrednost, ki označuje, ali je ali ni cikla z negativno utežjo, dosegljivega iz izhodišča. Če obstaja tak cikel, algoritem naznani, da
ni rešitve. Če tak cikel ne obstaja, algoritem proizvede najkrajše poti in njihove
uteži.
Podobno kot Dijkstrin algoritem tudi Bellman-Fordov algoritem uporablja
metodo relaksacije. Bellman-Fordov algoritem vrne TRUE, če in samo če graf ne
vsebuje ciklov z negativnimi utežmi, dosegljivih iz izhodišča.
BELLMAN-FORD(G, w, s)
1 INICIALIZACIJA(G, s)
2 for i ← 1 to |V | − 1
3
do for za vsako povezavo (u, v) ∈ E
4
do RELAKSACIJA(u, v, w)
5 for za vsako povezavo (u, v) ∈ E
6
do if d[v] > d[u] + w(u, v)
7
then return FALSE % eden od ciklov je negativen
8 return TRUE
% vsi cikli so pozitivni
V vrstici 1 postavimo začetne vrednosti za d[v] in oce[v] za vsako vozlišče
v iz G. V vrsticah 2–4 se izvede |V | − 1 iteracij preko povezav grafa. Vsaka
iteracija relaksira vsako povezavo grafa samo enkrat. Vrstice 5–8 preverjajo cikel
z negativno utežjo in vrnejo ustrezno Boolovo vrednost.
Zgled 3.4. Delovanje Bellman-Fordovega algoritma na usmerjenem grafu kaže
slika 3.8. Izhodišče s naj bo vozlišče 5. Ocene najkrajših poti so zapisane zunaj ob krogcih, ki predstavljajo vozlišča. S pomočjo poudarjene povezave lahko
določimo očeta ustreznega vozlišča (oče je vozlišče pri repu puščice). V tem zgledu
vsaka iteracija prve zanke for relaksira povezave po naslednjem vrstnem redu:
(1, 2), (1, 3), (1, 4), (2, 1), (3, 2), (3, 4), (4, 2), (4, 5), (5, 1), (5, 3). Vsi cikli imajo pozitivno vrednost, zato nam algoritem vrne vrednost TRUE. Podrobno ponazorimo
delovanje algoritma:
1. Vrstica 1: Po izvršitvi procedure INICIALIZACIJA(G, 5) imamo d[1] =
d[2] = d[3] = d[4] = ∞, d[5] = 0 in oce[1] = oce[2] = oce[3] = oce[4] =
oce[5] = NIL. Stanje po tej izvršitvi kaže slika 3.8a.
2. Vrstice 2–4: 1. iteracija 1. zanke for: Prvo relaksacijo dobimo šele pri
povezavi (5, 1) (0 + 6 < ∞). Tedaj se določita nova razdalja in novi oče pri
3.5 Bellman-Fordov algoritem
3-19
vozlišču 1, tj. d[1] = 6 in oce[1] = 5. Druga relaksacija se zgodi pri povezavi
(5, 3) (0 + 7 < ∞), ko se izračunata nova razdalja in novi oče pri vozlišču 3,
tj. d[3] = 7 in oce[3] = 5. Stanje po zaključku 1. iteracije 1. zanke for kaže
slika 3.8b.
3. Vrstice 2–4: 2. iteracija 1. zanke for: Prva relaksacija se izvede že pri
povezavi (1, 2) (6 + 5 < ∞). Tedaj se določita nova razdalja in novi oče
pri vozlišču 2, tj. d[2] = 11 in oce[2] = 1. Druga relaksacija se zgodi pri pri
povezavi (1, 4) (6 − 4 < ∞), ko dobimo novo razdaljo in novega očeta pri
vozlišču 4, tj. d[4] = 2 in oce[4] = 1. Tretja relaksacija se izvede pri povezavi
(3, 2) (7 − 3 < 11), ko dobimo ponovno novo razdaljo in novega očeta pri
vozlišču 2, tj. d[2] = 4 in oce[2] = 3. Stanje po zaključku 2. iteracije 1. zanke
for kaže slika 3.8c.
4. Vrstice 2–4: 3. iteracija 1. zanke for: V tej iteraciji se izvede samo ena
relaksacija in to pri povezavi (2, 1) (4 − 2 < 6). Tedaj se določita nova
razdalja in novi oče pri vozlišču 1, tj. d[1] = 2 in oce[1] = 2. Stanje po
zaključku 3. iteracije 1. zanke for kaže slika 3.8d.
5. Vrstice 2–4: 4. iteracija 1. zanke for: Izvede se samo ena relaksacija in to pri
povezavi (1, 4) (2−4 < 2). Tedaj se določi nova razdalja d[4] = −2, medtem
ko oče od vozlišča 4 ostane nespremenjen. Stanje po zaključku 4. iteracije
1. zanke for kaže slika 3.8e.
6. Vrstice 5–7: V tretji zanki for ne velja pri nobeni povezavi (u, v) neenačba
d[v] > d[u] + w(u, v). V danem grafu imamo štiri cikle, kjer se pojavijo tudi
povezave z negativnimi utežmi. Cikel h1, 2, 1i ima utež 5 + (−2) = 3, cikel
h1, 4, 2, 1i utež (−4) + 7 + (−2) = 1, cikel h1, 4, 5, 1i utež (−4) + 2 + 6 = 4
in cikel h1, 3, 2, 1i ima utež 8 + (−3) + (−2) = 3.
7. Vrstica 8: Bellman-Fordov algoritem nam vrne TRUE, ker ni ciklov z negativnimi vrednostmi.
3.5 Bellman-Fordov algoritem
3-20
Slika 3.8: Delovanje Bellman-Fordovega algoritma na usmerjenem grafu: a)
Stanje po izvršitvi vrstice 1, b) Stanje po izvršitvi 1. iteracije 1. zanke
for, c) Stanje po izvršitvi 2. iteracije 1. zanke for, d) Stanje po izvršitvi
3. iteracije 1. zanke for, e) Stanje po izvršitvi 4. iteracije 1. zanke for
3-21
3.5 Bellman-Fordov algoritem
Preglednica 3.3: Rezultati delovanja procedure BELLMAN-FORD
zap. št.
iteracije
pred 1. it.
1
2
3
4
oce[1]
NIL
5
5
2
2
oce[2]
NIL
NIL
3
3
3
oce[3]
NIL
5
5
5
5
oce[4]
NIL
NIL
1
1
1
oce[5]
NIL
NIL
NIL
NIL
NIL
d[1]
∞
6
6
2
2
d[2]
∞
∞
4
4
4
d[3]
∞
7
7
7
7
d[4]
∞
∞
2
2
-2
d[5]
0
0
0
0
0
Če narišemo samo poudarjene povezave v grafu na sliki 3.8e, dobimo drevo
najkrajših poti iz vozlišča 5 do vseh ostalih vozlišč v grafu (slika 3.9). Zunaj ob
vozlišču je označena dolžina najkrajše poti iz vozlišča 5. ♦
Slika 3.9: Drevo najkrajših poti iz vozlišča 5 do vseh ostalih vozlišč v grafu na
sliki 3.8a
3.5 Bellman-Fordov algoritem
3-22
Časovna zahtevnost
Inicializacija v vrstici 1 zahteva O(|V |) časa. Prva zanka for se izvede (|V | − 1)krat. V vsaki od iteracij te zanke se pa izvede še druga zanka for |E|-krat.
Skupaj torej prva zanka for zahteva O(|V ||E|) časa. Tretja zanka for zahteva
O(|E|) časa.
Skupna časovna zahtevnost Bellman-Fordovega algoritma je:
T (n) = O(|V |) + O(|V ||E|) + O(|E|) = O(|V ||E|).
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.