|
© h.hofstede (h.hofstede@hogeland.nl)
|
|
Grafen
doorzoeken. |
|
|
|
|
Als je een grote
graaf wilt doorzoeken (op zoek naar een knooppunt of
verbindingslijn met een bepaalde eigenschap) dan is het wel handig als
je dat op een systematische en efficiënte manier kunt doen. Dat gaat
uiteindelijk toch vaak met een computer, dus het is wel handig om een
soort zoek-algoritme te hebben.
Nou zijn er daarvoor twee soorten op de markt: Diepte-eerst
(Depth-First-Search; DFS) en Breedte-eerst (Breadth-First Search; BFS).De
Engelse benamingen zijn zo algemeen dat ik ze overneem, en het zal
hebben over DFS- en BFS-Search.
We nemen aan dat de grafen die we onderzoeken samenhangend zijn (als dat
niet zo is moeten we de afzonderlijke componenten apart onderzoeken)
en geen lussen hebben (als die er wel zijn laten we ze gewoon
weg!). |
|
|
|
|
Depth-First-Search (DFS). |
|
|
|
|
Het DFS-algoritme is
een beetje verschillend voor gerichte en ongerichte grafen, dus die
bekijken we apart. |
|
|
|
|
1. Ongerichte grafen.
De knooppunten noemen we Zoon (Z)
of Vader (V) waarbij we onthouden welke vader bij welke zoons hoort.
In het begin noemen we alle verbindingslijnen bruikbaar
maar in de loop van de zoektocht kunnen die veranderen in
onbruikbaar of onderzocht.
De zoekmethode werkt als volgt: |
1. |
Kies een willekeurig
beginknooppunt en geef het nummer 1 . |
2. |
Kies een willekeurige
bruikbare verbindingslijn vanaf dat knooppunt.
Als je bij een knooppunt komt dat al eerder een nummer kreeg is, dan
ga je terug en noem je de gebruikte verbindingslijn onbruikbaar
en begint weer bij 2.
Als het knooppunt nog geen nummer heeft, dan geeft je het het volgende
nummer, en je onthoudt wat de vader was (dat was het knooppunt waar je
vandaan kwam).
Maak de verbindingslijn V-Z gericht (van V naar Z) en die
verbindingslijn noemen we vanaf nu onderzocht. |
3. |
Herhaal stap 2
vanaf het nieuwe knooppunt. Net zolang totdat er geen verbindingslijnen
meer te vinden zijn. |
4. |
Als er geen
verbindingslijnen meer te vinden zijn, dan ga je terug naar de vader van
dit knooppunt en begint vanaf daar weer met stap 2. Als je al bij
het beginknooppunt bent (dus als er gaan vader meer is), ben je klaar. |
|
|
|
|
In een stroomdiagram
zou dat er zó uitzien:
(bij het paarse blokje gaan we een stapje "dieper" de graaf in, bij het
blauwe blokje nemen we een stapje "terug") |
|
|
|
|
|
|
|
|
|
Hieronder zie je in
12 plaatjes het algoritme in werking (leesvolgorde).
Elke keer als een stippellijn verschijnt is een verbindingslijn gekozen
die naar een knooppunt leidde dat al een nummer had.
Waarschijnlijk snap je nu ook waarom het Depth-First heet: we gaan
eerst zo diep mogelijk de graaf in! |
|
|
|
|
|
|
|
|
|
De rode
verbindingslijnen leveren uiteindelijk een gerichte graaf met alle
knooppunten op, en die wordt wel een DFS-boom van de graaf genoemd. Je
ziet dat hij bestaat uit "routes van familieleden". Twee
routes in dit geval. Bij het volgen van
een route ga je steeds van vader naar zoon.
Deze zoekmethode heet ook wel een driekleuren-algoritme: er zijn
op elk moment drie soorten knooppunten:
• witte: nog niet bezocht
• grijze: wel bezocht maar nog niet afgesloten
• zwarte: afgesloten.
Dan ziet het er zó uit: |
|
|
|
|
|
|
|
|
|
2. Gerichte grafen. |
|
|
|
|
Daarvoor kun je bijna
hetzelfde algoritme gebruiken, met één klein verschilletje. |
Het kan namelijk
gebeuren dat je vanaf een knooppunt geen nieuwe knooppunten meer kunt
bereiken, maar dat de graaf toch nog niet "klaar" is.
Kijk maar naar het graafje hiernaast.
Als je aan de linkerkant begint zul je nooit de rechterkant bereiken.
Na drie knooppunten wil het niet verder.
In die gevallen moet je gewoon ergens bij een nieuwe willekeurige nog
niet bezochte knoop opnieuw beginnen. Ofwel na de
STOP
uit het stroomdiagram moet je kijken of alle knopen een nummer hebben,
en als dat niet zo is er eentje zonder nummer kiezen en doorgaan. |
|
|
|
|
|
In zo'n geval zal het
eindresultaat niet uit één boom bestaan maar uit verschillende losse
bomen. Dat heeft een bos.
Mooi meegenomen:
Bij grafen die uit verschillende losse componenten bestaan vind je op
deze manier meteen hoeveel zulke componenten er zijn (namelijk
gewoon door te kijken hoe vaak je "opnieuw" moest beginnen). |
|
|
|
|
Cykels
vinden. |
|
|
|
|
Deze DFS-zoektocht
levert meteen bij gerichte grafen een mooie manier op om cykels te
vinden als die er zijn.
Stel dat de nieuwe lijn die je onderzoekt van knooppunt x
naar knooppunt y loopt. Dan zijn er 3 mogelijkheden: |
• |
y is één van
de zonen van x Dan heet de lijn xy een
vooruit-lijn (forward-edge).
Het nummer van
x is dan kleiner dan dat van y. |
• |
x is één van
de zonen van y. Dan heet lijn xy een
terug-lijn (back-edge). Het nummer van y is dan
kleiner dan dat van x |
• |
x en y
zijn geen familie van elkaar. Het nummer van
y is dan kleiner dan dat van x immers y was al
eerder bereikt.
Dat noemen we een kruis-lijn (cross-edge). |
|
|
|
|
|
|
|
|
|
En nu hebben we
meteen ontdekt of er cykels zijn: |
|
|
|
|
er is een cykel ⇔ er is een terug-lijn
geweest bij de DFS |
|
|
|
|
|
Voorbeeld. |
|
|
|
|
Hieronder zie je een graaf, met
daaronder het DFS-algoritme in werking (in leesvolgorde, grijs-wit-zwart
zoals hierboven omschreven)
• Rode verbindingslijnen komen in de uiteindelijke boom/bos.
• Blauwe lijnen zijn terug-lijnen en geven cykels.
• Groene lijnen zijn vooruit-lijnen.
• Paarse lijnen zijn kruis-lijnen.
Uiteindelijk levert het een bos (van twee bomen) en een cykel op. |
|
|
|
|
|
|
|
|
|
Tuurlijk, ik weet het: die
cykel en dat bos had je zó in één oogopslag ook wel gezien. Maar bedenk
je steeds dat die algoritmes zijn bedoeld om door een computer
uitgevoerd te worden. Vraag je maar eens af of je in een gerichte
graaf met zo'n 1000 knooppunten ook zo snel een cykel kunt vinden
en kunt zien uit hoeveel bomen het bos bestaat...... |
|
|
|
|
|
|
|
|
OPGAVEN |
|
|
|
|
1. |
Je hebt 3 kannen met inhoud 8, 5 en
3 liter.
De grootste kan zit vol met 8 liter water, en door water heen en
weer te gaan gieten wil je graag die 8 liter verdelen in
tweemaal 4 liter. De kannen hebben geen maatstreepjes, dus je
moet een kan steeds helemaal leeggieten of helemaal vullen.
Geef een "watertoestand" aan met de drie hoeveelheden in de drie
kannen (volgorde 8-5-3).
De begintoestand is dus (8,0,0), en de eindtoestand moet (4,4,0)
worden.
Hieronder zie je het begin van een graaf waarin alle mogelijke
toestanden als knooppunten staan aangegeven.
Verbind in deze graaf de toestanden die in elkaar over kunnen
gaan en los daarna met het DFS-algoritme dit gietprobleem
op. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
© h.hofstede (h.hofstede@hogeland.nl)
|