3 -1 chapter 3 recursion. 3 -2 iterative algorithm for n factorial n factorial n! = 1 if n = 0 n! =...

53
3 -1 Chapter 3 Recursion

Upload: lorin-magdalen-terry

Post on 28-Dec-2015

239 views

Category:

Documents


0 download

TRANSCRIPT

3 -1

Chapter 3

Recursion

3 -2

Iterative algorithm for n factorial

n factorial n! = 1 if n = 0n! = n*(n-1)*(n-2)*...*1 if n>0

Iterative algorithm prod = 1;

for (x = n; x > 0; x--) prod *= x; return(prod);

3 -3

Recursive definition for n factorial

recursive definition:

n! = 1 if n = 0n! = n * (n-1)! if n > 0

3 -4

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 */

3 -5

Stack and function call in C

e.g. printf(“%d”, fact(3))

n x y(a)(initially) (b)fact(3) (c)fact(2)

3 *

*

n x y

2

3

*

2

*

*

n x y

1

2

3

*

1

2

*

*

*

n x y

0

1

2

3

*

0

1

2

*

*

*

*

n x y

1

2

3

0

1

2

1

*

*

n x y

(d)fact(1) (e)fact(0) (f)y=fact(0)

3 -6

n x y

(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.)

2

3

1

2

1

*

n x y

3 2

2

n x y

3 -7

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 */

1

)!1(!

n

nn

Common errors

3 -8

Error checking for n < 0int 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 */

3 -9

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 */

3 -10

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

3 -11

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

3 -12

Fibonacci sequence (2) 0,1,1,2,3,5,8,13,21,34,...

3 -13

Fibonacci sequence and golden number

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

numberGolden 2

51lim

1

n

nn f

f

1 x-1

x

2

51

01

1

1

12

x

xx

x

x

3 -14

Iterative algorithm for Fibonacci sequence

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);}

fn = 0 if n = 0 fn = 1 if n = 1

fn = fn-1 + fn-2 if n 2

3 -15

Recursive algorithm for Fibonacci sequence

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));}

fn = 0 if n = 0 fn = 1 if n = 1

fn = fn-1 + fn-2 if n 2

3 -16

Stack simulation for fib(4)=3n

4

x

*

y

*

n

3

4

x

*

*

y

*

*

n

2

3

4

x

*

*

*

y

*

*

*

n

1

2

3

4

x

*

*

*

*

y

*

*

*

*

n

2

3

4

x

1

*

*

y

*

*

*

n

0

2

3

4

x

*

1

*

*

y

*

*

*

*

n

2

3

4

x

1

*

*

y

0

*

*

n

3

4

x

1

*

y

*

*

n

1

3

4

x

*

1

*

y

*

*

*

n

3

4

x

1

*

y

1

*

(a) (b) (c) (d) (e)

(f) (g) (h) (i) (j)

3 -17

n

4

x

2

y

1

n

4

x

2

y

*

n

2

4

x

*

2

y

*

*

n

1

2

4

x

*

*

2

y

*

*

*

n

0

2

4

x

*

1

2

y

*

*

*

n

2

4

x

1

2

y

0

*

(k) (l) (m) (n) (o)

(p) (q)

n

2

4

x

1

2

y

*

*

3 -18

The computation tree

Much computation is duplicated. The iterative algorithm for generating Fibonacci sequence is better.

f5

f1f2

f2f3

f4 f3

f2 f1

f0f1 f0 f1

f0f1

3 -19

Binary search It is used for finding a given element in a sor

ted 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); /* 未找到 */}

e.g. 1 4 7 8 10 11 14

Search 6

3 -20

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 */

3 -21

Recursive chains a(formal parameters) { b(arguments);

} /* end a */

b(formal parameters) { a(arguments); } /* end b */

A recursive function need not call itself directly.

3 -22

Algebraic expressionsexpression term + term | termterm factor * factor | factorfactor letter | (expresssion)letter A | B | C | D | ... | Z

e.g A : (A) : A+B : (A+B) : A*B : A*(B+C): (A+B*)C: A+B+C :

**

3 -23

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 *);

3 -24

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 */

3 -25

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 */

3 -26

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 */

3 -27

int getsymb(char str[], int length, int *ppos){ char c;

if (*ppos < length) c = str[*ppos]; else c = ‘ ‘; (*ppos)++; return(c); } /* end getsymb */

3 -28

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.

A B C

12345

The initial setup of the Towers of Hanoi.

3 -29

Strategy for moving disks how to move 3 disks?

how to move n disks?

**

**

3 -30

#include <stdio.h>void towers(int, char, char, char);

void main(){ int n; scanf(“%d”, &n); towers(n, ‘A’, ‘C’, ‘B’); } /* end main */

Recursive program for the Tower of Hanoi problem

3 -31

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 */

3 -32

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 conditionT(2) = 3T(3) = 7

Number of movements

3 -33

Translation from prefix to postfix

infixA+B*CA*B+CA+B*C+D–E*F

prefix+A*BC+*ABC-++A*BCD*EF

postfixABC*+AB*C+ABC*+D+EF*-

example for translation:1. +A*BC prefix + (A) (*BC)

+ (A) (BC*) (A)(BC*)+ ABC*+ postfix

prefix prefix

postfix postfix

+

*A

B C

3 -34

D E F

2. - + + A * BCD * EF prefix - ( + + A * BCD)(*EF)

-(+(+A * BC)(D))(*EF)

-(+(ABC * + )(D))(*EF)

-(ABC * + D +)(EF*)

ABC * + D + EF * - postfix

postfix postfix

postfix postfix prefix

prefix prefix prefix

prefix prefix

-

*+

+

*A

B C

演算法 :

**

3 -35

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 */

Recursive program for conversion

3 -36

/* 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 */

3 -37

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); // 假設第一個為 operato

r m = find(temp);

+( )( )

m n

0 1 m+1

3 -38

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 */

3 -39

Action of calling a function in C

1. Passing arguments2. Allocating and initializing local variables3. Transferring control to the functionmain program

call on b

procedure b

Return address

call on c

procedure c

Return address

call on d

procedure d

Return address

main program

call on b

procedure b

Return address

call on c

procedure c

Return address

call on d

(a)

(b)A Series of procedures calling one another.

Control

Control

3 -40

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.

3 -41

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);

}

...

...

...

...

...

jix

return address

temporarybats

return address

for f3

for f2

for f1

local variables

parameters

Stack

Some programming languages do not allow recursive programs, e.g. FORTRAN, COBAL.

3 -42

Stack simulation with C 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; long int y; short int retaddr;};struct stack{ int top; struct dataarea item[MAXSTACK];

};

3 -43

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;

3 -44

/* 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;

start:

label2:

3 -45

/* 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:

3 -46

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 50struct stack{ int top; int param[MAXSTACK]; // Only n is kept;};int simfact(int n){ struct stack s; short int und; long int result, y; int currparam, x;

s.top = -1; currparam = n;

3 -47

/* 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;

start:

label2:

3 -48

/* 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:

3 -49

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; 

3 -50

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:

3 -51

The final simulation program

simfact(n)int n;{ int x; long int y;  for (y = x =1; x <= n; x++) y = y*x; return(y);} /* end simfact */

3 -52

Recursion and nonrecursion recursion: (1) need more time and space when executing (ma

chine efficiency) (2) easy to write a program (programmer efficiency)

nonrecursion: 正好相反

3 -53

Machine efficiency andprogrammer efficiency

如果一個 program 常用 , 應寫成 nonrecursive program.

目前 compiler 技術很好 , recursive program 亦可很快

前題 : 使用 nonrecursion 時 , 若需用 stack,

如 Towers of Hanoi, 則使用 recursion, 否則 , 使用 nonrecursion,

如 n!, Fibonacci numbers.