Algoritmiek

advertisement
Doorzoeken van grafen
Algoritmiek
Vandaag
• Methoden om door grafen te wandelen
– Depth First Search
– Breadth First Search
– Gerichte Acyclische Grafen en topologische
sorteringen
2
Algoritmiek: Divide & Conquer
Doolhof
eind
start
3
Algoritmiek: Divide & Conquer
Depth First Search
• Methode om systematisch alle knopen (en
eventueel ook alle kanten) van een graaf te
bekijken
• Als in doolhof:
– Ga steeds verder waar je laatst was, tot je niet
meer verder kan, ga dan terug tot het laatste
punt waar je nog een andere kant op kan.
4
Algoritmiek: Divide & Conquer
DFS op ongerichte
samenhangende grafen
• Begin in een willekeurige knoop v
• Elke knoop heeft variable bezocht, die
aangeeft of we al eerder in die knoop
geweest zijn.
• Initieel zetten we voor elke v: bezocht(v) =
false.
• Wanneer we een knoop bezoeken wordt
bezocht op true gezet voor die knoop.
5
Algoritmiek: Divide & Conquer
DFS
• Procedure dfs(v)
– bezocht (v) = true
– for all w grenzend aan v
do if (bezocht(w) == false) then dfs(w)
recursie
6
Algoritmiek: Divide & Conquer
DFS zonder recursie
• Procedure dfs-nr(v)
–
–
–
–
Maak lege stack S;
Voor alle knopen v, zet bezocht(v) = false;
Push(S,v)
while (not(emptystack(S))) do
• x = pop(S);
• bezocht(v) = true;
• for all w grenzend aan v
do if (bezocht(w) == false) then push(S,w)
7
Algoritmiek: Divide & Conquer
g
e
a
d
f
h
b
i
c
8
Algoritmiek: Divide & Conquer
g
e
a
d
f
h
b
i
c
9
Algoritmiek: Divide & Conquer
Als G niet samenhangend is
• Hoofdloop, initialiseert en begint 1 keer per
samenhangende component
Initialisatie,
• Procedure dfSearch (graaf G)
net als bij
– for all v do bezocht(v) = false
– for all v
do if (bezocht(v) == false)
then dfs(v)
10
Algoritmiek: Divide & Conquer
samenhangende
grafen
Een keer per
samenhangende
component
Samenhangende componenten
• Een samenhangende
component van een
graaf G is een
maximale
samenhangende
deelgraaf van G.
G
11
Algoritmiek: Divide & Conquer
DFS en samenhangende
componenten
• DFS is eenvoudig te gebruiken om een
gegeven graaf te splitsen in samenhangende
componenten.
• Nuttig o.a., voor divide & conquer
preprocessing:
– Veel graafproblemen kunnen opgelost worden
door oplossingen voor samenhangende
componenten samen te voegen.
12
Algoritmiek: Divide & Conquer
Een eigenschap van DFS
• DFS bouwt een boom
op.
• Als G ongericht, dan
zijn er twee soorten
kanten in G:
– Kanten in de DFSboom: tree-edges
– Kanten tussen knoop
en voorouder in DFS
boom: backedges
13
1
2
5
3
4
Algoritmiek: Divide & Conquer
6
7
Articulatiepunten
• Een knoop v is
articulatiepunt, als de
samenhangende
component die v bevat
niet langer
samenhangend is als v
en zijn aangrenzende
kanten verwijderd
worden.
14
Algoritmiek: Divide & Conquer
Dubbelsamenhangende grafen
• Een graaf G is
dubbelsamenhangend
(biconnected) als
– G is samenhangend
– G heeft geen
articulatiepunten
15
Algoritmiek: Divide & Conquer
Toepassingen van
dubbelsamenhangendheid
• Betrouwbaarheid van netwerken
• Preprocessing (divide&conquer) door
splitsen in dubbelsamenhangende
componenten.
16
Algoritmiek: Divide & Conquer
Hoe vinden we de
articulatiepunten? (1)
• De wortel r van de
DFS-boom is een
articulatiepunt als r
minstens 2 kinderen in
de boom heeft.
1
4
2
3
5
Er zijn geen kanten
tussen de verschillende
deelbomen onder r
17
Algoritmiek: Divide & Conquer
6
Hoe vinden we de
articulatiepunten? (2)
• We nummeren de knopen in volgorde dat ze
bezocht worden.
• Initieel: pnum = 1
prenum geeft
preorder nummering
• Procedure dfs(v)
van de DFS-boom
– bezocht (v) = true;
– prenum(v) = pnum; pnum ++;
– for all w grenzend aan v
do if (bezocht(w) == false) then dfs(w)
18
Algoritmiek: Divide & Conquer
Vinden van articulatiepunten (3)
• Bepaal voor elke knoop v
een getal highest(v), wat
het nummer van de
hoogste voorouder van v
12
die bereikt kan worden
met een pad van eerst 0 of
3
1
meer tree-edges omlaag,
en dan 0 of 1 back-edge
omhoog.
4 1
highest is dus een zo
laag mogelijk nummer
van pnum
19
Algoritmiek: Divide & Conquer
1
2
1
Kan in
O(n+a)
tijd
5
6
6
7 2
Bepalen van highest
• Ga door T van beneden naar boven
(postorder).
• highest(v) is het minimum van
– prenum(v)
– prenum(w) over alle backedges (v,w)
– highest(x) over alle kinderen x van v in T
20
Algoritmiek: Divide & Conquer
Werkt in
O(n+a)
tijd
Vinden van articulatiepunten (4)
• Stelling.
Stel v is niet de wortel
van de DFSboom.
v is een articulatiepunt,
dan en slechts dan als v
een kind w in de
DFSboom heeft met
highest(w)  prenum(v).
21
1
1
12
3
4 1
Algoritmiek: Divide & Conquer
1
2
5
6
6
7 2
Algoritme voor vinden
articulatiepunten
• Doe DFS, en bereken DFS-boom en
waardes prenum
• Bereken waardes highest
• Doorloop DFSboom, en test voor elke
knoop v of het een articulatiepunt is
– Wortel: kijk of meerdere kinderen
– Niet wortel: kijk of kind x bestaat met
highest(x)  prenum(v)
22
Algoritmiek: Divide & Conquer
Werkt in
O(n+a)
tijd
DFS op gerichte grafen
1
• Procedure dfs(v)
– bezocht (v) = true
– for all arcs (v,w)
– do if (bezocht(w) ==
false)
then dfs(w)
2
6
3
7
8
4
O(n+a) tijd
23
5
Algoritmiek: Divide & Conquer
Vier types pijlen:
Tree-arcs
Back-arcs
Cross-arcs
Forward-arcs
Strongly connected components
• Gerichte graaf G is sterk samenhangend als
– Er tussen van elke knoop naar elke andere knoop een
pad is (en terug)
• Sterk samenhangende component:
– Maximale deelgraaf die sterk samenhangend is
• Er is een O(n+a) algoritme dat
– Test of een gegeven gerichte graaf sterk samenhangend
is
– Een gegeven gerichte graaf splitst in sterk
samenhangende componenten.
• Werkcollegeopgave!
24
Algoritmiek: Divide & Conquer
Gerichte acyclische grafen
• Een gerichte graaf is acyclisch als het geen
cycle bevat.
Toepassingen o.a.:
• Representatie van partiele ordeningen
• Afhankelijkheden van scheduling van taken
• Spreadsheet
25
Algoritmiek: Divide & Conquer
26
Algoritmiek: Divide & Conquer
Topologische sortering
• Een nummering van de knopen 1, … , n, zodat
voor elke pijl (v,w) in A:
– nr(v) < nr(w)
5
2
1
• Stelling.
G is acyclisch, d.e.s.d. als
G een topologische sortering heeft
4
3
–  : stel wel een cycle. Kijk naar de nummers als je
langs de cycle gaat …
–  : constructie … (2 verschillende manieren)
27
Algoritmiek: Divide & Conquer
6
Vinden van topologische
sortering by g.a.g. (Algoritme 1)
• Verandering van DFS:
• Initieel: teller = n
• Procedure dfs(v)
– bezocht (v) = true
– for all arcs (v,w)
– do if (bezocht(w) == false)
then dfs(w)
– nb(v) = teller; teller -- ;
28
Algoritmiek: Divide & Conquer
1
6
2
3
7
5
8
Er zijn geen
backedges.
4
Vinden van topologische
sortering (Algoritme 2)
• Als G acyclisch is, dan heeft G een knoop zonder
inkomende pijlen.
– (Want anders …)
• Mogelijk algoritme
teller = 1;
While G heeft nog ongenummerde knopen
do neem knoop v zonder inkomende pijlen vanuit andere
ongenummerde knopen
nb(v) = teller; teller ++;
29
Algoritmiek: Divide & Conquer
Implementatie algoritme 2
• Elke knoop heeft variable unnumin(v): geeft het
aantal pijlen naar v vanuit ongenummerde knopen.
• Verzameling B van knopen met unnumin 0.
• Initialiseer:
• For all v
do unnumin(v) = ingraad van v.
if (unnumin(v) == 0 ) then zet v in B.
• teller = 1;
30
Algoritmiek: Divide & Conquer
Vervolg implementatie
While B niet leeg
O(n+a) tijd
do haal knoop v uit B;
nb(v) = teller; teller++;
for all (v,w)
do unnumin(w) --;
if (unnumin(w) == 0 ) then zet w in B.
Als we niet alle knopen hebben genummerd als we klaar zijn,
dan had de graaf een cycle.
Als we wel alle knopen hebben genummerd, dan is de
nummering een topologische sortering.
31
Algoritmiek: Divide & Conquer
Samenvatting DFS
• Gaat verder van laatst bezochte knoop waar iets
nieuws te ontdekken is
• Kan gebruikt worden om graaf te splitsen in
– Samenhangende componenten (ongericht)
– Dubbelsamenhangende componenten (ongericht)
– Sterk samenhangende componenten (gericht)
• Kan gebruikt worden om te testen of gerichte
graaf acyclisch is, en zo ja, topologische sortering
te geven
– Ook een ander algoritme voor top. sort., zonder DFS
32
Algoritmiek: Divide & Conquer
Breadth First Search
• Graaf doorzoeken als een olievlek
• Waar DFS een stack gebruikt (bijv. door de
recursie) gebruikt BFS een queue
• Kan ook gebruikt worden voor vinden van
samenhangende componenten
• Voor bepalen van afstanden als alle
kanten/pijlen dezelfde lengte hebben
33
Algoritmiek: Divide & Conquer
Pseudocode voor BFS
Procedure BFS(v)
34
Q = lege queue;
O(n+a) tijd
bezocht[v] = true;
Zet v op Q.
Procedure BFSearch(G)
while Q is niet leeg
For all v:
do bezocht[v] = false;
do haal eerste element x uit Q
For all v:
for each w grenzend aan x
do if (not bezocht[v])
do if (bezocht[w] == false)
then BFS(v)
then bezocht[w] = true;
zet w op Q.
Dezelfde soort code als voor
Algoritmiek: Divide &DFSearch
Conquer
Gerichte grafen: net zo
Procedure BFS(v)
Q = lege queue;
bezocht[v] = true;
Zet v op Q.
while Q is niet leeg
do haal eerste element x uit Q
for each (x,w) in A
do if (bezocht[w] == false)
then bezocht[w] = true;
zet w op Q.
35
Algoritmiek: Divide & Conquer
g
e
a
d
f
h
b
i
c
36
Algoritmiek: Divide & Conquer
BFS bezoekt knopen in volgorde
afstand tot startknoop
• Alle knopen met
een kortste pad
naar v met i
knopen worden
bezocht voor de
knopen met een
kortste pad met
i+1 knopen naar
v
d
f
h
b
i
c
37
g
e
a
Algoritmiek: Divide & Conquer
3
g
e
a
4
2
d
2
b 1
f
4
h
i
3
c 0
38
Algoritmiek: Divide & Conquer
5
Kortste paden probleem als alle
kanten / pijlen lengte 1 hebben
• Definieer de afstand in G van s naar t als het
minimum aantal kanten op een pad van s
naar t.
• BFS kan gebruikt worden om deze
afstanden (single source) uit te rekenen
39
Algoritmiek: Divide & Conquer
BFS met afstanden
Procedure BFS(v)
Q = lege queue;
bezocht[v] = true;
d[v] = 0;
Zet v op Q.
while Q is niet leeg
do haal eerste element x uit Q
for each w grenzend aan x (elke pijl (x,w) )
do if (bezocht[w] == false)
then bezocht[w] = true; d[w] = d[x] + 1;
zet w op Q.
40
Algoritmiek: Divide & Conquer
Correctheid afstanden
• Voor elke w: d[w] is minstens de afstand van v
naar w
– Met inductie: er is een pad van v naar w met d[w]
kanten.
• Voor elke w: d[w] is hooguit de afstand van v naar
w
– Kijk naar een kortste pad van v naar w. Laat vi de ide
knoop op dit pad zijn.
– Met inductie: d[vi]  i.
41
Algoritmiek: Divide & Conquer
Concrete en impliciete grafen
• Alle behandelde algoritmen kosten O(n+a) tijd als
we de graaf met de adjacency list representatie
gekregen hebben.
• Algoritmen werken ook voor grafen die we
impliciet hebben gekregen:
– Mechanisme dat van een gegeven knoop vertelt welke
buren het heeft
– Omdat grafen heel (soms zelfs oneindig) groot kunnen
worden slaan we de graaf niet geheel op.
• Toepassingen: game graphs; optimization
problems; …
42
Algoritmiek: Divide & Conquer
Conclusie
• DFS
• Samenhangen: samenhangende componenten;
sterk samenhangend; dubbel samenhangend
• Acyclische grafen en topologisch sorteren
• BFS
43
Algoritmiek: Divide & Conquer
Download