Litt om grafer og traversering, og om hashing. Jeg gikk en tur i Tredje forelesning
Ikke la dere lure av ordet reduksjon her! X? Det er jo bare å Y. Hvilken vei gir informasjon? Hvis jeg vil vise at A er vanskelig og jeg vet at B er vanskelig må jeg redusere _ til _
Hvis jeg vil vise at A er vanskelig og jeg vet at B er vanskelig må jeg redusere B til A «B? Det er jo bare å A» Hvis A var enkelt ville B også være det
Vanskelig Enten Da kunne du jo Motsatt Ukjent Eller Det er jo bare å Enkelt
Sykelløs Traversering
Først: Høyreregelen. Så: Rekursiv formulering. Bilde: Hver node er en person. Det sendes rundt en påmeldingsliste. Hver person er ansvarlig for at alle underordnede signerer. Når har hver person lista? (Når signerer han/hun? Pre-, post- og in-fix.) B D F A C E G D B A B C B D F E F G F D
Traversering av trær. Dybde-først-søk, egentlig. Kjerneeksempel på rekursjon. Helt «likt» med mange rekursive algoritmer som ikke eksplisitt har trær å jobbe med. Helt essensielt å forstå grundig! Først: Prefikstraversering.
Infiks
Postfiks
Søketre-egenskap. Søk: «Ledet DFS» bare halve det rekursive arbeidet. Minimum og maksimum kan finnes lett. Hva blir kjøretiden på alt dette? D B F A C E G
Forg jenger/ etterkommer: Ser bare på etterkommer. Vi vil nå kunne gå oppover, så vi har piler begge veier. D Tilfelle 1: Vi har et høyre deltre velg minimum der. Tilfelle 2: Vi har ikke et høyre deltre. Velger da «Den laveste forfader med venstre barn som også er en forfader (eller oss selv)». B F A C E G
Best-case/average-case: Roughly balansert. Full traversering: O(n). Å traversere k etterfølgende noder: O(lg n + k). Søk, minimum, maksimum, forgjenger, etterfølger: O(lg n). Generelt (worst-case): «h» (høyden) i stedet for «lg n». Vi kan få h = n (lenket liste). log 2 n n 2 n 1 n n 2 = n 1 1 1 n Nok et bilde for å huske antall interne noder: Anta at alle foreldre (interne noder) har to iskrem hver, og at de gir dem til barna sine. Alle vil da ha is, bortsett fra rota. Ser du hvorfor det betyr at vi har n løvnoder og n 1 interne?
Binærsøk
Søk Kun sortert Rekursiv Halvering Tabell Liste Θ(lg n) Binærsøk
ALF Tenk på et tall mellom 1 og 100. BETH OK. (Hun tenker på 42.) ALF Er det over 50? BETH Nei. ALF Over 25? BETH Ja. ALF Over 37? BETH Ja. Over 43? Nei. Over 40? Ja. Over 42? Nei. Over 41? Ja. ALF BETH ALF BETH ALF BETH ALF BETH 7 spørsmål = lg2 100
Vi leter etter 27. 3 13 15 16 27 28 29 32 39 46 49 73 77 85 88 3 13 15 16 27 28 29 32 39 46 49 73 77 85 88 3 13 15 16 27 28 29 32 39 46 49 73 77 85 88 3 13 15 16 27 28 29 32 39 46 49 73 77 85 88 3 13 15 16 27 28 29 32 39 46 49 73 77 85 88 Forkast halve tabellen i hver iterasjon Her spesialbehandler vi midt-elementet Ja, vi kan velge om vi vil bruke rekursjon eller iterasjon. Og: Ved at vi sjekker midt-elementet spesifikt kan vi av og til avslutte tidligere. Konstant best-case.
n 1 n log 2 n n 2 n 2 = n 1 1 1 n
Nøkler i interne noder eller ikke Binære eller ikke Fortsatt traversering av trær. Denne gangen: På leting etter løvnode *med* veiskilt. + Søketrær
Søk Ordnede verdier Rekursiv «Halvering» Tilfeldige verdier Sorterte verdier Avg Θ(lg n) Ω(1), O(n) Søk i søketre uten balansering
Innsetting og søk. Rekursjon igjen. D B F A C E G
Hashfunksjoner kan være brutale Hashing
Nøkler mappes til posisjoner i en tabell; heltall kan f.eks. brukes direkte som indeks (direkte hashing). 0 1 / 0 3 2 1 2 1 X 2 Y 5 4 / 3 4 4 Z / 5
men ordet hashing kommer av det at vi hakker opp og knøvler nøkkelen. (Bilde: Hash brown potatoes, aka hash browns.)
( Bør ikke forveksles med hash brownies )
Vi knøvler (hasher) fordi vi da kan tillate større (og merkeligere) verdiområder. Hver nøkkel får en hashverdi (som er en indeks inn i tabellen) vha. en hashfunksjon. k 1 / 0 h(k 1 ) k 3 k 2 / h(k 2 ) h(k 2 ) / m 1
Her er de grønne og blå spiralene faktisk (utrolig nok) samme farge Mer her: http://www.psy.ritsumei.ac.jp/~akitaoka/color12e.html
k 1 h(k 1 ) = h(k 2 ) k 1 k 1 / k 2 Vi kan nå få kollisjoner (fordi verdiområdet er større enn lengden til tabellen; «pigeonholeprinsippet»). Vi løser det f.eks. ved å legge flere nøkler i hver «bås» (vha. en lenket liste). Vi må bare sørge for jevn fordeling = korte lister.
/ w X / / Y «Chaining»: Vi kan, i stedet for å bruke en liste, hoppe videre i tabellen (etter et mønster) for å finne en ledig plass. Når vi leter etter en nøkkel må vi følge samme prosedyre. Z / /
Eksempel: Vi kan også bruke en form for hashing til å sortere med! (Bucket Sort) Litt uvanlig: Rekkefølge på bøttene, og en «mening» med hvor ting plasseres. Vanligvis prøver vi jo å være «tilfeldige». Hvis vi har nok bøtter, og verdiene er relativt jevnt fordelt (f.eks. uniformt tilfeldige) så vil hver bøtte ha (forventet) konstant størrelse (og det vil ta konstant tid å sortere hver av dem for seg.) 0 1
Direkte oppslag God plass Statiske objekter Beregn indeks Håndter kollisjoner Veldig kjapt Kollisjonsfare O(1) Avg-case Hashing
Grafer
En digraf. Rettet (directed) graf. (Evt. på tavla: Definisjon, G=(V, E). Self-loops. incident from/to, leaves/enters. Kant fra/til.) 1 2 3 4 5 6
Merk: Man kan så å si alltid simulere en urettet graf ved hjelp av en digraf ved å ha kanter i begge retninger. Dermed virker f.eks. Dijkstras algoritme på grafer. 1 2 3 4 5 6 En (urettet) graf. incident on. Naboer er adjacent i begge typer. degree/(kant-)grad. Isolert. In-degree/outdegree.
Path, lengde k. Inneholder visse noder. Reachable. Simple path. Subpath. Cycle. Simple cycle. (Strongly) connected component. 1 2 3 4 5 6
En indusert graf (av nodesettet {1, 2, 3, 6}). 1 2 3 6
Representasjoner Nabolister Nabomatrise Nabotabeller Kantliste Nabokantlister Nabokantmatrise og mange andre
Venstrehåndsregelen. Fungerer bare hvis vi ikke har sykler.
To completely traverse all the passages of a labyrinth twice, from any initial point, simply follow the rules posed by Trémaux, marking each entry to or exit from an intersection. These rules may be summarized as follows: When possible, avoid passing an intersection you have already visited, and avoid taking passages you have already traversed. Is this not a prudent approach, which also applies in everyday life? Édouard Lucas, Récréations Mathématiques (1891) Trémaux s algoritme har faktisk blitt nevnt i en Simpsons-episode.
Traversering som for trær (som jo er en type grafer): Vedlikehold en liste med noder du har oppdaget (to do-lista) og noder du har besøkt (krysset av). Når du besøker en node oppdager du (de ubesøkte) naboene. Hvis du har en FIFO-TODO så får du BFS; har du en LIFO-TODO får du DFS. Alle andre rekkefølger også OK (best-first, random-first ) og gir full traversering. (Usammenhengende grafer litt spesielt.)
def walk(g, s): # Walk the graph from node s P, Q = dict(), set() # Predecessors + "to do" queue P[s] = None # s has no predecessor Q.add(s) # We plan on starting with s while Q: # Still nodes to visit u = Q.pop() # Pick one, arbitrarily for v in G[u].difference(P): # New nodes? Q.add(v) # We plan to visit them! P[v] = u # Remember where we came from return P # The traversal tree Fra «Python Algorithms». All koden fra boka kan lastes ned gratis (og lovlig ;-) på nett.
def components(g): comp = [] seen = set() for u in G: if u in seen: continue C = walk(g, u) seen.update(c) comp.append(c) return comp # The connected components # Nodes we've already seen # Try every starting point # Seen? Ignore it # Traverse component # Add keys of C to seen # Collect the components Fra «Python Algorithms»
Adventurers always go right Ikke alle grafer ser ut som grafer Men hvordan navigerer vi i grafer? Eksempel: (1) Besøk aldri et sted mer enn én gang; (2) gå alltid til (f.eks.) høyre; (3) snu 180 i blindveier. D.F.S.
Evt. google flood-fill for et viktig eksempel. def dfs(g, s, S=None): if S is None: S = set() S.add(s) for u in G[s]: if u in S: continue dfs(g, u, S) # Initialize the history # We've visited s # Explore neighbors # Already visited: Skip # New: Explore recursively Fra «Python Algorithms»
D.F.S. Foreldre skrives opp før barn Discover-time
D.F.S. Foreldre strykes ut etter barn Backtracking/finish-time
Ikke veldig sentralt pensum beskrives ikke i Kleinberg. Tree edges Traveseringstreet Forward edges Back edges Cross edges Fremover i treet Bakover i treet Andre kanter Kan avgjøres ved hjelp av discover-time og finish-time.
Leting i spiralmønster B.F.S. En annen fremgangsmåte: Jobb deg ut fra startpunktet nivå for nivå i spiral. Nye områder du kommer i kontakt med må vente ( stå i kø ) til du er ferdig med nåværende runde (dvs. det som alt står i kø ).
def bfs(g, s): P, Q = {s: None}, deque([s]) # Parents and FIFO queue while Q: u = Q.popleft() # Constant-time for deque for v in G[u]: if v in P: continue # Already has parent P[v] = u # Reached from u: u is parent Q.append(v) return P Fra «Python Algorithms»
Kjekt å vite Korrekthet (rekursjon, induksjon) for korteste vei og to-farging. BFS kan finne én-til-alle korteste vei DFS har andre nyttige egenskaper All traversering kan brukes til to-farging Vi snakker her om *uvektet* korteste vei. Mer om DFS-anvendelser neste gang. En tofargbar graf kalles også *bipartitt*. Nodene kan deles i to mengder uten interne kanter (f.eks. konflikter). Trefarging (etc.) er atskillig vanskeligere ingen kjente metoder.
Besøk noder BFS: Korteste vei Uvektede grafer «Huskeliste» Oppdateres Svært anvendelige Ganske naive O(E+V) BFS og DFS