HØGSKOLEN I SØR-TRØNDELAG Avdeling for informatikk og e-læring - AITeL Kandidatnr: Eksamensdato:. desember 00 Varighet: timer (9:00 1:00) Fagnummer: LO117D Fagnavn: Algoritmiske metoder Klasse(r): DA DB Studiepoeng: 6 Faglærer(e): Mildrid Ljosland Hjelpemidler: Alle skriftlige, kalulator Oppgavesettet består av: oppgaver og sider Vedlegg består av: 1 sider Merknad: Oppgaveteksten kan beholdes av studenter som sitter eksamenstiden ut. Lykke til!
Pass på tida! Hvis du ikke får til en oppgave, prøv neste. Så kan du heller komme tilbake til de du ikke har fått til når du har gjort alt som går greit. Oppgave 1 (0%) Lever inn øving 7. Gitt følgende vektede graf som naboliste (første tall er til-node, andre er vekta): 0, 10 1, 1, 10, 8 0, 10, 1 0, 10 1, 10, 0, 17 a) Tegn grafen. Vis også hvordan den blir seende ut i tabellrepresentasjon. 1 0 10 8 17 0 1 0 1 0 10 1 8 10 10 1 10 0 10 17 b) Utfør bredde-først-søk på grafen med utgangspunkt i node. Fortell nøyaktig hvilken rekkefølge de ulike noder/kanter behandles i, og hva som skjer. Hvilken avstand fra får de ulike nodene? Node får distanse 0 og legges i kø. Node hentes ut av køen Naboene 0 og får distanse 1 og legges i kø. Node 0 hentes ut av køen. Naboen har allerede fått distanse mindre enn uendelig og legges ikke inn i køen. Naboen 1 får distanse og legges i køen. Node hentes ut av køen. Naboen 0 er allerede behandlet. Naboen får distanse og legges i køen. Node 1 tas ut av køen. Nabeoen er allerede behandlet.
Naboen får distanse og legges i køen. Node tas ut av køen. Begge naboene (1 og ) er allerede behandlet. Node tas ut av køen. Nå er køen tom og metoden avslutter. Vi har fått følgende tre: 0 1 La grafen representere et flytnettverk, og vektene den maksimale kapasiteten i nettverket. c) Bruk Edmonds Karp-algoritmen til å finne den første flytøkende veien i dette nettverket. Kilde er node og sluk er node. Tegn opp restnettet etter at den første flytøkende veien er brukt. Edmonds-karp-algoritmen bruker Bredde-først-søk til å finne flytøkende veier. Vi kan derfor bruke resultatet fra oppgave b, og finner at første flytøkende vei er -0-1-. maks flyt via denne veien er. Restnettet blir da slik: 1 0 17 0 1 I vedlegg 1 er det gitt en algoritme for å finne maksimal flyt mellom to noder (inkludert noen hjelpemetoder). Algoritmen er tenkt lagt inn i klassen Vgraf som er en subklasse til Graf. d) Programmer metodene finnvei() og veifinnestil() (begge trenger én linje kode) samt metoden finnmaksflyt(). void finnvei(node fra) { bfs(fra); boolean veifinnestil(node til) { return ((Forgj)(til.d)).dist < Forgj.uendelig; int finnmaksflyt(node til) { int maks = Integer.MAX_VALUE; Node fra = ((Forgj)(til.d)).forgj; while (fra!= null) {
Vkant k = (Vkant)finnKant(fra, til); if (k == null) { System.out.println("Fant ikke kant fra " + fra.navn + " til " + til.navn); System.exit(0); if (maks > k.vekt) maks = k.vekt; til = fra; fra = ((Forgj)(til.d)).forgj; return maks; e) Programmer metoden oppdaterflyt(). Du trenger ikke å ta hensyn til at flyt kan kanselleres. void oppdaterflyt(int flyt, Node til, Vgraf svar) { Node svartil = svar.finnnode(til.navn); Node fra = ((Forgj)(til.d)).forgj; while (fra!= null) { Node svarfra = svar.finnnode(fra.navn); Vkant k1 = (Vkant)svar.finnKant(svarFra, svartil); if ( k1!= null) k1.vekt += flyt; // Herfra. else { k1 = (Vkant)svar.finnKant(svarTil, svarfra); if ( k1!= null) { k1.vekt -= flyt; if (k1.vekt <= 0) { if (k1 == svartil.kant1) svartil.kant1 = k1.neste; else { Kant k = svartil.kant1; while (k.neste!= k1) k = k.neste; k.neste = k1.neste; svar.k--; // og hit behandles kanselering av flyt else { svarfra.kant1 = new Vkant(svarTil, (Vkant)svarFra.kant1, flyt); svar.k++; til = fra; svartil = svarfra; fra = ((Forgj)(til.d)).forgj; Oppgave (0%) Begge de følgende algoritmene foretar binærsøk:
private static int søka(int[] tab, int start, int slutt, int finn) { if (start < slutt) { int midt = (start+slutt)/; if (finn > tab[midt]) return søka(tab, midt+1, slutt, finn); else return søka(tab, start, midt, finn); else if (start > slutt) return -1; else return start; private static int søkb(int[] tab, int start, int slutt, int finn) { if (start <= slutt) { int midt = (start+slutt)/; if (finn == tab[midt]) return midt; else if (finn > tab[midt]) return søkb(tab, midt+1, slutt, finn); else return søkb(tab, start, midt-1, finn); else return -1; a) De to algoritmene gir ikke alltid samme svar. Finn ut hva som skiller dem ved å prøve ut begge på tabellen, 8, 8, 10, 10, 10, 10, 10, 1, 1, 1. Er det noe forskjell i kompleksitet (O, Ω og/eller Θ) for de to algoritmene? søka gir alltid ut indeks til første tall som er større eller lik det søkte tallet. søkb velger vilkårlig blant alle som er lik det søkte, og gir ut -1 hvis det ikke finnes. Begge er O(log n). søka avslutter aldri før start >= slutt, så den er også Ω(logn), og dermed Θ(logn). I sokb kan vi være heldige å treffe på første sammenligning, så den blir Ω(1). Dermed vil ikke Θ eksistere for denne algoritmen. Nå skal vi se på følgende problemstilling: Du har fått i oppdrag å lage et hjelpemiddel for kryssordløsere. Det trengs blant annet en metode for å finne alle ord på et bestemt antall bokstaver som inneholder bestemte bokstaver på bestemte plasser, for eksempel alle ord på 8 bokstaver som har 'M' som andre bokstav og 'K' til slutt. Til å hjelpe deg har du ei ordliste som er lagret som en tabell av tekststrenger. Denne ordlista kan sorteres på strenglengdene, deretter kan du sjekke alle ordene med den ønskede lengden, og samle sammen de som oppfyller kravet om rett bokstav på rett plass. b) Hvilken sorteringsalgoritme vil egne seg best når ordlista skal sorters på strenglengder? Begrunn svaret. Siden vi skal sortere på strenglengder, som er et heltall innenfor et begrenset intervall, vil tellesortering være et godt alternativ her. Da får vi en O(n)-algoritme for sorteringen. c) Når ordlista er sortert på strenglengde, kan man bruke binærsøk til å finne ord med en gitt lengde. Hvilken av de to algoritmene gitt i begynnelsen av oppgaven vil egne seg best i denne situasjonen? Hvorfor? (Her og i det etterfølgende kan du anta at algoritmene er modifisert slik at de kan brukes på denne oppgaven.)
soka vil egne seg best. For da får vi indeksen til starten av de ordene som har riktig lengde, og kan sjekke fortsløpende til vi kommer til for lange ord. Hadde vi derimot brukt sokb, måtte vi ha sjekket både forover og bakover. d) Programmer metoden ArrayList finnmuligeord(string[] ordliste, String mønster) der mønster viser hvor langt ordet er og hvilke bokstaver vi kjenner. Ukjente bokstaver markeres med '*', og du kan anta at vi både i ordlista og i mønsteret bare har store bokstaver (bortsett fra '*' i mønster). Her er et eksempel som viser hvordan metoden kan kalles for å finne alle ord på 8 bokstaver med 'M' som andre bokstav og 'K' til slutt: ArrayList svar = finnmuligeord(ordliste, "*M*****K"); private static ArrayList<String> finnmuligeord(string[] ordliste, String mønster) { System.out.println("Mønster: " + mønster); ArrayList<String> res = new ArrayList<String>(); int tablengde = ordliste.length; int ordlengde = mønster.length(); int indeks = søka(ordliste, 0, tablengde, ordlengde); System.out.println("Start på indeks " + indeks); while(indeks < tablengde && ordliste[indeks].length() == ordlengde) { if (test(ordliste[indeks], mønster)) { res.add(ordliste[indeks]); System.out.println(ordliste[indeks] + " lagt til"); indeks++; return res; private static boolean test(string ord, String mønster) { for (int i=0; i < mønster.length(); i++) { char bokstav = mønster.charat(i); System.out.println("tester " + bokstav + " mot " + ord.charat(i)); if (bokstav!= '*' && ord.charat(i)!= bokstav) return false; return true; e) Hva blir kompleksiteten til finnmuligeord når ordlista inneholder n ord og du skal finne ord på m bokstaver? Du kan anta at det finnes k ord som har lengde m. Forklar hvordan du kommer fram til svaret. Søke etter riktig lende: Θ(n) Sammenligne bokstavene i ordet med mønsteret: O(m). Dette må gjøres k ganger. Totalt: O(n + mk) 6
Vedlegg 1 Vgraf maksimalflyt(node kilde, Node sluk) { Vgraf svar = kopiernoder(); Vgraf restnett = kopiergraf(); Node fra = restnett.finnnode(kilde.navn); Node til = restnett.finnnode(sluk.navn); restnett.finnvei(fra); while (restnett.veifinnestil(til)) { int maks = restnett.finnmaksflyt(til); restnett.oppdaterflyt(maks, til, svar); restnett.oppdaterrest(maks, til); restnett.finnvei(fra); return svar; Vgraf kopiernoder() { Vgraf res = new Vgraf(); res.n = N; res.k = 0; res.node = new Node[N]; for (int i=0; i < N; ++i) { res.node[i] = new Node(node[i].navn); return res; Vgraf kopiergraf() { Vgraf res = kopiernoder(); res.k = K; for (int i=0; i < N; ++i) { Vkant kant = (Vkant)node[i].kant1; while (kant!= null) { Node til = res.finnnode(kant.til.navn); res.node[i].kant1 = new Vkant(til, (Vkant)res.node[i].kant1, kant.vekt); kant = (Vkant)kant.neste; return res; Node finnnode(string navn) { /* Gjelder generelt: for (int i=0; i < N; ++i) { if (node[i].navn.equals(navn) return node[i]; return null; */ // Gjelder hvis navn er lik plassering int nr = Integer.parseInt(navn); return node[nr]; Kant finnkant(node fra, Node til) { Kant k = fra.kant1; while(k!= null && k.til!= til) k = k.neste; return k; 7