Föreläsning 9 Innehåll Mer om rekursion söndra-och-härska-algoritmer dynamisk programmering backtracking Orientering om versionshantering med git Söndra och härska Divide and conquer teknik för att konstruera rekursiva algoritmer. Avser rekursiva algoritmer som gör minst två rekursiva anrop i varje upplaga. Problemet delas upp i två eller flera mindre som löses rekursivt (söndra). Lösningarna kombineras till en lösning till det ursprungliga problemet (härska). 2 87 34 12 56 10 2 34 87 10 12 56 2 10 12 34 56 87 Datavetenskap (LTH) Föreläsning 9 HT 2018 1 / 28 Söndra och härska Datavetenskap (LTH) Föreläsning 9 HT 2018 2 / 28 Fibonaccitalen Sortering av element i en vektor: Dela vektorn i två lika stora halvor Sortera (rekursivt) första halvan a[0... n/2] Sortera (rekursivt) andra halvan a[n/2 + 1... n 1] Slå samman de båda sorterade delvektorerna så att hela vektorn a[0... n 1] blir sorterad Det är i härska-steget som själva algoritmkonstruktionen ligger. Söndra-stegen är bara rekursiva anrop. Sorteringsmetoden, Mergesort, blir mycket snabb när steg 3 utförs på ett effektivt sätt. Vi återkommer till denna metod senare i kursen. Leonardo Pisano Fibonacci Born: 1170 in (probably) Pisa (now in Italy) Died: ca 1250 in (possibly) Pisa (now in Italy) A certain man put a pair of rabbits in a place surrounded on all sides by a wall. How many pairs of rabbits can be produced from that pair in a year if it is supposed that every month each pair begets a new pair which from the second month on becomes productive? Datavetenskap (LTH) Föreläsning 9 HT 2018 3 / 28 Datavetenskap (LTH) Föreläsning 9 HT 2018 4 / 28
Fibonaccitalen Talföljd där varje tal är summan av de två föregående. Definition Fn = Fn 1 + Fn 2 för n 2 F0 = 0 F1 = 1 return n; else { return fib(n - 1) + fib(n - 2); Diskutera Fibonaccitalen rekursiv lösning return n; else { return fib(n - 1) + fib(n - 2); Är detta en bra metod? Hur många anrop av metoden fib krävs för att beräkna andra fibonaccitalet? Tredje? Fjärde? Datavetenskap (LTH) Föreläsning 9 HT 2018 5 / 28 Fibonaccitalen effektivitet Datavetenskap (LTH) Föreläsning 9 HT 2018 6 / 28 Fibonaccitalen iterativ lösning Samma Fibonaccital kommer att beräknas många gånger. Ineffektivt! Det blir exponentiell tidskomplexitet, dvs O(2 n ) för att beräkna fib(n). Studera t ex ett anrop fib(4): fib(3) fib(4) I detta fall blir en iterativ lösning effektivare (linjär). return n; else { long nbr1 = 0; long nbr2 = 1; long res = 0; for (int i = 2; i <= n; i++) { res = nbr1 + nbr2; nbr1 = nbr2; nbr2 = res; return res; Datavetenskap (LTH) Föreläsning 9 HT 2018 7 / 28 Datavetenskap (LTH) Föreläsning 9 HT 2018 8 / 28
Fibonaccitalen effektivare rekursiv algoritm Vi har sett att den iterativa lösningen är mycket effektivare än den rekursiva algoritm vi startade med. Vi ska nu använda Fibonaccitalen för att visa hur man kan skriva en effektiv rekursiv metod med hjälp av dynamisk programmering. Dynamisk programmering (R. Bellman, 1953) går ut på att man rekursivt bryter ned problemet i subproblem. Lösningarna till subproblemen sparas för att sedan hämtas när de behövs. Exempel: Första gången vi behöver beräknas det rekursivt på vanligt sätt. Resultatet sparas i en tabell och nästa gång behövs hämtas resultatet direkt istället för att beräknas på nytt. Dynamisk programmering Dynamisk programmering innebär att man i en tabell håller reda på vilka instanser av problemet man redan löst. När man behöver lösningen till en viss instans kontrollerar man först i tabellen om den redan beräknats. I så fall hämtas lösningen där, d.v.s. man gör inget rekursivt anrop. Om lösningen inte finns i tabellen gör man ett rekursivt anrop och sätter sedan in den beräknade lösningen i tabellen. fib(4) 0 1 1 2-1 -1-1 -1-1 fib(3) 0 1 2 3 4 5 6 7... Datavetenskap (LTH) Föreläsning 9 HT 2018 9 / 28 Dynamisk programmering - Fibonaccitalen long[] table = new long[n + 1]; // skapa en tabell Arrays.fill(table, -1); return fib(n, table); private static long fib(int n, long[] table) { if (table[n] == -1 ) { table[n] = n; else { table[n] = fib(n - 1, table) + fib(n - 2, table); return table[n]; Datavetenskap (LTH) Föreläsning 9 HT 2018 10 / 28 Dynamisk programmering - Fibonaccitalen Dynamisk programmering för Fibonaccitalen har linjär tidskomplexitet. Varje tal beräknas en gång. fib(4) fib(3) Datavetenskap (LTH) Föreläsning 9 HT 2018 11 / 28 Datavetenskap (LTH) Föreläsning 9 HT 2018 12 / 28
Fibonaccitalen alternativ, rekursiv lösning Notera: F (n) alltid bygger på seriens två föregående tal. Om vi returnerar ett par av heltal (vektor) behöver varje värde bara beräknas en gång: private static long[] recfib(int n) { // returnera [F(n-1), F(n)] long[] result = { 0, n ; return result; else { long[] prev = recfib(n - 1); long[] result = {prev[1], prev[0] + prev[1]; return result; return recfib(n)[1]; Ingen generell ersättning för dynamisk programmering, men praktiskt just här, då det bara är den föregående dellösningen som behövs. Datavetenskap (LTH) Föreläsning 9 HT 2018 13 / 28 Åtta damer på ett schackbräde Backtracking Allmän strategi för att lösa vissa typer av problem, där vi kan bryta ner problemet i ett antal steg. Exempel: Idé: Hitta den kortaste vägen genom en labyrint Placera ut åtta damer på ett schackbräde Lös ett Sudoku-pussel Pröva en hypotes (ta ett steg i labyrinten, placera ut en dam, sätt ut en Sudoku-siffra,...) Ofta praktiskt att notera detta i något slags datastruktur Undersök rekursivt lösningarna som återstår Basfall: lyckad eller misslyckad lösning Datavetenskap (LTH) Föreläsning 9 HT 2018 14 / 28 Åttadamersproblemet algoritm I schack hotar en dam en pjäs på samma rad, kolonn, eller diagonal. Kan man placera ut åtta sådana damer på ett schackbräde (8 8), utan att någon av dem hotar någon annan? Placera första damen så tidigt som möjligt på rad 1, andra damen så tidigt som möjligt på rad 2... Om damen på en viss rad inte kan placeras, gå tillbaka till föregående rad och hitta en ny plats (åt höger) för damen på denna rad. Om sådan plats finns fortsätt att placera damer på efterföljande rader, annars backa till föregående rad. När damen på rad 8 har placerats har vi funnit en lösning. Datavetenskap (LTH) Föreläsning 9 HT 2018 15 / 28 Datavetenskap (LTH) Föreläsning 9 HT 2018 16 / 28
Åttadamersproblemet: mitt i en lösning, fem damer återstår Pseudokod för att lösa åttadamersproblemet Pröva att placera ut en dam på rad 3, på var och en av de åtta positionerna, i tur och ordning. För varje position vi prövar: Kolla om det gick att göra utan att den nya damen hotar någon befintlig dam. Om det gick bra, gör ett nytt rekursivt anrop för att placera ut de återstående fyra damerna. Pseudokod för att hitta en lösning: boolean solve(int row) om (row == 8) annars for col = 0 to 7 om en dam kunde placeras på row, col på brädet if solve(row + 1) annars tag bort damen från row, col på brädet return false Anropas med solve(0). Datavetenskap (LTH) Föreläsning 9 HT 2018 17 / 28 Rekursiv metod för att lösa åttadamersproblemet Datavetenskap (LTH) Föreläsning 9 HT 2018 18 / 28 Pseudokod för backtracking public boolean solve(int row) { if (row == 8) { ; for (int col = 0; col < 8; col++) { Queen q = new Queen(row, col); if (board.tryadd(q)) { if (solve(row + 1)) { ; board.remove(q); return false; Pseudokod för allmän backtracking som kontrollerar om det finns minst en lösning: boolean solve(v) if v is a solution for each promising choice c make choice c if (solve(v with c)) unmake choice c return false Datavetenskap (LTH) Föreläsning 9 HT 2018 19 / 28 Datavetenskap (LTH) Föreläsning 9 HT 2018 20 / 28
Åttadamersproblemet: en lösning (av flera) Pseudokod för att lösa åttadamersproblemet Pseudokod för att skriva ut alla lösningar och räkna dem: int solve(int row) if (row == 8) return 1 solutions = 0 for col = 0 to 7 if (board.tryaddqueen(row, col)) solutions += solve(row + 1) board.removequeen(row, col) return solutions Hur många lösningar finns det? Anropas med solve(0). Koden kan generaliseras till att hantera n damer på ett n n-bräde. Datavetenskap (LTH) Föreläsning 9 HT 2018 21 / 28 Rekursionsträd för fyra damer Datavetenskap (LTH) Föreläsning 9 HT 2018 22 / 28 När ska rekursion användas? Vissa av de exempel vi sett kan lika enkelt lösas icke-rekursivt. Ex: beräkning av n! Ibland leder rekursion till väldigt ineffektiva algoritmer. Ex: fibonaccitalen (om man inte tänker sig för) Divide-and-conquer möjliggör effektiva algoritmer Effektivt för mergesort (kommer senare)......men ineffektivt för fibonaccitalen Rekursion bör användas då Det är svårt att uttrycka lösningen icke-rekursivt. Ex: algoritmer som manipulerar datastrukturer som träd och grafer. Det finns en icke-rekursiv lösning, men den rekursiva är effektivare. Ex: sortering Det finns icke-rekursiv lösning och en lika effektiv rekursiv lösning som är enklare (att förstå, implementera... ). Ex: binärsökning, behandla elementen i en lista i omvänd ordning Datavetenskap (LTH) Föreläsning 9 HT 2018 23 / 28 Datavetenskap (LTH) Föreläsning 9 HT 2018 24 / 28
git git: ett verktyg, byggt för ett sätt att arbeta git: A completely ignorant, childish person with no manners. (www.urbandictionary.com) Ett versionshanteringssystem: Koden i en gemensam databas (gitlab.com) Varje användare har en egen lokal kopia Integrerat i Eclipse Vi kan skicka lokala förändringar till databasen (commit+push) Vi kan hämta förändringar från databasen till den lokala kopian (pull) Många fler finesser... Datavetenskap (LTH) Föreläsning 9 HT 2018 26 / 28 Datavetenskap (LTH) Föreläsning 9 HT 2018 25 / 28 Detaljerade instruktioner för git Exempel på vad du ska kunna Detaljerade instruktioner för hur du använder git, Eclipse och gitlab ihop finner du i filmform på kursens Moodle-sida. Instruktionerna utgår från att du och dina projektkollegor redan skapat varsitt konto på gitlab.com. Förklara begreppet söndra-och-härska i samband med rekursion. Resonera om tidskomplexitet för rekursiva algoritmer. Använda dynamisk programmering för att eliminera onödiga rekursiva anrop. Använda backtracking för att lösa vissa problem (typiskt av kombinatorisk karaktär). Vid behov, om du vill: använda git med Eclipse för att samarbeta i programmeringsprojekt. Datavetenskap (LTH) Föreläsning 9 HT 2018 27 / 28 Datavetenskap (LTH) Föreläsning 9 HT 2018 28 / 28