530 likes | 644 Views
Chapter 3. Recursion. Iterative algorithm for n factorial. n factorial n! = 1 if n = 0 n! = n*(n-1)*(n-2)*...*1 if n>0 Iterative algorithm prod = 1; for (x = n; x > 0; x--) prod *= x; return (prod);. Recursive definition for n factorial. recursive definition:
E N D
Chapter 3 Recursion
Iterative algorithm for n factorial • n factorial n! = 1 if n = 0n! = n*(n-1)*(n-2)*...*1 if n>0 • Iterative algorithmprod = 1; for (x = n; x > 0; x--) prod *= x; return(prod);
Recursive definition for n factorial • recursive definition: n! = 1 if n = 0 n! = n * (n-1)! if n > 0
Recursive program for n factorial int fact(int n) { int x, y; if (n == 0) //boundary condition return(1); x = n-1; y = fact(x); return(n*y); } /* end fact */
Stack and function call in C e.g. printf(“%d”, fact(3)) (a)(initially) (b)fact(3) (c)fact(2) (d)fact(1) (e)fact(0) (f)y=fact(0)
(g)y=fact(1) (h)y=fact(2) (i)printf(“%d”, fact(3)) • The stack varies during execution. (from (a) to (i)) • (An asterisk indicates an uninitialized value.)
Common errors • Calling printf(“%d”, fact(-1)) would cause infinite loops • It is invalid to define there must be a boundary condition. • Concise code: int fact(int n) { return( n == 0 ? 1 : n * fact(n-1)); } /* end fact */
Error checking for n < 0 int fact(int n) { int x, y; if (n < 0) { printf(“%s”, “negative parameter in the factorial function”); exit(1); } /* end if */ if (n == 0) return(1); x = n-1; y = fact(x); return(n*y); } /* end fact */
Multiplication of natural numbers a * b = a if b = 1 a * b = a * (b-1) + a if b > 1 int mult(int a, int b) { int c, d, sum; if (b == 1) return(a); c = b-1; d = mult(a, c); sum = d+a; return(sum); } /* end mult */
Concise code: int mult(int a, int b) { return(b == 1 ? a : mult(a, b-1) + a); } /* end mult */ • Invalid definition: a * b = a * (b+1) - a
Fibonacci sequence (1) • 0,1,1,2,3,5,8,13,21,34,... • Leonardo Fibonacci (1170 -1250)用來計算兔子的數量每對每個月可以生產一對兔子出生後, 隔一個月才會生產, 且永不死亡 生產0 1 1 2 3 ... 總數 1 1 2 3 5 8 ... http://www.mcs.surrey.ac.uk/Personal/R.Knott/Fibonacci/fibnat.html
Fibonacci sequence (2) • 0,1,1,2,3,5,8,13,21,34,...
fn = 0 if n = 0 fn = 1 if n = 1 fn = fn-1 + fn-2 if n >= 2 Fibonacci sequence and golden number • 0,1,1,2,3,5,8,13,21,34,... 1 x-1 x
Iterative algorithm for Fibonacci sequence fn = 0 if n = 0 fn = 1 if n = 1 fn = fn-1 + fn-2 if n 2 int fib(int n) { int i, x, logib, hifib; if (n <= 1) return(n); lofib = 0; hifib = 1; for (i = 2; i <= n; i++){ x = lofib; /* hifib, lofib*/ lofib = hifib; hifib = x + lofib; /* hifib = lofib + x */ } /* end for */ return(hifib); }
Recursive algorithm for Fibonacci sequence fn = 0 if n = 0 fn = 1 if n = 1 fn = fn-1 + fn-2 if n 2 int fib(int n) { int x, y; if (n <= 1) return(n); x = fib(n-1); y = fib(n-2); return(x+y); } /* end fib */ • Concise code: int fib(int n) { if (n <= 1) return(n); return (fib(n-1) + fib(n-2)); }
Stack simulation for fib(4)=3 (a) (b) (c) (d) (e) (f) (g) (h) (i) (j)
(k) (l) (m) (n) (o) (p) (q)
f5 f4 f3 f3 f2 f2 f1 f2 f1 f1 f0 f1 f0 f1 f0 The computation tree • Much computation is duplicated. • The iterative algorithm for generating Fibonacci sequence is better.
e.g. 1 4 7 8 10 11 14 Search 6 Binary search • It is used for finding a given element in a sorted sequence stored in an array. binsrch(int a[],int x,int low,int high) { int mid; if (low > high) return(-1); while (high >= low){ mid = (low+high)/2; if (x == a[mid]) return(mid); if (x < a[mid]) high = mid-1; else low = mid+1; } return(-1); /* 未找到 */ }
Recursive binary search int binsrch(int a[], int x, int low, int high) { int mid; if (low > high) return(-1); mid = (low+high) /2; return(x == a[mid] ? mid : x < a[mid] ? binsrch(a, x, low, mid-1) : binsrch(a, x, mid+1, high)); } /* end binsrch */
Recursive chains a(formal parameters) { b(arguments); } /* end a */ b(formal parameters) { a(arguments); } /* end b */ • A recursive function need not call itself directly.
expression term + term | term term factor * factor | factor factor letter | (expresssion) letter A | B | C | D | ... | Z Algebraic expressions e.g A : (A) : A+B : (A+B) : A*B : A*(B+C): (A+B*)C: A+B+C : **
C program for checking expression #include <stdio.h> #include <ctype.h> #define TRUE 1 #define FALSE 0 #define MAXSTRINGSIZE 100 void readstr(char *, int); int expr(char *, int, int *); int term(char *, int, int *); int getsymb(char *, int, int *); int factor(char *, int, int *);
void main() { char str[MAXSTRINGSIZE]; int length, pos; readstr(str, &length); pos = 0; if (expr(str, length, &pos) == TRUE && pos >= length) printf(“%s”, “valid”); else printf(“%s”, “invalid”); /* The condition can fail for one (or both) of two */ /* reasons. If expr(str, length, &pos) == FALSE */ /* then there is no valid expression beginning at */ /* pos. If pos < length, there may be a valid */ /* expression starting at pos but it does not */ /* occupy the entire string. */ } /* end main */
int expr(char str[], int length, int *ppos) { /* look for a term */ if (term(str, length, ppos) == FALSE) return(FALSE); /* We have found a term; look at the */ /* next symbol. */ if (getsymb(str, length, ppos) != ‘+’){ /* We have found the longest expression */ /* (a single term). Reposition pos so */ /* it refers to the last position of */ /* the expression */ (*ppos)--; return(TRUE); } /* end if */ /* At this point, we have found a term */ /* and a plus sign. We must look for */ /* another term. */ return(term(str, length, ppos)); } /* end expr */
int term(char str[], int length, int *ppos) { if (factor(str, length, ppos) == FALSE) return(FALSE); if (getsymb(str, length, ppos) != ‘*’){ (*ppos)--; return(TRUE); } /* end if */ return(factor(str, length, ppos)); } /* end term */ int factor(char str[], int length, int *ppos) { int c; if ((c = getsymb(str, length, ppos)) != ‘(‘) return(isalpha(c)); return(expr(str, length, ppos) && getsymb(str, length, ppos) == ‘)’); } /* end factor */
int getsymb(char str[], int length, int *ppos) { char c; if (*ppos < length) c = str[*ppos]; else c = ‘ ‘; (*ppos)++; return(c); } /* end getsymb */
A B C 1 2 3 4 5 The initial setup of the Towers of Hanoi. The Towers of Hanoi problem • Disks are of different diameters • A larger disk must be put below a smaller disk • Object: to move the disks, one each time, from peg A to peg C, using peg B as auxiliary.
Strategy for moving disks • how to move 3 disks? • how to move n disks? ** **
Recursive program for the Tower of Hanoi problem #include <stdio.h> void towers(int, char, char, char); void main() { int n; scanf(“%d”, &n); towers(n, ‘A’, ‘C’, ‘B’); } /* end main */
void towers(int n, char frompeg, char topeg, char auxpeg) { if ( n == 1){ // If only one disk, make the move and return. printf(“\n%s%c%s%c”, “move disk 1 from peg ”, frompeg, “ to peg “, topeg); return; } /* end if */ /*Move top n-1 disks from A to B, using C as auxiliary*/ towers(n-1, frompeg, auxpeg, topeg); /* move remaining disk from A to C */ printf(“\n%s%d%s%c%s%c”, “move disk “, n, “ from peg “, frompeg, “ to peg “, topeg); /* Move n-1 disk from B to C using A as auxiliary */ towers(n-1, auxpeg, topeg, frompeg); } /* end towers */
T(n) : # of movements with n disks 已知 T(n) = T(n-1) + 1 + T(n-1) = 2T(n-1) + 1 = 2(2T(n-2) + 1) + 1 = 4T(n-2) + 2 + 1 = 8T(n-3) + 4 + 2 + 1 = 2n-1 T(n-(n-1)) + 2n-2 + 2n-3 + … + 1 = 2n-1 T(1) + 2n-2 + 2n-3 + … + 1 = 2n-1 + 2n-2 + … + 1 = 2n - 1 T(1) = 1 boundary condition T(2) = 3 T(3) = 7 Number of movements
1. +A*BC prefix + (A) (*BC) + (A) (BC*) (A)(BC*)+ ABC*+ postfix + prefix prefix * A postfix postfix BC Translation from prefix to postfix infix A+B*C A*B+C A+B*C+D–E*F prefix +A*BC +*ABC -++A*BCD*EF postfix ABC*+ AB*C+ ABC*+D+EF*- • example for translation:
2. - + + A * BCD * EF prefix - ( + + A * BCD)(*EF) -(+(+A * BC)(D))(*EF) -(+(ABC * + )(D))(*EF) -(ABC * + D +)(EF*) ABC * + D + EF * - postfix - prefix prefix + * prefix prefix prefix + DEF postfix postfix prefix A * postfix postfix BC 演算法: **
Recursive program for conversion void convert(char prefix[], char postfix[]) { char opnd1[MAXLENGTH], opnd2[MAXLENGTH]; char post1[MAXLENGTH], post2[MAXLENGTH]; char temp[MAXLENGTH]; char op[1]; int length; int i, j, m, n; if ((length = strlen(prefix)) == 1){ if (isalpha(prefix[0])){ /* The prefix string is a single letter. */ postfix[0] = prefix[0]; postfix[1] = ‘\0’; return; } /* end if */ printf(“\nillegal prefix string”); exit(1); } /* end if */
/* The prefix string is longer than a single */ /* character. Extract the operator and the */ /* two operand lengths. */ op[0] = prefix[0]; op[1] = ‘\0’; substr(prefix, 1, length-1, temp); m = find(temp); substr(prefix, m+1, length-m-1, temp); n = find(temp); if ((op[0] != ‘+’ && op[0] != ‘-’ && op[0] != ‘*’ && op[0] != ‘/’) || (m == 0) || (n == 0) || (m+n+1 != length)){ printf(“\nillegal prefix string”); exit(1); } /* end if */ substr(prefix, 1, m, opnd1); substr(prefix, m+1, n, opnd2); convert(opnd1, post1); convert(opnd2, post2); strcat(post1, post2); strcat(post1, op); substr(post1, 0, length, postfix); } /* end convert */
m n +( )( ) 0 1 m+1 int find(char str[]) /* 找到最後合法的prefix */ { char temp[MAXLENGTH]; int length; int i, j, m, n; if ((length = strlen(str)) == 0) return(0); if (isalpha(str[0]) != 0) /* First character is a letter. */ / That letter is the initial substring. */ return(1); /* otherwise find the first operand */ if (strlen(str) < 2) return(0); substr(str, 1, length-1, temp); // 假設第一個為operator m = find(temp);
if (m == 0 || strlen(str) == m) /* no valid prefix operand or no second operand */ return (0); substr(str, m+1, length-m-1, temp); n = find(temp); if (n == 0) return(0); return(m+n+1); } /* end find */
Action of calling a function in C • Passing arguments • Allocating and initializing local variables • Transferring control to the function (a) Control Control (b) A Series of procedures calling one another.
Storage allocation for a C compiler (1) dynamic allocation: storage for local variables, parameters are allocated when a function is called. (2) A function call is maintained by using a stack.
e.g. int f1(int x) { int i, j; } int f2(float s,float t) { char a, b; f1(4) } int f3() { f2(2.4, 7.5); } for f1 ... local variables ... for f2 parameters ... ... for f3 ... Stack • Some programming languages do not allow • recursive programs, e.g. FORTRAN, COBAL.
Recursive program of factorial int fact(int n) { int x, y; if (n == 0) return(1); x = n-1; y = fact(x); return(n*y); } /* end fact */ Stack simulation struct dataarea{ int param; int x; longint y; short int retaddr; }; struct stack{ int top; struct dataarea item[MAXSTACK]; }; Stack simulation with C
int simfact(int n) { struct dataarea currarea; struct stack s; short int i; long int result; s.top = -1; /* initialize a dummy data area */ currarea.param = 0; currarea.x = 0; currarea.y = 0; currarea.retaddr = 0; /* push the dummy data area onto the stack */ push(&s, &currarea); /* set the parameter and the return address of */ /* the current data area to their proper values. */ currarea.param = n; currarea.retaddr = 1;
start: /* this is the beginning of the simulated */ /* factorial routine. */ if (currarea.param == 0){ /* simulation of return(1); */ result = 1; i = currarea.retaddr; popsub(&s, &currarea); switch(i){ case 1: goto label1; case 2: goto label2; } /* end switch */ } /* end if */ currarea.x = currarea.param –1; /* simulation of recursive call to fact */ push(&s, &currarea); currarea.param = currarea.x; currarea.retaddr = 2; goto start; /* This is the point to which we return */ /* from the recursive call. Set currarea.y */ /* to the returned value. */ currarea.y = result; label2:
/* simulation of return(n*y) */ result = currarea.param * currarea.y; i = currarea.retaddr; popsub(&s, &currarea); switch(i){ case 1: goto label1; case 2: goto label2; } /* end switch */ /* At this point we return to the main routine. */ return(result); } /* end simfact */ label1:
Improving the simulation routine • The old value of n must be used after returning from the recursive call. Thus, the value of n must be kept on the stack. But, x and y need not be stacked. #define MAXSTACK 50 struct stack{ int top; int param[MAXSTACK]; // Only n is kept; }; int simfact(int n) { struct stack s; shortint und; long int result, y; int currparam, x; s.top = -1; currparam = n;
start: /* This is the beginning of the simulated */ /* factorial routine. */ if (currparam == 0){ /* simulation of return(1) */ result = 1; popandtest(&s, &currparam, &und); switch(und){ case FALSE: goto label2; case TRUE : goto label1; } /* end switch */ } /* end if */ /* currparam !=0 */ x = currparam - 1; /* simulation of recursive call to fact */ push(&s, currparam); currparam = x; goto start; /* This is tje point to which we return */ /* from the recursive call. Set */ /* y to the returned value. */ y = result; label2:
/* simulation of return ( n*y); */ result = currparam * y; popandtest(&s, &currparam, &und); switch(und){ case TRUE : goto label1; case FALSE: goto label2; } /* end switch */ /* At this point we return to the main */ /* routine. */ return(result); } /*end simfact*/ label1:
Eliminating unnecessary gotos: struct stack { int top; int param[MAXSTACK]; }; int simfact(int n); { struct stack s; short int und; int x; long int y;
s.top = -1; x = n; /* This is the beginning of */ /* the simulated factorial routine. */ if (x == 0) y = 1; else{ push(&s, x--); goto start; } /* end else */ label1: popandtest(&s, &x, &und); if (und == TRUE) return(y); label2: y *= x; goto label1; } /* end simfact */ start: