Longest. increasing. subsequence. Betingelser. Matrise- common. Grådig vs. DP. Forside. Intro. Fibonacci-tall. Memoisering DP

Like dokumenter
Longest increasing. subsequence Betingelser. Longest. common subsequence. Knapsack Grådig vs. DP Moro: 2D-Nim Spørsmål. Forside. Repetisjon.

Algdat - øvingsforelesning

Dynamisk programmering Undervises av Stein Krogdahl

Dynamisk programmering

Rekursiv programmering

INF Algoritmer og datastrukturer

Dagens stoff er hentet fra kapittel 9 i læreboka, samt kapittel 20.5 (som vi «hoppet over» sist)

Alle mot alle. Åttende forelesning. (eller eller Bellman-Ford, eller BFS, alt ettersom) fra alle noder.

Grådige algoritmer. Lars Vidar Magnusson Kapittel 16. Aktivitetvelgingsproblemet Huffmankoder

LO118D Forelesning 2 (DM)

Live life and be merry

Choices, choices. Tiende forelesning. Dynamisk programmering: En serie med valg der valgmulighetene er avhengige av hva vi har valgt før.

Dagens plan. INF Algoritmer og datastrukturer. Koding av tegn. Huffman-koding

Dijkstras algoritme. Her finnes det også (minst) en riktig rekkefølge for Relax, men den må vi oppdage litt etter hvert.

Dynamisk programmering

ALGORITMER OG DATASTRUKTURER

INF Algoritmer og datastrukturer

Norsk informatikkolympiade runde. Sponset av. Uke 46, 2016

Løsnings forslag i java In115, Våren 1996

Choices, choices. Tiende forelesning. Dynamisk programmering: En serie med valg der valgmulighetene er avhengige av hva vi har valgt før.

INF 4130 Oppgavesett 3, 20/ m/løsningsforslag

INF2220: Time 12 - Sortering

Norsk informatikkolympiade runde

Rekursiv programmering

Algdat Eksamensforelesning. Nils Barlaug

Norsk informatikkolympiade runde

Norsk informatikkolympiade runde. Sponset av. Uke 46, 2017

ALGORITMER OG DATASTRUKTURER

Norsk informatikkolympiade runde

MAT1030 Forelesning 28

Rekursjon som programmeringsteknikk

EKSAMEN. Dato: 18. mai 2017 Eksamenstid: 09:00 13:00

Live life and be merry

LO118D Forelesning 12 (DM)

Kjøretidsanalyse. Hogne Jørgensen

INF2220: Time 4 - Heap, Huffmann

Norsk informatikkolympiade runde. Sponset av. Uke 46, 2015

Dijkstras algoritme Spørsmål

Divide-and-Conquer. Lars Vidar Magnusson

Algdat Oppsummering, eksamen-ting. Jim Frode Hoff

Kondisjonstest. Algoritmer og datastrukturer. Python-oppgaver - LF. Onsdag 6. oktober Her er noen repetisjonsoppgaver i Python.

ALGORITMER OG DATASTRUKTURER

ALGORITMER OG DATASTRUKTURER

Øvingsforelesning 5 Python (TDT4110)

MAT1030 Diskret Matematikk

Python: Rekursjon (og programmering av algoritmer) Python-bok: Kapittel 12 + teoribok om Algoritmer

Dijkstras algoritme. Her finnes det også (minst) en riktig rekkefølge for Relax, men den må vi oppdage litt etter hvert.

Et eksempel: Åtterspillet

Steg 1: Rest etter divisjon

Øvingsforelesning 1 Python (TDT4110)

IN Algoritmer og datastrukturer

Finne ut om en løsning er helt riktig og korrigere ved behov

4 Matriser TMA4110 høsten 2018

Kontinuasjonseksamen i fag SIF8010 Algoritmer og Datastrukturer Torsdag 9. August 2001, kl

Anbefalte forkunnskaper Studentene forutsettes å kunne programmere, for eksempel ved å ha tatt TDT4100 Objektorientert programmering.

INF1020 Algoritmer og datastrukturer GRAFER

Eksamensoppgave i TDT4120 Algoritmer og datastrukturer

Norsk informatikkolympiade runde

Øvingsforelesning 5 Python (TDT4110)

NIO 1. runde eksempeloppgaver

Skilpaddefraktaler Erfaren Python PDF

INF120: Oblig 3. Yngve Mardal Moe

Python: Løkker. TDT4110 IT Grunnkurs Professor Guttorm Sindre

Norsk informatikkolympiade runde. Sponset av. Uke 46, 2014

TDT4105 Informasjonsteknologi, grunnkurs. Mer om funksjoner: - rekursive funksjoner

Definisjon av binært søketre

Norsk informatikkolympiade runde

Live life and be merry

Kondisjonstest. Algoritmer og datastrukturer. Python-oppgaver. Onsdag 6. oktober Her er noen repetisjonsoppgaver i Python.

Finne ut om en løsning er helt riktig og korrigere ved behov

Algdat-ninja på 60 minutter: Et galskapsprosjekt. Magnus Lie Hetland

Notater til INF2220 Eksamen

Algoritmer og Datastrukturer IAI 21899

Løsnings forslag i java In115, Våren 1998

Divide-and-Conquer II

Norsk informatikkolympiade runde

Pensum: 3. utg av Cormen et al. Øvingstime: I morgen, 14:15

TDT4110 IT Grunnkurs Høst 2012

I dag skal vi ved hjelp av ganske enkel Python-kode finne ut om det er mulig å tjene penger på å selge og kjøpe en aksje.

Python: Løkker. TDT4110 IT Grunnkurs Professor Guttorm Sindre

Løsningsforslag for eksamen i fag SIF8010 Algoritmer og datastrukturer Lørdag 9. august 2003, kl

Avsluttende eksamen i TDT4120 Algoritmer og datastrukturer

Læringsmål og pensum. Algoritmeeffektivitet

Norsk informatikkolympiade runde

TDT4110 Informasjonsteknologi grunnkurs: Tema: Algoritmer i praksis. Professor Alf Inge Wang

KONTINUASJONSEKSAMEN I FAG ALGORITMER OG DATASTRUKTURER

Forelesning 25. MAT1030 Diskret Matematikk. Litt repetisjon. Litt repetisjon. Forelesning 25: Trær. Dag Normann

SIF8010 ALGORITMER OG DATASTRUKTURER

MAT1030 Diskret Matematikk

Spenntrær, oppsummert: Kruskal: Traverserer ikke. Plukker kanter i hytt og vær Prim: Legger alltid til den noden som er nærmest treet

Hash-funksjoner. Introduksjon. Steg 1: Strekkoder. Eksempel. Skrevet av: Martin Strand

MAT Oblig 1. Halvard Sutterud. 22. september 2016

Definisjon. I et binært tre har hver node enten 0, 1 eller 2 barn

Steg 1: Regneoperasjoner på en klokke

TDT4110 IT Grunnkurs Høst 2015

Algoritmer og datastrukturer Løsningsforslag

Matriser. Kapittel 4. Definisjoner og notasjon

Kap 2: Løkker og lister

På tide med et nytt spill! I dag skal vi lage tre på rad, hvor spillerne etter tur merker ruter med X eller O inntil en av spillerne får tre på rad.

INF2820 Datalingvistikk V Gang 2.3 Jan Tore Lønning

Transkript:

og dynamisk Matrisemultiplikasjomultiplikasjon programmering Matrise- Åsmund Eldhuset og Dette er to ganske like teknikker for å lage algoritmer De kan brukes på svært mange tilsynelatende forskjellige problemer Kan gjøre eksponentiell kjøretid om til polynomisk kjøretid Vi vil nå "finne opp" memoisering og mens vi løser et problem asmunde *at* stud.ntnu.no folk.ntnu.no/asmunde/algdat/dp.ppt Problem: Regn ut nummer n Matematisk definisjon: f(0) = 0 f(1) = 1 f(n) = f(n 1) + f(n 2) for n > 1 Hvert tall unntatt de to første er altså summen av de to foregående: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55... Dette ser lett ut å programmere. Vi fyrer løs med følgende Python-kode: def f(n): elif n == 1: return 1 return f(n - 1) + f(n - 2) Bortkasting av arbeid Dessverre blir kjøretiden helt forferdelig! For n < 20 går det bra Utregning av f(25) tar et halvt sekund f(30) tar fem sekunder f(35) tar et minutt f(42) vil vare ut forelesningen f(50) vil ta et år Når n øker med 1, dobles nesten kjøretiden Hva skyldes dette? La oss ta en kikk på hva funksjonen gjør f(6) f(5) f(4) f(3) f(2) f(1) f(1) f(0) f(2) f(1) f(0) f(3) f(2) f(1) f(1) f(0) f(4) f(3) f(2) f(1) f(1) f(0) f(2) f(1) f(0) 1

med dictionary Løsning: Når funksjonen har regnet ut et, registrerer vi resultatet Når funksjonen blir bedt om å regne ut et, sjekker vi først om vi allerede har regnet det ut Har vi allerede regnet det ut, returnerer vi resultatet vi har Ellers starter vi utregningen på vanlig måte Kan bruke enten dictionary eller array Array er raskere, men du må vite størrelsen på forhånd def f(n, dict): elif n == 1: return 1 elif dict.has_key(n): return dict[n] result = f(n 1, dict) + f(n 2, dict) dict[n] = result return result def fib(n): dict = {} return f(n, dict) med array Dynamisk programmering def f(n, array): elif n == 1: return 1 elif array[n]!= -1: return array[n] result = f(n 1, array) \ + f(n 2, array) array[n] = result return result Vi ser at utregningen av hvert tall bare avhenger av tidligere tall Derfor kan vi faktisk fylle ut arrayet vårt direkte Går raskere enn memoisering pga. ingen rekursjon def fib(n): array = [-1] * (n + 1) return f(n, array) Dynamisk programmering plassoptimalisering def f(n): array = [-1] * (n + 1) array[0] = 0 array[1] = 1 for i in xrange(2, n + 1): array[i] = array[i 1] + array[i 2] return array[n] Vi ser at utregningen av hvert tall bare avhenger av de to forrige tallene Dermed klarer vi oss med bare å lagre tre tall av gangen sparer minne Merk at dette bare er nyttig hvis man kun er interessert i det siste tallet, og ikke vil beholde tallene underveis 2

plassoptimalisering Rekursiv problemformulering def f(n): previous = 0 current = 1 for i in xrange(n 1): next = previous + current previous = current current = next return current Det gjelder å formulere løsningen av et problem som en kombinasjon av løsninger av mindre utgaver av samme problem Eksempel: nummer n er summen av nummer n -1og nummer n -2 De mindre utgavene kalles delproblemer (LIS) LIS optimal substruktur Problem: Gitt en rekke med n reelle tall a 1, a 2, a 3,..., a n, finn den største delrekken slik at tallene står i stigende rekkefølge Eksempel: [1, 2, 9, 3, 8, 5, 7] Fungerer en grådig algoritme? Nei, fordi delproblemene ikke er uavhengige. Velger man å ta med et bestemt tall, har man lagt begrensninger på hvilke tall man kan få med seg senere Vi observerer at en LIS for en bestemt liste består av en LIS for en mindre del av listen Eksempel: [1, 2, 9, 3, 8, 5, 7] LIS'en er [1, 2, 3, 5, 7] [1, 2, 3, 5] er en LIS for [1, 2, 9, 3, 8, 5] [1, 2, 3] er en LIS for [1, 2, 9, 3] [1, 2] er en LIS for [1, 2] [1] er en LIS for [1] Konklusjon: en optimal løsning av problemet består av optimale løsninger av delproblemer LIS rekursiv definisjon LIS rekursiv definisjon La oss se på problemet med å finne en LIS som slutter med tall nummer i, og la oss kalle lengden av dette for L(i) Da vil hovedproblemet vårt være å finne det beste av L(1), L(2), L(3),..., L(n) Hvordan regner vi ut L(i)? Vi finner lengste delsekvensen som ikke inneholder tall større enn tall nummer i Altså: vi må finne den j som er slik at j < i, a j a i, og L(j) er størst mulig L(i) er da lik den største L(j) pluss 1 L(i) = max(l(j) : 0 j < i a j a i ) + 1 Hvis alle tallene på plasser til venstre for i er mindre enn a i, er L(i) = 1. Dette kan bygges inn i definisjonen over, eller så kan man innføre et ekstra element a 0 = - og definere at L(0) = 0 3

LIS tabellutfylling LIS sporing [1, 2, 9, 3, 8, 5, 7] [0, -, -, -, -, -, -, -] [0, 1, -, -, -, -, -, -] [0, 1, 2, -, -, -, -, -] [0, 1, 2, 3, -, -, -, -] [0, 1, 2, 3, 3, -, -, -] [0, 1, 2, 3, 3, 4, -, -] [0, 1, 2, 3, 3, 4, 4, -] [0, 1, 2, 3, 3, 4, 4, 5] Nå har vi funnet lengden på LIS'en, men det hadde jo vært kjekt å finne selve LIS'en også... I et array P registrerer vi hvilket delproblem hvert delproblem benyttet a: [1, 2, 9, 3, 8, 5, 7] L: [0, 1, 2, 3, 3, 4, 4, 5] P: [-, 0, 1, 2, 2, 4, 4, 6] def LIS(a): n = len(a) a.insert(0, -1) # A "fake" element to make #the real elements start at index 1 L = [0] * (n + 1) P = [-1] * (n + 1) for i in range(1, n + 1): bestindex = 0 for j in range(1, i): if a[j] <= a[i] and \ L[j] > L[bestIndex]: bestindex = j L[i] = L[bestIndex] + 1 P[i] = bestindex return L, P Koden på forrige lysbilde returnerer både lengde-arrayet L og forgjenger-arrayet P Det er ikke sikkert at LIS'en slutter med det siste tallet, så vi må gå gjennom L for å finne den største lengden Når vi har gjort det, kan vi spore oss tilbake gjennom P for å finne selve LIS'en Dette kan gjøres iterativt eller rekursivt def LIS_result(a, L, P): bestindex = 1 for i in xrange(2, len(l)): if L[i] > L[bestIndex]: bestindex = i # Use make_lis_recursively # or make_lis_iteratively # (from the next slide) return make_lis(a, P, bestindex) # Example of use a = [1, 2, 9, 3, 8, 5, 7, 1] L, P = LIS(a) print LIS_result(a, L, P) # Should print [1, 2, 3, 5, 7] def make_lis_recursively(a, P, i): if i == 0: return [] b = make_lis_recursively(a, P, P[i]) b.append(a[i]) return b def make_lis_iteratively(a, P, i): b = [] while i!= 0: b.append(a[i]) i = P[i] b.reverse() return b 4

Betingelse 1: Optimal substruktur Betingelse 1.5: Uavhengige delproblemer Ofte har vi en situasjon hvor vi ønsker å finne den best mulige løsningen av et problem Skal vi kunne bruke, må det være slik at problemet har en optimal substruktur en optimal løsning av hele problemet består av optimale løsninger av delproblemer Når vi løser et delproblem, må vi som regel se gjennom alle de mindre delproblemene og velge det beste Løsningen av ett delproblem må ikke legge begrensninger på hvordan andre delproblemer kan løses delproblemer må ikke ha felles ressurser som kan spises opp (Anti)eksempel: simple path Man skulle tro at en LSP fra a til b må bestå av en LSP fra a til x og fra x til b men det er ikke sikkert, for hvis disse LSP'ene overlapper, kan de ikke kombineres til en lovlig LSP fra a til b. Vi har altså ikke optimal substruktur, og det skyldes at vi ikke har uavhengige delproblemer fordi en LSP fra a til x, risikerer vi å "bruke opp" noder som LSP fra x til b trenger. Betingelse 2: Overlappende delproblemer (Betingelse 3: Antall delproblemer) Det burde muligens heller hete "gjentatte delproblemer" Løsningen av to forskjellige delproblemer kan involvere løsning av ett eller flere felles delproblemer Derfor blir rekursjon uten memoisering ekstremt ineffektivt fordi det samme arbeidet gjøres gang på gang Dersom delproblemene ikke overlapper, kan en splitt-og-hersk-algoritme brukes Det er bare vits i hvis antallet delproblemer er "lite" nok til at vi kan lagre alle svarene i minnet og får løst alle delproblemene raskt nok Ting å være oppmerksom på fungerer som regel bare på problemer der ting må komme i en bestemt rekkefølge, eller hvor du står fritt til å plukke en rekkefølge selv (slik som Floyd- Warshall gjør) Selv om tilsynelatende gir polynomisk kjøretid, kan det fremdeles være eksponentielt i inputstørrelsen! (f.eks. myntutdeling eller 0/1 knapsack) Eksempel: Regne ut A 1 A 2 A 3 A 4 A 5 Dimensjonene må passe (a b, b c, c d, d e, e f) er ikke kommutativt (du kan ikke bytte om på rekkefølgen) Men det er assosiativt (du kan velge i hvilken rekkefølge du utfører multiplikasjonene) Vi kan kontrollere multiplikasjonsrekkefølgen ved å sette på parenteser: A 1 A 2 A 3 A 4 A 5 = A 1 ((A 2 A 3 )(A 4 A 5 )) = ((A 1 A 2 )A 3 )( A 4 A 5 ) = A 1 (A 2 (A 3 (A 4 A 5 ))) =... 5

Multiplikasjon av to matriser med dimensjoner a b og b c gir abc skalarmultiplikasjoner (multiplikasjoner av tall) Forskjellige parentessettinger fører som regel til forskjellig totalt antall skalarmultiplikasjoner Hvilken parentessetting er best? Fullt parentessatt uttrykk: enten en enkelt matrise eller et produkt av to utrykk som er fullt parentessatt Alle måter å multiplisere sammen matriser på kan skrives fullt parentessatt Den beste måten må derfor være et fullt parentessatt uttrykk altså et produkt av to andre FPU, som selv må være optimale (LCS) Den ytterste multiplikasjonen kan settes på en av følgende måter: A 1 (A 2 A 3 A 4 A 5 ) (A 1 A 2 )(A 3 A 4 A 5 ) (A 1 A 2 A 3 )(A 4 A 5 ) (A 1 A 2 A 3 A 4 )A 5 For hver av disse mulighetene må vi (rekursivt) undersøke matrisegruppene vi får og finne optimal parentessetting for dem Vi har Θ(n 2 ) forskjellige matrisegrupper, så antallet delproblemer er overkommelig Finne lengste tegnsekvens som er felles for to strenger Eksempel: A = "abqaeehgpcydkpklz" B = "rwazxbaertctdz" Vi definerer LCS(i, j) til å være LCS'en til A[0:i] og B[0:j] (LCS) Dersom siste tegn i A[0:i] og B[0:j] er like, er disse en del av LCS(i, j); da trenger vi bare å finne LCS(i 1, j 1) og legge til 1 Hvis ikke, kan ikke begge de siste tegnene være med i LCS(i, j) Da er LCS(i, j) enten lik LCS(i 1, j) eller LCS(i, j 1) Vi får da en tabell over LCS(i, j) på m x n, hvor m er lengden til A og n er lengden til B Begge utnytter optimal substruktur Grådig Lokalt optimale valg er globalt optimale; optimal løsning avgjøres ut fra hva som virker best der og da Løses som regel ovenfra og ned; man tar et valg og ender opp med et mindre delproblem Optimal løsning avgjøres ved å se på optimale løsninger av delproblemer Løses nedenfra og opp; man løser større og større delproblemer ut i fra de mindre delløsningene 6

Sammendrag: krav for Rekursiv struktur Optimal substruktur Overlappende delproblemer (hvis ikke: splitt og hersk-algoritme) Du vet ikke hvilket delproblem du skal benytte før du har løst alle delproblemene (hvis ikke: grådig algoritme) 7