370 likes | 587 Views
Alg. Dat. Øvingsforelesning 3 Grafer, BFS, DFS og hashing Børge Rødsjø rodsjo@stud.ntnu.no. Dagens tema. Grafer Terminologi Representasjon av grafer Bredde først søk (BFS) Dybde først søk (DFS) Hashing Hashfunksjoner, hashtabeller Kollisjonshåndtering Øving 2: Redd Ratatosk
E N D
Alg. Dat Øvingsforelesning 3 Grafer, BFS, DFS og hashing Børge Rødsjø rodsjo@stud.ntnu.no
Dagens tema • Grafer • Terminologi • Representasjon av grafer • Bredde først søk (BFS) • Dybde først søk (DFS) • Hashing • Hashfunksjoner, hashtabeller • Kollisjonshåndtering • Øving 2: Redd Ratatosk • Øving 3: Kobra lærer å stave
Terminologi: Grafer • Node • Kant • Nabo • Sykel • Rettet graf • DAGs • Trær
Generelle grafer vs. trær • Grafer er en overordnet, generell struktur • Et tre er en graf som er sammenhengende, asyklisk og urettet • I graftraversering er ”farging” nyttig • I en graf kan man oppdage grå eller svarte noder på nytt • Vi må huske hvilke noder vi har sett
Representasjon av grafer • En graf består av noder og relasjoner • G = (V, E). V er alle nodene, E er relasjoner mellom nodene (i dagligtale: kanter)
Representasjon av grafer • En graf består av noder og relasjoner • G = (V, E). V er alle nodene, E er relasjoner mellom nodene (i dagligtale: kanter)
Representasjon av grafer • En graf består av noder og relasjoner • G = (V, E). V er alle nodene, E er relasjoner mellom nodene (i dagligtale: kanter)
Representasjon av grafer • En graf består av noder og relasjoner • G = (V, E). V er alle nodene, E er relasjoner mellom nodene (i dagligtale: kanter)
Nabolister • Hver node har en liste over sine naboer • Nyttigst hvis det er få kanter i forhold til antall noder (en sparse graf).
Nabomatrise • En nxn matrise der en nabo er representert med en verdi • Nyttig hvis grafen er tett (dense graf)
Bredde først søk (BFS) • En enkel algoritme for å søke i en graf • Har en kø over oppdagede (grå) elementer • Vi har en mengde/liste av besøkte(ferdige) noder • Se side 531 i Cormen
Bredde først søk (BFS) • Begynn med å legge startnoden i kø • Så lenge det finnes noder i køen: • Plukk ut en node x fra starten av køen • Legg alle naboer som ikke er besøkte eller oppdagede inn i køen (vi oppdager/gråfarger dem) • Legg x inn i besøkt-mengden (farge den sort)
Kode for BFS def bfs(root): queue = Queue() queue.put(root) while len(queue) > 0: node = queue.get() # gjør noe fancy med noden her node.colour = Black for adj in node.adjacent: if adj.colour == White: adj.colour = Grey queue.add(adj)
Bruk av BFS • Finne korteste vei fra en node til alle andre, i en uvektet graf • Kan sjekke om en graf er bipartitt • BFS er en viktig grunnstein for mer avanserte algoritmer
Kjøretid BFS • Med naboliste: O(V + E) • Må besøke alle noder (V), og sjekke alle kanter (E) • Med nabomatrise: O(V2) • Må besøke alle noder (V), og sjekke alle kanter (V2)
Dybde først søk (DFS) • Også en enkel algoritme for å søke i en graf • Starter i en node og søker i dybden så langt det går • Se side 541 i Cormen
Implementere DFS • Kan implementeres via både rekursjon og iterasjon • Med rekursjon så kaller metoden seg selv • Dette er en treg måte å gjøre det på • Med iterasjon har man nodene i en stakk • Kjappere, behøver ikke rekursive kall
Kode for rekursiv DFS def dfs(node): node.colour = Grey for adj in node.adjacent: if adj.colour == White: dfs(adj) # gjør noe fancy med noden her node.colour = Black
Iterativ DFS • Tar vare på nodene i en stakk. • Alle noder er hvite før man oppdager dem • Når en node blir oppdaget blir den fargelagt grå, og lagt til i stakken. • En node er ferdig når alle dens barn er ferdigbehandlet; da taes noden ut av stakken og fargelegges svart
Bruk av DFS • Brukes i mange andre algoritmer • Strongly connected components (neste gang) • Topologisk sortering (neste gang) • Kjøretid: O(V + E) • Alle noder(V) må besøkes, og alle kanter(E) må sjekkes
Hashing og hashtabeller Problemet vi søker en løsning på: Man har et lite/moderat antall elementer, i et stort verdiområde. Hvordan lagre og søke etter disse effektivt? Eks: Telefonnummer og navn på ansatte. Direkte-adressering vil kreve altfor stor plass.
Hvordan løser hashing dette? • Hashing er en måte å konvertere verdier fra et stort utfallsrom til et som er mye mindre. • Hashing gir en form for fingeravtrykk av en verdi. • Vi kan bruke dette til å lagre og hente data effektivt fra en liten og kompakt tabell
Hashtabeller: Fordelene • Oppslag i O(1) tid • Innsetting i O(1) tid • Sletting i O(1) tid O(1) betyr ”konstant tid” Dvs. at hastigheten på operasjonene ikke er avhengig av antall elementer i tabellen • NB! Dette er average-case, ikke worst case
Hashtabeller • En tabell hvor vi får en hash av dataene til å beskrive hvor vi lagrer dem.
Hashing • Hashfunksjon: • h(k) = x • h er hashfunksjonen vi har valgt oss • k er hashnøkkelen, hele eller deler av dataene • x er hashen av nøkkelen, dvs. posisjonen der vi plasserer dataene i en hashtabell
Valg av hashfunksjon • Mål: transformere potensielt store data til en indeks i en tabell • Påkrevd egenskap: Deterministisk • Ønsket egenskap: Uniform fordeling • Ønsket egenskap: Kjapp å utføre
Valg av hashfunksjon Noen eksempler på enkle, gode funksjoner (Se Cormen kap. 11): • Divisjonsmetoden (”modulo-metoden”) • h(k) = k mod m • Multiplikasjonsmetoden • h(k) = m (k A mod 1)
Valg av hashfunksjon Noen eksempler på dårlige hashfunksjoner: • En konstant funksjon: • h(k) = 20 • Java 1.1 (før 1998): • java.lang.String.hashCode() benyttet kun de første 16 bokstavene i en string til å generere hashen.
Håndtering av kollisjoner • Kjeding (”chaining”) • Lagrer en lenket liste i hver hash-bøtte • Hvis vi får mange kollisjoner tar det tid å lete etter elementene • Fordel: Enkelt å implementere • Ulempe: Kan bli tregt, og kan bli en del overhead
Håndtering av kollisjoner • Lineær søking • Hvis det er en kollisjon, prøv neste plass i tabellen • Ulempe: ”Primary clustering” – yter enda dårligere enn kjeding hvis man har mange kollisjoner
Håndtering av kollisjoner • Kvadratisk søking • Hvis det er en kollisjon, prøv å hoppe videre slik: • For hopp i: • Posisjon = (h(k) + a*i + b*i2) mod m • Fordel: Yter bedre enn de to forrige • Ulempe: ”Secondary clustering” – kan fremdeles bli problemer hvis mange elementer hasher til samme posisjon
Håndtering av kollisjoner • Dobbel hashing • Bruker 2 hashfunksjoner, h1(k) og h2(k) • Prøv først plass h1(k) i tabellen • Hvis det oppstår kollisjon, prøv å hoppe h2(k) posisjoner videre helt til vi når en åpen plass • Fordel: Enkel og kjapp å implementere
Øving 2: Redd Ratatosk • Hvorfor vil BFS være bedre enn DFS her? • Ratatosk har lik sjanse til å være på hvert nivå • Færre noder per nivå nært roten av treet • BFS sjekker da først de mest sannsynlige nodene • Løsningsforslag ligger ute • BFS bruker kø • DFS bruker stakk
Øving 2: Redd Ratatosk • Tweak-løsning • Vi vet allerede hvilken node Ratatosk er i • Trenger ikke å lese all input, og konstruere tre • Husk koblinger ”barn->forelder”, fremfor andre veien • La Ratatosk ”klatre ned” treet ved å følge koblingene • Denne spesifikke løsningen er ikke eksamensrelevant
Øving 3: Kobra lærer å stave • def bygg(ordliste): • Skal bygge et tre ut fra ei liste av (ord, posisjon) • Skal returnere rot-noden • def posisjoner(ord, indeks, node): • Skal returnere ei liste av posisjoner der ”ord” matcher • Hvis man møter spørsmålstegn, må man sjekke alle subtrær rekursivt, ved å spesifisere indeks og node i nye kall til posisjoner