Föreläsning 8 Innehåll Orientering om samarbete om Eclipse-projekt med git Orientering om konstruktion av användargränssnitt i Android Mer om rekursion söndra-och-härska-algoritmer dynamisk programmering backtracking Datavetenskap (LTH) Föreläsning 8 HT 2018 1 / 31
git git: A completely ignorant, childish person with no manners. (www.urbandictionary.com) Datavetenskap (LTH) Föreläsning 8 HT 2018 2 / 31
git: ett verktyg, byggt för ett sätt att arbeta 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 8 HT 2018 3 / 31
Detaljerade instruktioner för git 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. Datavetenskap (LTH) Föreläsning 8 HT 2018 4 / 31
Applikationer i Android Skrivs i Java, i Android Studio En applikation består av en eller flera aktiviteter (subklasser till Activity) Aktiviteter har vyer grafiska komponenter (subklasser till View) För användargränssnittet används andra klasser än i t.ex. JavaFX Man behöver inte veta så mycket om Android för att komma igång! Om du är särskilt intresserad kan du använda Android istället för JavaFX i Sudoku-inlämningsuppgiften. Datavetenskap (LTH) Föreläsning 8 HT 2018 5 / 31
Applikationer i Android Recept på hur man får igång en applikation i Android: Låt Android Studio skapa en ny, tom applikation (ev. med en lämplig mall) Använd layoutredigeraren för att rita användargränssnittet Inför referenser till View-objekten i applikationen, koppla dem till dina ritade objekt med findviewbyid(r.id.xyz) (där XYZ är ID för resp. objekt) Få din Java-kod att anropas när användaren klickar i användargränssnittet Tillsätt lämplig algoritm Visa resultatet i användargränssnittet Datavetenskap (LTH) Föreläsning 8 HT 2018 6 / 31
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 8 HT 2018 7 / 31
Söndra och härska 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. Datavetenskap (LTH) Föreläsning 8 HT 2018 8 / 31
Fibonaccitalen 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 8 HT 2018 9 / 31
Fibonaccitalen Talföljd där varje tal är summan av de två föregående. Definition F n = F n 1 + F n 2 för n 2 F 0 = 0 F 1 = 1 public static long fib(int n) { if (n <= 1) { return n; else { return fib(n - 1) + fib(n - 2); Datavetenskap (LTH) Föreläsning 8 HT 2018 10 / 31
Diskutera Fibonaccitalen rekursiv lösning public static long fib(int n) { if (n <= 1) { 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 8 HT 2018 11 / 31
Fibonaccitalen effektivitet 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(4) fib(3) fib(2) fib(2) fib(1) fib(1) fib(0) fib(1) fib(0) Datavetenskap (LTH) Föreläsning 8 HT 2018 12 / 31
Fibonaccitalen iterativ lösning I detta fall blir en iterativ lösning effektivare (linjär). public static long fib(int n) { if (n <= 1) { 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 8 HT 2018 13 / 31
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 fib(2) beräknas det rekursivt på vanligt sätt. Resultatet sparas i en tabell och nästa gång fib(2) behövs hämtas resultatet direkt istället för att beräknas på nytt. fib(4) fib(3) fib(2) fib(2) fib(1) fib(1) fib(0) Datavetenskap (LTH) Föreläsning 8 HT 2018 14 / 31
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. 0 1 1 2 0 1 2 3-1 -1-1 -1 4 5 6 7-1... Datavetenskap (LTH) Föreläsning 8 HT 2018 15 / 31
Dynamisk programmering - Fibonaccitalen public static long fib(int n) { long[] table = new long[n + 1]; Arrays.fill(table, -1); return fib(n, table); // skapa en tabell private static long fib(int n, long[] table) { if (table[n] == -1 ) { if (n <= 1) { table[n] = n; else { table[n] = fib(n - 1, table) + fib(n - 2, table); return table[n]; Datavetenskap (LTH) Föreläsning 8 HT 2018 16 / 31
Dynamisk programmering - Fibonaccitalen Dynamisk programmering för Fibonaccitalen har linjär tidskomplexitet. Varje tal beräknas en gång. fib(4) fib(3) fib(2) fib(1) fib(0) Datavetenskap (LTH) Föreläsning 8 HT 2018 17 / 31
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)] if (n <= 1) { long[] result = { 0, n ; return result; else { long[] prev = recfib(n - 1); long[] result = {prev[1], prev[0] + prev[1]; return result; public static long fib(int n) { 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 8 HT 2018 18 / 31
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: Hitta den kortaste vägen genom en labyrint Placera ut åtta damer på ett schackbräde Lös ett Sudoku-pussel Idé: 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 8 HT 2018 19 / 31
Åtta damer på ett schackbräde 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? Datavetenskap (LTH) Föreläsning 8 HT 2018 20 / 31
Åttadamersproblemet algoritm 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 8 HT 2018 21 / 31
Åttadamersproblemet: mitt i en lösning, fem damer återstår 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. Datavetenskap (LTH) Föreläsning 8 HT 2018 22 / 31
Pseudokod för att lösa åttadamersproblemet Pseudokod för att hitta en lösning: boolean solve(int row) om (row == 8) return true annars for col = 0 to 7 om en dam kunde placeras på row, col på brädet if solve(row + 1) return true annars tag bort damen från row, col på brädet return false Anropas med solve(0). Datavetenskap (LTH) Föreläsning 8 HT 2018 23 / 31
Rekursiv metod för att lösa åttadamersproblemet public boolean solve(int row) { if (row == 8) { return true; for (int col = 0; col < 8; col++) { Queen q = new Queen(row, col); if (board.tryadd(q)) { if (solve(row + 1)) { return true; board.remove(q); return false; Datavetenskap (LTH) Föreläsning 8 HT 2018 24 / 31
Pseudokod för backtracking Pseudokod för allmän backtracking som kontrollerar om det finns minst en lösning: boolean solve(v) if v is a solution return true for each promising choice c make choice c if (solve(v with c)) return true unmake choice c return false Datavetenskap (LTH) Föreläsning 8 HT 2018 25 / 31
Åttadamersproblemet: en lösning (av flera) Hur många lösningar finns det? Datavetenskap (LTH) Föreläsning 8 HT 2018 26 / 31
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 Anropas med solve(0). Koden kan generaliseras till att hantera n damer på ett n n-bräde. Datavetenskap (LTH) Föreläsning 8 HT 2018 27 / 31
Rekursionsträd för fyra damer Datavetenskap (LTH) Föreläsning 8 HT 2018 28 / 31
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 8 HT 2018 29 / 31
Exempel på vad du ska kunna 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 8 HT 2018 30 / 31
Datorlaboration 4 Fraktaler Rita fraktala figurer med hjälp av rekursion. Kochs triangel byggs upp av tre linjer, där varje linje (längd, riktning) ersätts med fyra nya linjer: Berg byggs upp av en triangel (tre punkter) som ersätts av fyra nya trianglar: b b a c a c Datavetenskap (LTH) Föreläsning 8 HT 2018 31 / 31