Rekursion och induktion för algoritmkonstruktion

Relevanta dokument
Rekursion och induktion för algoritmkonstruktion

Rekursion och induktion för algoritmkonstruktion

Algoritmanalys. Inledning. Informationsteknologi Malin Källén, Tom Smedsaas 1 september 2016

Medan ni väntar. 2. Skriv metoden. 3. Skriv metoden. Naturligtvis rekursivt och utan användning av Javas standardmetoder.

Föreläsning 7 Innehåll. Rekursion. Rekursiv problemlösning. Rekursiv problemlösning Mönster för rekursiv algoritm. Rekursion. Rekursivt tänkande:

SCB :-0. Uno Holmer, Chalmers, höger 2 Ex. Induktiv definition av lista. // Basfall

Algoritmanalys. Genomsnittligen behövs n/2 jämförelser vilket är proportionellt mot n, vi säger att vi har en O(n) algoritm.

Introduktion till algoritmer - Lektion 4 Matematikgymnasiet, Läsåret Lektion 4

Föreläsning 11: Rekursion

Föreläsning 6 Innehåll. Rekursion. Rekursiv problemlösning Mönster för rekursiv algoritm. Rekursiv problemlösning. Rekursion. Rekursivt tänkande:

Föreläsning 1, vecka 7: Rekursion

Metodanrop - primitiva typer. Föreläsning 4. Metodanrop - referenstyper. Metodanrop - primitiva typer

Föreläsning 13. Rekursion

Rekursion. Koffman & Wolfgang kapitel 5

Exempel: Förel Rekursion III Nr 14. Uno Holmer, Chalmers,

Det finns en referensbok (Java) hos tentavakten som du får gå fram och läsa men inte ta tillbaka till bänken.

public static void mystery(int n) { if (n > 0){ mystery(n-1); System.out.print(n * 4); mystery(n-1); } }

Lite om felhantering och Exceptions Mer om variabler och parametrar Fält (eng array) och klassen ArrayList.

Att deklarera och att använda variabler. Föreläsning 10. Synlighetsregler (2) Synlighetsregler (1)

FÖRELÄSNING 2, TDDC74, VT2018 BEGREPP PROBLEMLÖSNING MED HJÄLP AV FALLANALYS PROBLEMLÖSNING MED HJÄLP AV REKURSION

Tentamen i Programmering

Föreläsning 9 Innehåll. Söndra och härska. Fibonaccitalen. Söndra och härska. Divide and conquer teknik för att konstruera rekursiva algoritmer.

Sökning och sortering

Anmälningskod: Lägg uppgifterna i ordning. Skriv uppgiftsnummer (gäller B-delen) och din kod överst i högra hörnet på alla papper

Anmälningskod: Lägg uppgifterna i ordning. Skriv uppgiftsnummer (gäller B-delen) och din kod överst i högra hörnet på alla papper

Procedurer och villkor. Rekursiva procedurer. Exempel: n-fakultet

Procedurer och villkor

Instuderingsfrågor, del D

Algoritmer och datastrukturer H I HÅKAN S T R Ö M B E R G N I C K L A S B R A N D E F E L T

6 Rekursion. 6.1 Rekursionens fyra principer. 6.2 Några vanliga användningsområden för rekursion. Problem löses genom:

TENTAMEN PROGRAMMERINGSMETODIK MOMENT 2 - JAVA, 4P

Föreläsning 9 Innehåll. Söndra och härska. Fibonaccitalen. Söndra och härska. Divide and conquer teknik för att konstruera rekursiva algoritmer.

Föreläsning 5. Rekursion

Objektorienterad programmering i Java I. Uppgifter: 2 Beräknad tid: 5-8 timmar (OBS! Endast ett labbtillfälle) Att läsa: kapitel 5 6

Tentamen i Grundläggande Programvaruutveckling, TDA548

Föreläsning 3-4 Innehåll

DD1361 Programmeringsparadigm. Carina Edlund

Objektorienterad programmering

Rekursion. Att tänka rekursivt Att programmera rekursivt i Java Exempel. Programmeringsmetodik -Java 254

Tommy Färnqvist, IDA, Linköpings universitet. 2 Rekursion i C Implementation av rekursion Svansrekursion En till övning...

Objektorienterad programmering Föreläsning 8. Copyright Mahmud Al Hakim Agenda (halvdag)

Föreläsning 8 Innehåll

PROGRAMMERING-Java Omtentamina

Objektorienterad programmering E. Back to Basics. En annan version av printtable. Ett enkelt exempel. Föreläsning 10

Föreläsning 5&6 LOGISKA VARIABLER; IMPLEMENTERA KLASSER; MER ALGORITMER

732G Linköpings universitet 732G11. Johan Jernlås. Översikt. Repetition. Muddy. Funktioner / metoder. Punktnotation. Evalueringsordning

C++ Funktioner 1. int summa( int a, int b) //funktionshuvud { return a+b; //funktionskropp } Värmdö Gymnasium Programmering B ++ Datainstitutionen

Lösning av några vanliga rekurrensekvationer

Tentamen OOP

Magnus Nielsen, IDA, Linköpings universitet

if (n==null) { return null; } else { return new Node(n.data, copy(n.next));

Del A (obligatorisk för alla)

TDDC77 Objektorienterad Programmering

Anmälningskod: Lägg uppgifterna i ordning. Skriv uppgiftsnummer (gäller B-delen) och din kod överst i högra hörnet på alla papper

Föreläsning 3-4 Innehåll. Diskutera. Metod. Programexempel med metod

Föreläsning 8 SLUMPTAL, SIMULERING + INTRODUKTION TILL VEKTORER

Tentamen i Introduktion till programmering

Objektorienterad programmering. Fält som funktionsresultat. Mer om fält: att uppdatera ett parameterfält. Kontrast: Parametrar av primitiv typ

Uppgift 1 (grundläggande konstruktioner)

Programmeringsmetodik DV1 Programkonstruktion 1. Moment 4 Om rekursion. PK1&PM1 HT-06 moment 4 Sida 1 Uppdaterad

Datatyper och kontrollstrukturer. Skansholm: Kapitel 2) De åtta primitiva typerna. Typ Innehåll Defaultvärde Storlek

Föreläsning 12. Söndra och härska

1 Uppgift 1. a) Skapar ett Company-objekt med hjälp av den överlagrade konstruktorn. Du kan själv välja värden på instansvariablerna.

Programkonstruktion och Datastrukturer

Tentamen, Algoritmer och datastrukturer

Anmälningskod: Lägg uppgifterna i ordning. Skriv uppgiftsnummer (gäller B-delen) och din kod överst i högra hörnet på alla papper

Outline. I Vi kan lätt göra samma sak för fyra variabler... I Hur gör vi för 400 inlästa värden? I Ofta behöver man flera likadana variabler

JAVAUTVECKLING LEKTION 4

Användarhandledning Version 1.2

Datastrukturer och algoritmer. Föreläsning 4 Test, Stack och Kö

Programexempel: tärningsspel. Programexempel: tärningsspel Kasta tärning tills etta. Klassen Die Specifikation. Slumptalsgenerator Klassen Random

Objektorienterad Programmering (TDDC77)

Övningshäfte 2: Induktion och rekursion

BEGREPP HITTILLS FÖRELÄSNING 2 SAMMANSATTA UTTRYCK - SCHEME DATORSPRÅK

F11 - Rekursion. ID1004 Objektorienterad programmering Fredrik Kilander

Lägg uppgifterna i ordning. Skriv uppgiftsnummer och din kod överst i högra hörnet på alla papper.

Summera godtyckligt antal tal. Programkonstruktion. Moment 4 Om rekursion. Fullständigt resonemang för summeringen. Analys av summeringsproblemet

Funktionens deklaration

Tentamen i Algoritmer & Datastrukturer i Java

Chapter 4: Writing Classes/ Att skriva egna klasser.

Programmering för språkteknologer I, VT2012. Rum

Föreläsning 3. Iteration while-satsen

732G Linköpings universitet 732G11. Johan Jernlås. Översikt. Repetition. Felsökning. Datatyper. Referenstyper. Metoder / funktioner

Algoritmer, datastrukturer och komplexitet

Några svar till TDDC70/91 Datastrukturer och algoritmer

Lösningsförslag till exempeltenta 2

Programmeringsteknik med C och Matlab

Iteration while-satsen

Föreläsning 10 OM DELMÅLSKONTROLLEN ; VARIABLERS SYNLIGHET STRING OCH STRINGBUILDER

Dagens program. Programmeringsteknik och Matlab. Objektorienterad programmering. Vad är vitsen med att ha både metoder och data i objekten?

Tommy Färnqvist, IDA, Linköpings universitet

Hej Då, Karel! Programmering. Vårt första Javaprogram. hh.se/db2004. Java. Grundtyper, variabler och arrayer

Dekomposition och dynamisk programmering

Algoritmer, datastrukturer och komplexitet

Föreläsning 4. Kö Implementerad med array Implementerad med länkad lista Djup kontra bredd Bredden först mha kö

BINÄRA TRÄD. (X = pekarvärdet NULL): struct int_bt_node *pivot, *ny; X X X 12 X X 12 X X -3 X X

Programmera i C Varför programmera i C när det finns språk som Simula och Pascal??

Föreläsning 2. Täcker material från lektion 1, 2, 3 och 4:

Repetition i Python 3. Exemplen fac. Exemplen fac motivering. Exemplen fac i Python

Transkript:

Informationsteknologi Tom Smedsaas, Malin Källén 20 mars 2016 Rekursion och induktion för algoritmkonstruktion Att lösa ett problem rekursivt innebär att man uttrycker lösningen i termer av samma typ av problem som dock måste vara i någon mening enklare. Man delar alltså upp problemet i ett eller flera delproblem (av samma typ), löser dessa (på samma sätt) och kombinerar sedan lösningarna av delproblemen till en lösning av ursprungsproblemet. Vi skall demonstrera hur det går till genom ett antal exempel. I många av exemplen finns det enkla iterativa algoritmer men vårt fokus är nu att öva rekursivt tänkande! Exempel 1: Beräkning av fakultet Iterativ definition: n! = n(n 1)(n 2)... 2 1 0! = 1 Iterativ beräkning: public static int fac(int n) { int p = 1; for (; n>1; n--) p = p*n; return p; Rekursiv definition: Rekursiv beräkning: n! = { 1 om n = 0 n(n 1)! om n > 0 public static int fac(int n) { if (n == 0) return 1; else return n*fac(n-1); Förutom i det så kallade basfallet (dvs när n = 0) så anropar metoden fac alltså sig själv med ett argument som är 1 mindre den egna parametern. Antag att vi anropar med fac(3). Java kommer då behöva beräkna 3*fac(2). Då behöver man först beräkna fac(2). Detta görs genom uttrycket 2*fac(1) som, först, måste beräkna 1*fac(0). Vid anropet fac(0) hamnar vi på ett basfall och returnerar 1 vilket gör att uttrycket 1*1 kan beräknas osv. Det hela kan illustreras på vidstående sätt. fac(3): 3*fac(2) 2*fac(1) 1*fac(0) 1*1 2*1 3*2 6 1

Det finns alltså, som mest, fyra stycken fac-uppväckningar igång samtidigt, var och en med sitt eget värde på n. Observera att det allid måste finnas minst ett basfall och att en beräkning alltid måste bottna i ett basfall. Om man anropar metoden ovan med ett negativt argument kommer man inte att nå basfallet (i alla fall inte på något vettigt sätt). Exempel 2: Beräkning av x n - första version Vi skall beräkna x n, n heltal 0, med upprepade multiplikationer. Iterativ definition: Iterativ beräkning: x n = x x x x... x public static double power(double x, int n) { double p = 1.; for (int i = 1; i<=n; i++) { p = p*x; return p; Rekursiv definition: x n = { 1 om n = 0 xx n 1 om n > 0 Övning 1: Implementera ovanstående rekursiva algoritm i en metod public static double power(double x, int n) Övning 2: Skriv en rekursiv metod (dvs utan iteration) public static int multiply(int m, int n) som beräknar produkten m*n utan att använda multiplikation. Produkten skall alltså beräknas med additioner. Du kan förutsätta att m och n är icke-negativa. Övning 3: Skriv en rekursiv metod (dvs utan iteration) public static int divide(int t, int n) som beräknar hur många gånger n går i t (dvs en heltalsdivision) utan att använda division 2

eller multiplikation. Du kan förutsätta att t och n är större än 0. Övning 4: Skriv en rekursiv metod public static double harmonic(int n) som beräknar den harmoniska summan h(n) = 1 + 1/2 + 1/3 + + 1/n Övning 5: Skriv en rekursiv metod public static int largest(int[] a, int i) som returnerar det största värdet av elementen a[0], a[1],... a[i]. I ovanstående exempel blir de rekursiva metoderna varken enklare eller effektivare än den iterativa men rekursiva resonemang är ett ändå kraftfullt sätt att lösa problem och hitta effektiva algoritmer. Att uttrycka sig rekursivt är ofta naturligt i matematiken. Deriveringsreglerna ( derivatan av en summa är summan av derivatorna ) är ett exempel på detta. Inte desto mindre brukar rekursion betraktas som svårt när man börjar med det i programmering. Orsaken till detta är säkert att man i de flesta fall lärt sig programmera med iterationer. Frågor att besvara vid konstruktion av en rekursiv algoritm: 1. Hur kan jag dela upp ursprungsproblemet i mindre problem av samma slag? 2. Hur kombinerar jag lösningarna till delproblemen till en lösning på ursprungsproblemet? 3. Vilka rekursionsterminerande fall är lämpliga? Kommer man alltid till ett sådant oberoende av indata? Detta är mycket besläktat med induktionsbevis: Vi antar att vi kan lösa problemet för ett eller flera mindre problem. Sedan visar man hur man med hjälp av dessa lösningar kan lösa ursprungsproblemet. Precis som i induktionsbevis så måste man ha minst ett basfall. Exempel 3: Skriv ut en sträng med n tecken i omvänd ordning Hur definiera problemet i termer av sig självt? Induktionsantagande: Antag att vi kan lösa problemet för de n 1 första tecknen i strängen. Basfall: Att skriva ut ett tecken i omvänd ordning. Trivialt. Eftersom det sista tecknet skall vara först måste vi börja med att skriva det. 3

Algoritm i pseudokod: Java-kod: Skriv strängen i omvänd ordning om längden är 1 eller mindre så skriv strängen annars skriv det sista tecknet skriv alla utom det sista i omvänd ordning public static void reverse(string s) { if (s.length()<=1) { System.out.println(s); else { System.out.print(s.charAt(s.length()-1)); reverse(s.substring(0, s.length()-1)); Övning 6: Skriv en metod public static String reverse(string s) som returnerar en sträng där tecknen kommer i omvänd ordning mot tecknen i s. Använd antagandet att vi kan lösa problemet för ut de n 1 sista tecknen i strängen. Det finns ofta möjlighet att välja de mindre problemen på olika sätt. I ovanstående exempel hade vi två naturliga val men vilket vi valde spelar egentligen ingen roll. När vi diskuterar algoritmanalys kommer kommer vi att presentera exempel på hur man kan hitta en bättre (effektivare) algoritmer genom att välja andra delproblem. Exempel 4: Trava brickor En mängd med n brickor av olika storlek ligger travade på en plats (A) i storleksordning med den största underst. Problemet går ut på att flytta hela traven till en annan plats (C) under iakttagande av följande regler: 1. Endast en bricka får flyttas per gång. 2. En större bricka får aldrig läggas på en mindre. Till hjälp har man ytterligare en plats (B) som får användas för mellanlagring. Induktionsantagande: Vi kan lösa problemet med n 1 brickor. Basfall: Flytta en bricka. Trivialt. Induktionssteg: Vi gör på följande sätt 1. Flytta de n 1 översta brickorna till B. 2. Flytta den kvarvarande från A till C. 3. Flytta de n 1 brickorna på B till C. Problemet löses således rekursivt genom att lösa två problem av storlek n 1. Övning 7: Implementera en metod bricklek(char from, char to, char help, int n) som skriver ut hur flyttningen av n brickor från from till to med hjälp av help skall göras. En förflyttning från A till B följd av en förflyttning från C till pinne B ska skrivas ut såhär: A -> B 4

C -> B Observera radbrytningarna! Exempel 5: Fibonaccitalen Fibonaccitalen F n definieras enligt 0 om n = 0, F n = 1 om n = 1, F n 1 + F n 2 om n > 1. Definitionen ovan kan användas för att skriva en metod som returnerar det n:te Fibonaccitalet: public static long fib(int n) { if ( n==0 ) return 0; else if ( n==1 ) return 1; else return fib(n-1) + fib(n-2); Detta är en korrekt metod men den är, som vi ska se när vi talar om algoritmanalys, hopplöst ineffektiv för stora värden på n. Exempel 6: Växlingsproblemet På hur många sätt kan man växla a kronor i 100, 50, 10, 5 och 1-mynt (sedlar)? (t.ex. 90 kronor i 50+4*10, 9*10, 8*10 + 10*1 etc.) Formulera en lösning av problemet i termer av sig självt. Viktigt att rekursionsfallet/fallen löser ett i någon mening mindre problem: Ordna myntsorterna i någon ordning. Dela in växlingsförsöken i två grupper: de som inte använder något mynt av första sorten de som använder första sortens mynt Problemets lösning kan nu formuleras: Antalet sätt att växla a kronor vid användande av n olika sorters mynt är 1. antalet sätt att växla a kronor vid användande av alla utom den första sortens mynt (n 1 sorter) plus 2. antalet sätt att växla a d kronor användande alla n sorters mynt (d = 1:a myntsortens valör) Delproblem 1 är mindre än ursprungsproblemet eftersom det använder färre myntslag och delproblem 2 är mindre eftersom det växlar en mindre summa. Antag att myntsorterna representeras i en array change med värden {1, 5, 10, 50, 100 på index 1, 2, 3, 4, 5. 5

Ger grundprogram: public static int count( int a, int n ) { return count( a, n-1 ) + count( a-change[n], n ); Vilka specialfall behövs för att undvika oändlig rekursion? a kan bli = 0 vilket innebär ett lyckat försök (räkna det) a kan bli < 0 vilket innebär ett misslyckat försök (räkna ej) n kan bli = 0 vilket innebär ett misslyckat försök (räkna ej) Slutlig version: public static int count( int a, int n) { if ( a == 0 ) return 1; else if (a < 0) (n == 0) return 0; else return count(a,n-1) + count(a-change[n],n); Övning 8: Testkör programmet ovan. Låt programmet läsa in belopp som skall växlas. Övning 9*: Modifiera programmet så att det även skriver ut de växlingar som görs. Övning 10: Skriv en metod static String reverseint(int x) som returnerar talet x i decimalform men med siffrorna i omvänd ordning dvs så att reverseint(12345) returnerar "54321" Övning 11: Skriv en rekursiv metod static String longtostring(long x, int b) som returnerar en sträng med de siffror som representerar talet x i basen b. Lös först problemet under förutsättning att b 10. Skriv sedan en metod som fungerar för b 16. Övning 12: Skriv en metod public static String reversenumbers(scanner scan) som läser en följd av hela tal från Scanner-objektet scan och returnerar en sträng med talen talen i omvänd ordning det vill säga med det sist inlästa talet först. Metoden skall inte använda någon array, lista eller sträng och skall bara deklarera en enda variabel. Inläsningen av tal skall avbrytas när det inte längre går att läsa ett heltal. I retursträngen skall varje tal föregås av exakt ett blanktecken. Exempel: Anropet reversenumbers(new Scanner("11 23 31 49 56 611")) skall returnera strängen " 611 56 49 31 23 11" 6

Skriv sedan en metod public static void reversenumbers() som läser tal från standard input och skriver ut talen i omvänd ordning. Talen skrivs på en enda rad med radbyte efter sista talet. Övning 13: En stack är en mekanism som lagrar ett antal element. Till stacken hör operationerna lagra nytt element respektive ta bort ett lagrat element. Operationerna kallas traditionellt push och pop. Stackens väsentliga egenskap är att uttagsordningen är omvänd mot inläggningsordningen dvs pop tar ut det som senast lagrades med push. Av detta skäl kallas stacken även LIFO för Last In First Out. (Av motsvarande skäl kallas en kö för FIFO för First In First Out.) Stackmekanismen används bl a för att implementera rekursion. Denna övning går ut på att implementera en stack som lagrar heltal. Stacken ska dock ha en speciell begränsning: det är förbjudet att lägga ett större tal på ett mindre. Se specifikation av stacken! Observera att stacken inte får ha några andra metoder än de här angivna! Tips: En arraylista är en lämplig struktur för att implementera en stack. Se till att tiden för metoderna push och pop är oberoende av stackens storlek! Ladda ner filen StackPlay.java och implementera metoderna move och getnumberofmoves enligt dokumentationen i koden. Exempel på utskrift från main-metoden: Start state From: [3, 2, 1] To: [] Help: [] Number of moves: 0 End state From: [] To: [3, 2, 1] Help: [] Number of moves: 7 7