Rekursiva funktioner Föreläsning 10 (Weiss kap. 7) Induktion och rekursion Rekursiva funktioner och processer Weiss 7.1-3 (7.4, 7.5.3 utgår) Fibonaccital (7.3.4) Exempel: Balansering av mobil (kod se lab 5) Exempel: Tornen i Hanoi Ex. Räkna personer i en grupp utan global överblick SCB :-0 #? :-o :-? :-o :- :-o :- Denna grupp klarar att räkna sig själv Varje person exekverar följande rekursiva algoritm: 1. Om ingen står till vänster så ropa 1 åt höger 2. Annars, fråga grannen till vänster hur många de är. Om svaret är K så ropa K+1 åt höger.? höger..? :-( :- 1 2 Ex. Räkna personer i en grupp utan global överblick Induktiva definitioner och rekursion SCB :-0 :-)? :-o :-) :- -1? -2? :-o :-) :- höger.. 2 :-o :-) :- 1? :-) :-( :- Ex. Induktiv definition av lista Varje person exekverar följande rekursiva algoritm: 1. Om ingen står till vänster så ropa 1 åt höger 2. Annars, fråga grannen till vänster hur många de är. Om svaret är K så ropa K+1 åt höger. Basfall En lista är antingen (1) tom eller (2) ett element följt av en lista Induktionsfall 3 4 Ex. Summera elementen i en lista - rekursiv process sum( [7,3,6,4,2,9] ) 7 + sum( [3,6,4,2,9] ) 31 7 + (3 + (6 + (4 + (2 + (9 + sum( [] )))))) 7 + (3 + (6 + (4 + (2 + (9 + 0))))) Rekursiv algoritm: 1. Om listan är tom är summan av elementen 0 2. Annars, summera elementen i resten av listan och addera första elementet till resultatet En rekursiv listsummeringsfunktion int sum( Listode l ) { if ( l == null ) return 0; // Basfall else // Rekursionssteg return l.element + sum( l.next ); Funktionen kan summera elementen i en kortare lista. 5 6
Iterativa och rekursiva processer Iterativ process En iterativ process (loop) har konstant minnesbehov (i princip) Rekursiv process En rekursiv process har variabelt minnesbehov oftast används en stack Anropsstack och aktiveringsposter Lokala funktionsvariabler lagras i aktiveringsposter, en per funktionsanrop är en funktion anropas (aktiveras) läggs en aktiveringspost på anropsstacken. Den frigörs vid retur. 7 En iterativ listsummeringsfunktion int sum( Listode l ) { int s = 0; while ( l!= null ) { s = s + l.element; l = l.next; return s; 8 En rekursiv process kräver minne Två exempel i Weiss sum( [7,3,6,4,2,9] ) 7 + sum( [3,6,4,2,9] ) 7 + (3 + sum( [6,4,2,9] )) 7 + (3 + (6 + sum( [4,2,9] ))) 7 + (3 + (6 + (4 + sum( [2,9] )))) 7 + (3 + (6 + (4 + (2+ sum( [9] ))))) 7 + (3 + (6 + (4 + (2 + (9 + sum( [] )))))) 7 + (3 + (6 + (4 + (2 + (9 + 0))))) 7 + (3 + (6 + (4 + (2 + 9))))) 7 + (3 + (6 + (4 + 11)))) 7 + (3 + (6 + 15))) 7 + (3 + 21) 7 + 24 31 Maximalt minnesbehov 9 Summering av tal rekursivt (Fig. 7.1) i1 Utskrift av heltal i valfri bas (Fig. 7.2-4) i 1 ( 1 i1 i) 1 2 X d 1d2d0 d 110 d 2 10 d0 10 siffrigt tal 3 2 1 0 Ex. 7364 710 310 610 410 0 10 Summering av serie 1 // Recursive routine to compute sum of 2 // the first n integers 3 4 int s( int n ) 5 { 6 if ( n == 1 ) 7 return 1; 8 else 9 return s( n - 1 ) + n; 10 Figure 7.1 Recursive evaluation of the sum of the first integers Rekursiv utskrift av heltal i decimal form kvot (/10) 7 3 6 4 rest (%10) Skriv först ut detta tal rekursivt 7 3 6 4 Skriv därefter ut detta ensiffriga tal Basfall: Om talet är ensiffrigt kan det skrivas ut direkt 11 12
Utskrift av heltal i decimal form 1 // Print n in base 10,recursively. 2 // Precondition: n >= 0. 3 public static void printdecimal( long n ) 4 { 5 if ( n >= 10 ) 6 printdecimal( n / 10 ); 7 System.out.print( (char)( 0 + (n % 10))); 8 Figure 7.2 Recursive routine for printing in decimal form Hur exekveras Sum? S( 5 ) Anropsstacken S( 4 ) + 5 1 2 S( 3 ) + 4 3 S( 2 ) + 3 4 5 S( 1 ) + 2 1 13 14 Exempel: En Mobil är antingen 1. en fisk m eller 2. på formen snöre l 1 Förgreningsrekursion l 2 stång snöre Balanserad Är min mobil balanserad? Mobil Mobil m5 m 2 15 m 4 16 Vad väger en mobil? 1. vikt( ) = m m 2. vikt( ) = vikt( M 1 ) + vikt( M 2 ) är är en mobil balanserad? 1. En fisk är alltid balanserad 2. En mobil på formen är balanserad l 1 l 2 M 1 M 2 Denna metod kan räkna ut vikterna hos M 1 och M 2 17 om l1 vikt ( M 1) l2 vikt ( M 2 ) och både M 1 och M 2 är balanserade M 1 M 2 Denna metod kan avgöra om M 1 och M 2 är balanserade 18
Fibonaccitalen 1 1 2 3 5 8 13 21 34 55 89... 0 1 2 3 4 5 6 7 8 9 10... fib(0) 1 fib(1) 1 fib( ) fib( 1) fib( 2) Gyllene snittet a b b a b a = 1 a b 1 fib( ) fib( 1) fib( 2) ( 1) ( 1) b 1 5 lim fib( 1) b 2 fib( ) 19 20 Rekursiv beräkning av Fibonaccital 1 // Compute the th Fibonacci umber. 2 // Bad algorithm. 3 long fib( int n ) 4 { 1 fib ( ) 5 if ( n <= 1 ) fib ( 1) fib ( 2) 6 return 1; 7 else 8 return fib( n - 1 ) + fib( n - 2 ); 9 Figure 7.6 Recursive routine for Fibonacci umbers: A bad idea. 1 1 fib(3) fib(1) fib(2) fib(0) fib(1) Problem med Fib fib(5) fib(4) fib(2) fib(3) fib(0) fib(1) fib(1) fib(2) fib(0) fib(1) - Hur många funktionsanrop genererar anropet fib()? 21 22 Iterativ beräkning av Fibonaccital currentfib previousfib currentfib 1 1 2 3 5 8 13 21 34 55 89... 1 1 2 3 5 8... 12 35 8 + 0 12 35 Skriv ut de minsta Fibonaccitalen iterativt med loop void fibiterprint( int n ) { long previousfib = 0, currentfib = 1; for ( int i = n; i > 0; i-- ) { System.out.println( currentfib ); long nextfib = previousfib + currentfib; previousfib = currentfib; currentfib = nextfib; previousfib Övning: Variabeln nextfib är överflödig, eliminera den! 23 24
Returnera det :te Fibonaccitalet iterativt med loop long fibiter( int n ) { long previousfib = 0, currentfib = 1; for ( int i = n; i > 0; i-- ) { long nextfib = previousfib + currentfib; previousfib = currentfib; currentfib = nextfib; return currentfib; Fibonaccital med iterativ rekursion s.k. svansrekursion void fibiterrekprint(long previousfib,long currentfib,int n) { if ( n == 0) return; else { System.out.println( currentfib ); fibiterrekprint( currentfib, previousfib + currentfib, n - 1 ); void fibprint( int n ) { return fibiterrekprint(0,1,n); Denna typ av rekursion kan av en optimerande kompilator automatiskt översättas till en loop 25 26 Returnera det :te Fibonaccitalet iterativ rekursion long fibiterrek( long previousfib, long currentfib, int n ) { if ( n == 0) return currentfib; else return fibiterrek( currentfib, previousfib + currentfib, n - 1 ); long fib( int n ) { return fibiterrek( 0, 1, n ); 27 Tornen i Hanoi st. ringar av olika diametrar är uppträdda på en pinne Ingen ring ligger ovanpå en mindre ring Problem Flytta alla ringarna från pinne A till pinne B Regler Högst en ring får flyttas åt gången En ring får bara läggas ovanpå en större ring 28 Flytta två ringar från A till C via B Flytta en ring från A till B 29 30
Flytta två ringar från C till B via A Flytta ringar från A till B via C - 1 { Övriga ringar 31 Metoden kan flytta en stapel av höjd -1 på korrekt sätt - 1 32 Implementering Programmet skall skriva ut en arbetsbetsinstruktion enligt följande modell: umber of rings? 3 Do the following moves: A --> B A --> C B --> C A --> B C --> A C --> B A --> B En rekursiv algoritm för Hanois torn from to via 1 void move( char a, char b, char c, int n ) { 2 if ( n > 0 ) { 3 // move n - 1 rings from a to c via b 4 move( a, c, b, n - 1 ); 5 6 System.out.println( a + " --> " + b ); 7 8 // move n - 1 rings from c to b via a 9 move( c, b, a, n - 1 ); 10 11 12 void hanoi( int n ) { 13 move( 'A', 'B', 'C', n ); 14 33 34 Definition: Låt T() vara antalet ringförflyttningar som utförs av anropet move( X, Y, Z, ) för att flytta en stapel av ringar från X till Y via Z. Sats: För alla 0 gäller att T( )2 1 d.v.s. T( ) O( 2 ) Bevis: Induktion över. X Låt P(X) vara påståendet: T( X) 2 1 Visa att P() gäller för varje 0 Tidskomplexiteten hos Hanoi 35 Vi gör induktion över. Bevis av satsen 0 Basfall: Visa att P(0) håller, d.v.s. T( 0) 2 10 Om move anropas med = 0 är villkoret på rad 2 falskt och funktionen terminerar direkt, vilket stämmer eftersom ingenting skall flyttas om vi inte har några ringar alls! 36
Induktionssteg: Visa att P() håller för godtyckligt > 0, d.v.s. T( ) 2 1 ( 0) Induktionsantagande: Antag P(k) är sant för alla k < 1. Först anropas move(a,c,b,-1) (rad 4) Induktionsantagandet gäller för varje k < 1 och speciellt för - 1, d.v.s. T( 1) 2 1 2. Därefter flyttas en ring från A till B (rad 6) 3. Sist anropas move(c,b,a,-1) (rad 9) Av induktionsantagandet följer att detta ger ytterligare 2 1 1 förflyttningar Det totala antalet förflyttningar, T(), blir således 1 22 ( 1) 12 212 1 37