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



Relevanta dokument
Övningshäfte 2: Induktion och rekursion

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

Algoritmer, datastrukturer och komplexitet

Övningshäfte 1: Induktion, rekursion och summor

Magnus Nielsen, IDA, Linköpings universitet

Algoritmer, datastrukturer och komplexitet

Rekursion och induktion för algoritmkonstruktion

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

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

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

Tommy Färnqvist, IDA, Linköpings universitet

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 7 Innehåll. Rekursion. Rekursiv problemlösning. Rekursiv problemlösning Mönster för rekursiv algoritm. Rekursion. Rekursivt tänkande:

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

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

Rekursion och induktion för algoritmkonstruktion

Explorativ övning 5 MATEMATISK INDUKTION

Programkonstruktion och Datastrukturer

Föreläsning 9: NP-fullständighet

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 6 Innehåll. Rekursion. Rekursiv problemlösning Mönster för rekursiv algoritm. Rekursiv problemlösning. Rekursion. Rekursivt tänkande:

Uppsala Universitet Matematiska Institutionen Thomas Erlandsson

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

Uppgift 1 ( Betyg 3 uppgift )

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

Lösningsförslag till Tentamen i 5B1118 Diskret matematik 5p 14 augusti, 2002

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

Föreläsning 8 Innehåll

Föreläsning 5: Summor (forts) och induktionsbevis

Föreläsning 5. Rekursion

Explorativ övning 5 MATEMATISK INDUKTION

Föreläsning 5 Innehåll

Föreläsning 13. Dynamisk programmering

Föreläsning 13. Rekursion

Algoritmer, datastrukturer och komplexitet

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

Rekursion och induktion för algoritmkonstruktion

Föreläsning 5: Dynamisk programmering

Dugga Datastrukturer (DAT036)

1. Inledning, som visar att man inte skall tro på allt man ser. Betrakta denna följd av tal, där varje tal är dubbelt så stort som närmast föregående

Material till kursen SF1679, Diskret matematik: Lite om kedjebråk. 0. Inledning

Föreläsning 5 Innehåll. Val av algoritm och datastruktur. Analys av algoritmer. Tidsåtgång och problemets storlek

Övningar. MATEMATISKA INSTITUTIONEN STOCKHOLMS UNIVERSITET Avd. Matematik. Linjär algebra 2. Senast korrigerad:

Tentamensskrivning i Diskret Matematik för CINTE och CMETE, SF1610, onsdagen den 20 augusti 2014, kl

Lösningar för tenta i TMV200 Diskret matematik kl. 14:00 18: Svar: Ja, det gäller, vilket kan visas på flera sätt (se nedan).

Lösningar för tenta i TMV200 Diskret matematik kl. 14:00 18:00

Föreläsning 1: Dekomposition, giriga algoritmer och dynamisk programmering

String [] argv. Dagens Agenda. Mer om arrayer. Mer om arrayer forts. String [] argv. argv är variabelnamnet. Arrayer och Strängar fortsättning

Algoritmer, datastrukturer och komplexitet

Programmering för Språkteknologer II. Innehåll. Associativa datastrukturer. Associativa datastrukturer. Binär sökning.

Dekomposition och dynamisk programmering

Introduktion till programmering D0009E. Föreläsning 5: Fruktbara funktioner

Kappa 2014, lösningsförslag på problem 5

Kimmo Eriksson 12 december Att losa uppgifter av karaktaren \Bevisa att..." uppfattas av manga studenter

Algoritmer, datastrukturer och komplexitet

Sökning och sortering

Instruktioner - Datortentamen TDDD73 Funktionell och imperativ programmering i Python TDDE24 Funktionell och imperativ programmering del 2

Algoritmer och effektivitet. Föreläsning 5 Innehåll. Analys av algoritmer. Analys av algoritmer Tidskomplexitet. Algoritmer och effektivitet

Tommy Färnqvist, IDA, Linköpings universitet

Föreläsning 6: Induktion

Datastrukturer och algoritmer. Föreläsning 15 Inför tentamen

Tentamen Datastrukturer för D2 DAT 035

Algoritmer, datastrukturer och komplexitet

Lösningsförslag till tentamensskrivning i SF1610 Diskret Matematik för CINTE 30 maj 2018, kl

TDDI16 Datastrukturer och algoritmer. Algoritmanalys

Rekursion. Rekursiv lösningsstrategi. Algoritmkonstruktion. Exempelproblem Hitta största elementet i en sekvens v i där i 1... n.

Lösning till tentamensskrivning i Diskret Matematik för CINTE, CL2 och Media 1, SF1610 och 5B1118, onsdagen den 17 augusti 2011, kl

Mer om reella tal och kontinuitet

Explorativ övning 4 ÄNDLIGT OCH OÄNDLIGT. Övning A

Textsträngar från/till skärm eller fil

Introduktion till programmering SMD180. Föreläsning 5: Fruktbara funktioner

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

Matematik 5 Kap 2 Diskret matematik II

18 juni 2007, 240 minuter Inga hjälpmedel, förutom skrivmateriel. Betygsgränser: 15p. för Godkänd, 24p. för Väl Godkänd (av maximalt 36p.

Föreläsning 5: Grafer Del 1

Läsanvisning till Discrete matematics av Norman Biggs - 5B1118 Diskret matematik

Bakgrund. Bakgrund. Bakgrund. Håkan Jonsson Institutionen för systemteknik Luleå tekniska universitet Luleå, Sverige

Grundläggande logik och modellteori

Lösningar till utvalda uppgifter i kapitel 4

Programkonstruktion och Datastrukturer, lektion 7

Logik och Jämförelser. Styrsatser: Villkorssatsen if och repetitonssatsen for. Scriptfiler. Kommentarer. Tillämpningar: Ett enkelt filter.

Övningar. c) Om någon vektor i R n kan skrivas som linjär kombination av v 1,..., v m på precis ett sätt så. m = n.

Tillämpad Programmering (ID1218) :00-13:00

Tentamen i TDDC75 Diskreta strukturer

Programmering för språkteknologer II, HT2014. Rum

(N) och mängden av heltal (Z); objekten i en mängd behöver dock inte vara tal. De objekt som ingår i en mängd kallas för mängdens element.

Föreläsning 4 Datastrukturer (DAT037)

Kontinuitet och gränsvärden

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

Tentamen: Programutveckling ht 2015

Lösning av några vanliga rekurrensekvationer

Tentamen Datastrukturer D DAT 035/INN960

TDDC74 Lab 02 Listor, sammansatta strukturer

Instruktioner - Datortentamen TDDD73 Funktionell och imperativ programmering i Python

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

Datastrukturer och algoritmer

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 6 Jonas Lindgren, Institutionen för Datavetenskap, LiU

Tentamen TEN1 HI

Transkript:

Introduktion till algoritmer - Lektion 4 Matematikgymnasiet, Läsåret 014-015 Denna lektion ska vi studera rekursion. Lektion 4 Principen om induktion Principen om induktion är ett vanligt sätt att bevisa ett påstående P (n) om ett heltal n. Vi kan t.ex ha påståendet P (n) : summan av de n första positiva heltalen är n(n+1). Man kan bevisa detta påstående i två steg: Visa att P (1) är sant. Visa att om P (n) är sant, så är även P (n + 1) sant. Dessa två steg bevisar tillsammans att påståendet P (n) är sant för alla heltal n 1 och kallas alltså för ett induktionsbevis. Exempel 1. Visa att summan av de n första positiva heltalen är n(n+1). Bevis. Påståendet är uppenbarligen sant för n = 1, eftersom n(n+1) = 1 = 1. Antag nu att påståendet är sant för något heltal k, dvs att summan av de k första heltalen är k(k+1). Om vi adderar k + 1 till båda led får vi att summan av de k + 1 första heltalen är k(k+1 + k + 1 = k(k+1)+(k+1) = k +3k+ = (k+1)(k+), dvs formeln är sann även för k + 1. Enligt principen om induktion är då påståendet samt för alla heltal n 1. Att principen faktiskt är sann går att bevisa beroende på inom vilket axiomsystem man valt att konstruera heltalen. Vi kan i alla fall bevisa att den följer av den lite mer intuitiva välordningsprincipen att varje icke-tom delmängd av positiva heltal har ett minsta element. Sats 1. Principen om induktion är korrekt. Bevis. Antag att för ett visst påstående P (n) gäller att P (1) är sant och P (n) P (n + 1). Låt S = {n Z + : P (n) är falskt }. Vi vill visa att S =, d.v.s att P (n) faktiskt är sann för alla positiva heltal. Antag att så intet är fallet. Då finns enligt välordningsprincipen ett minsta element a S. Vi vet att a > 1, eftersom P (1) är sann. Då är a 1 1. Vi får nu två fall. Om a 1 S var a inte det minsta elementet, så detta kan inte vara fallet. Alltså är a 1 inte ett element i S. Detta innebär med andra ord att P (a 1) är sann. Enligt antagandet har vi P (a 1) P (a). Ur detta följer alltså att a inte heller är i S. Antagandet att S hade ett minsta element är alltså falskt. Den enda möjligheten som är kvar är att S faktiskt är tom, alltså att P (n) är sann för alla positiva heltal. Rekursiva definitioner Induktion är nära förknippat med s.k. rekursiva definitioner. En rekursiv definition är ett sätt att definera någon storhet i termer av sig själv. Om vi betecknar summan av de första n positiva heltalen med S n kan vi t.ex. uttrycka S n som S n 1 + n vi uttrycker summan av n heltal med hjälp av summan av n 1 heltal. Detta självreferrerande är alltså vad som brukar kalls för rekursion. Den vakna läsaren noterade antagligen att S n = S n 1 + n dock inte duger som en definition av S n. När vi ska definera S 0 får vi nämligen S 0 = S 1 + 0. Summan av de 1 första heltalen är dock svårt att definera rent semantiskt. Vad som fattades i vår beskrivning är det så kallade basfallet

Introduktion till algoritmer - Lektion 4 Matematikgymnasiet, Läsåret 014-015 ett värde på S n som vi bestämmer oss för att definera direkt. I vårt fall är det lämpligt att använda oss av basfallet S 0 = 0. Ett antal basfall samt ett rekursivt samband utgör alltså en rekursiv definition. En annan vanlig rekursiv definition som de flesta är bekanta med är Fibonaccitalen. De brukar defineras med basfallen F 0 = 0 och F 1 = 1 samt rekursionssambandet F n+ = F n+1 + F n. Övning 1. Visa att den rekursiva definitionen av Fibonaccitalen är entydiga, d.v.s basfallen tillsammans med rekursionssambandet ger tillräcklig med information för att unikt bestämma värdet på F n för alla n 0. Gör detta med hjälp av induktionsprincipen. Rekursiva definitioner uppkommer ofta både i matematiken och datavetenskapen, ofta som svar till en uppgift. I matematikproblem försöker man ofta finna ett så kallat slutet uttryck från den rekursiva definitionen. Detta är alltså ett uttryck för storheten som inte är rekursiv. Vi bevisade i föregående avsnitt att S n har det slutna uttrycket n(n+1). Exempel. Definera en sekvens a i utifrån den rekursiva definitionen a 0 = 0 a n+1 = a n + 1 Finn ett slutet uttryck för rekursionen. Bevis. Vi börjar med att beräkna sekvensens värde för några termer i början: a 0 = 0 a 1 = 0 + 1 = 1 a = + 1 = 3 a 3 = 3 + 1 = 7 a 4 = 7 + 1 = 15 Vi kan här börja misstänka att a n har det slutna uttrycket n 1. Vi kan bevisa detta med hjälp av induktion: a 0 = 0 1 = 1 1 = 0, så påståendet är sant för 0. Vidare, om a n = n 1 är a n+1 = (a n ) 1) = ( n 1) + 1 = n+1 + 1 = n+1 1. Enligt principen om induktion gäller alltså a n = n 1 för alla n 0. Observera att principen om induktion kan användas även om vi inte börjar med just P (1). Det är inte särskilt svårt att visa, med föregående bevis i hand: Övning. Visa att om P (n) är sant där n är vilket heltal som helst, samt P (k) P (k + 1) för alla k n så är P (k) sant för alla k n. Tips: påståenden om heltalen n, n+1, n+,... osv. kan också uttryckas som påståenden om heltalen 1,, 3,... Rekursion och programmering Rekursiva definitioner är en av de viktigaste principer inom programmering. En väldigt stor klass av programmeringsproblem löser man nämligen genom att reducera problemet till samma problem, fast av mindre storlek. Om du ska beräkna summan av de 10 första heltalen kan du först beräkna summan av de 9 första heltalen, och sedan addera 10. Då har du reducerat problemet till att

Introduktion till algoritmer - Lektion 4 Matematikgymnasiet, Läsåret 014-015 beräkna summan av de 9 första heltalen istället. Detta kan du i sin tur reducera till att beräkna summan av de 8 första heltalen, osv, ända tills du ska summera de 0 första heltalen. Men detta basfall är enkelt svaret är helt enkelt 0. När vi programmerar ligger rekursionen rent konkret i en funktion som anropar sig själv. Den rekursiva definitionen av de n första positiva heltalen, n!, kan defineras rekursivt som: { 1 om n = 0 n! = n (n 1)! annars I C++ kan vi implementera denna rekursiva definition så här: 1 int fakultet(int n){ if(n == 0){ 3 return 1; 4 } 5 return n * fakultet(n - 1); 6 } Fibonaccitalen har en lite mer komplicerad definition: 0 om n = 0 F n = 1 om n = 1 F n 1 + F n annars Men att vi har två basfall gör saken inte mycket svårare: 1 int fib(int n){ if(n == 0){ 3 return 0; 4 } 5 if(n == 1){ 6 return 1; 7 } 8 return fib(n - 1) + fib(n - ); 9 } I programmeringsproblem är rekursionerna oftast inte lika enkla att komma på som i våra små exempel. Basfallen brukar däremot vara tämligen enkla att komma på själv. Exempel 3. Från Programmeringsolympiadens onlinekval, 010 kommer uppgiften Gourmeten (Kattis-id: gourmeten). Den franska gourmeten Frank är en väl respekterad gourmet; hans yrke är att gå runt till olika restauranger, äta av deras mat och ge sitt omdöme om restaurangen. Men han bär på en hemlighet: han är egentligen bara intresserad av att äta så mycket som möjligt och i vilken ordning som helst! Frank förstår dock att en äkta gourmet inte kan äta hur som helst, t.ex. börja med sin dessert och avsluta med en sallad. Därför ber han om din hjälp att ta fram en lista med alla olika sätt att ordna maträtterna under en bjudning, så han kan välja ut den ordning som är finast. På bjudningen har Frank M minuter på sig att äta. Restaurangen bjuder på N olika rätter som han kan äta hur många portioner som helst av. Varje rätt tar ett visst givet antal minuter att äta. Frank vill äta kontinuerligt under alla de M minuter han har på sig, och han vill hinna äta klart alla rätter han påbörjat. Han vill aldrig påbörja en ny rätt innan han ätit färdigt den förra. Din uppgift är att skriva ett program som räknar ut på hur många olika sätt han kan lägga upp middagen, givet dessa restriktioner.

Introduktion till algoritmer - Lektion 4 Matematikgymnasiet, Läsåret 014-015 Indata: Ett heltal M, antalet minuter. Ett heltal N, antalet rätter. N heltal T i, tiden det tar att äta rätt nummer i. Utdata: Antalet möjliga sätt Frank kan äta under exakt M minuter. Bevis. För att göra det enkelt för oss låter vi a(k) vara antalet sätt Frank kan äta om vi har k minuter kvar. Vi noterar först att om Frank har exakt 0 minuter kan han äta på ett sätt, nämligen genom att inte äta något alls. Man kan tvista om att det egentligen borde vara 0 sätt (är att äta inget verkligen att äta något?), men man brukar traditionellt betrakta det tomma sättet som ett giltigt sätt (på samma sätt som den tomma mängden är en delmängd i alla mängder). Om han har mindre än 0 minuter kvar har han redan överskridit sin tid, så då är antalet sätt han kan äta istället 0. Detta innebär att a(0) = 1 och a(k) = 0, när k < 0. Detta visar sig faktiskt tillräckligt som basfall. För alla tider större än 0 minuter kan vi istället formulera svaret rekursivt. Antag att vi har k > 0 minuter kvar att äta på. Om den sista rätten vi äter var rätt i som tar t i minuter, så kan vi äta på totalt a(k t i ) sätt. Vi kan alltså gå igenom varje rätt i och summera a(k t i ), eftersom någon rätt måste vara den sista rätten vi åt. Detta ger oss rekurionssambandet a(k) = N i=1 a(k t i). Tillsammans med våra basfall kan vi nu beräkna a(m). Vi implementerar detta i kod såhär: 1 // indatan int N, M; 3 vector<int> t; 4 5 int gourmeten(int minuter){ 6 if(minuter < 0) return 0; 7 if(minuter == 0) return 1; 8 int svar = 0; 9 for(int i = 0; i < N; ++i){ 10 svar += gourmeten(minuter - t[i]); 11 } 1 return svar; 13 } Dekomposition En speciell sorts rekursion är så kallad dekomposition (eng. divide and conquer). Här bryter vi ned problemet vi ska lösa i mindre, separata delar. Sedan löser vi delarna för sig och kombinerar svaren på dessa till svaret för det stora problemet. Exempel 4. Vi har en vektor med n stycken heltal, som har elementen a[0],..., a[n 1]. Vi undrar nu vilket element som är det största av dessa. Bevis. Vi löser ett mer generellt problem: vad är det största av elementen a[l],..., a[r 1], för två heltal 0 l < r n. Om vi betecknar detta med f(l, r) så är svaret till det ursprungliga problemet f(0, n). Om r = l+1 har vi endast ett element, nämligen a[l]. Detta är vårt basfall i rekursionen: f(l, l+1) = a[l].

Introduktion till algoritmer - Lektion 4 Matematikgymnasiet, Läsåret 014-015 Annars kan vi låta m = (l + r)/ och dela upp vår array i a[l],...a[m 1] samt a[m],..., a[r]. Det största värdet av dessa två delar är ju f(l, m) respektive f(m, r). Det största värdet av hela intervallet måste ju ligga i någon av dessa två delar. Därmed är f(l, r) = max {f(l, m), f(m, r)}. Detta ger oss den rekursiva definitionen: f(l, l + 1) = a[l] f(l, r) = max {f(l, (l + r)/ ), f( (l + r)/, r)} som vi implementerar i C++ som 1 int findmax(vector<int>& a, int l, int r){ if(r == l + 1){ 3 return a[l]; 4 } 5 return max(findmax(a, l, (l + r)/), findmax(a, (l + r)/, r)); 6 } Övning 3. Bevisa att den rekursiva definitionen är korrekt med hjälp av induktion. Övning 4. Visa att tidskomplexiteten för f indm ax-funktionen är O(r l) med induktion. Merge sort En känd algoritm som använder sig utav dekomposition är sorteringsalgoritmen merge sort. Algoritmen använder sig av samma princip som vår findmax-funktion, i att den delar upp indatan i delar som den rekursivt sorterar, men att kombinera dessa delsvar är lite mer komplicerat. Antag att vi har två sorterade vektorer a och b. Vi vill kombinera dessa till en sorterad vektor c. Vi inser att det lägsta elementet i c, dvs c[0] måste vara det lägsta av de det lägsta elementet i a, dvs a[0], samt det lägsta elementet i b, alltså b[0]. Vi har alltså c[0] = min(a[0], b[0]). Om a däremot är tom sätter vi c[0] = b[0] och vice versa. Självfallet kan vi nu upprepa denna procedur med de kvarvarande elementen, dvs vi tar bort det lägsta elementet från antingen a eller b och fortsätter med detta tills båda vektorer är tomma. Algoritmen är alltså: 1 vector<int> sortvector(vector<int>& a, int l, int r){ int m = (l + r)/; 3 sortvector(a, l, m); // sort left half 4 sortvector(a, m, r); // sort right half 5 6 //combine the answers 7 vector<int> answer; 8 int x = l; 9 int y = m; 10 while(x < m y < r){ 11 if(x == m (y < r && a[y] < a[x])){ 1 answer.push_back(a[y]); 13 y++; 14 } else { 15 answer.push_back(a[x]); 16 x++; 17 }

Introduktion till algoritmer - Lektion 4 Matematikgymnasiet, Läsåret 014-015 18 } 19 return answer; 0 1 } Övning 5. Skriv ner funktionens beteende för vektorn (3, 6, 1, 5, 4, 0,, 7). Övning 6. Visa att sortv ector har tidskomplexitetn O(n log n), där n är antalet element i vektorn. Det finns två sätt att göra detta på: dels via induktion (ganska svårt), och dels genom att rita upp ett diagram över de olika anropen och hur lång tid kombinationssteget tar i varje anrop. Det som utmärker dekomposition från vanlig rekursion är just att man delar upp sitt problem i två eller flera oberoende delar, som man sedan kan kombinera för att lösa det ursprungliga problemet. Vi kommer se fler tillämpningar av dekomposition senare i kursen.

Introduktion till algoritmer - Lektion 4 Matematikgymnasiet, Läsåret 014-015 Uppgifter Övning 7. Bevisa med hjälp av induktion att n i i! = (n + 1)! 1 i=1 Övning 8. Bevisa med hjälp av induktion att ett schackbräde av storlek n n med en ruta borttagen kan täckas med hjälp av triominos (L-formade brickor formade av tre 1 1-rutor). Övning 9. Bevisa med hjälp av induktion att varje komplett, riktad graf har en hamiltonsk väg (en väg som besöker alla hörn exakt en gång). Övning 10. Bakåtinduktion Ett komplement till principen om induktion är bakåtinduktion. Givet ett påstående P (n) bevisar vi att det är sant för alla n 1 genom följande steg: Visa att P (1) är sann Visa att P (n) P (n 1) för n > 1 (observera minustecknet!) Visa att P (n) P (n ) där n > n Med hjälp av villkor 1 och 3 bevisar man att P (n) är sant för godtyckligt stora n, och använder sedan villkor för att backa beviset till mindre n. Bevisa att om de tre villkoren är sanna så är P (n) sant för alla n 1. Tips: välordningsprincipen.