Sie sind auf Seite 1von 16

What is recursion? How does it work?

• Recursion is when a function or procedure calls itself.


• Mutual recursion is when two or more functions or
procedures call each other in a cycle.

• A classic example, factorial:

/* Returns n!= 1*2*3...(n-1)*n for n >= 0. */


int factorial(int n) {
if (n < 2) return 1;
else return factorial(n-1) * n;
}
Another classic example

Fibonacci Numbers
/* Returns nth fibonacci number fn for n >= 0:
- fn = fn-1 + fn-2
- Base case f0 = 0 and f1 = 1 */

int fibonacci (int n) {


if (n >= 2) {
return fibonacci(n-1) + fibonacci(n-2);
} else if (n == 1) {
return 1;
} else { /* n == 0 */
return 0;
}
}

NOTE: Fibonacci and not F-iobonacci!


Function calls (a picture approach)

If A calls B calls C calls D:

A B C D
passes program
control (flow)

returns program
control (flow)

Each call of routine Y from line L of routine X:


• suspends execution of caller X at line L
• starts called routine Y at line 1
• returns to caller X line L+1 on exit from called routine Y
Picture approach: Factorial

int factorial(int n) {
if (n < 2) return 1;
else return factorial(n-1) * n;
factorial(3): }

int factorial (3)


int factorial (2)
int factorial (1)

factorial (2)
factorial (1)
2
1

3
Value returned
Implementation of recursive calls

In practice, a recursive call doesn’t make an entire new copy


of a routine. Instead, it:

• Step 1: records the location to which it will return


• Step 2: reenters the new function code at the beginning
• Step 3: allocates memory for the local data for this new
invocation of the function

- For our factorial example, the parameter argument is the


only local variable.
Iteration versus Recursion

Two implementations of factorial:


int fact_re(int n) { int fact_it(int n) {
if (n < 2) { int result = 1;
return 1; int i;
else { for (i = 1; i <= n; ++i) {
return fact_re(n-1) * n; result *= i;
} }
} return result;
}

How many multiplications does each procedure do?


What other operations (total computational cost) does each
procedure do?
Function call overhead

What’s involved when making a function call?


• save caller’s state
• allocate stack space for arguments
• allocate stack space for local variables
• invoke routine
• at exit (return), release resources
• restore caller’s “environment”
• resume execution of caller

Both the recursive and the iterative routines take time which is
proportional to n, but the iterative algorithm will be much
faster in most language implementations. However, the
recursive is usually more efficient to code!
Combinatorial explosion in Fibonacci

fib(5) = fib(4) + fib(3)


= [ fib(3) + fib(2) ] + [ fib(2) + fib(1) ]
= [ [ fib(2) + fib(1) ] + [ fib(1) + fib(0) ] ] +
[ [ fib(1) + fib(0) ] + 1 ]
= [ [ [ fib(1) + fib(0) ] + 1 ] + [ 1 + 0 ] ] +
[[1+0]+1]
=[[[1+0] +1]+[1+0]]+[[1+0]+1]

Notice that fib(3) is expanded twice, and fib(2) is expanded


three times.
Fibonacci by iteration
int fibonacci(int n) {

int fib0 = 0, fib1 = 1, i, temp;

if (n == 0) return fib0;
else if (n == 1) return fib1;

/* After each iteration, fib0 and fib1 are


* the ith and (i-1)st fibonacci numbers
for (i = 2; i <= n; ++i) {
temp = fib1;
fib1 = fib0 + fib1;
fib0 = temp;
}

return fib1;

}
Choosing recursion

In languages not supporting recursion (e.g. Fortran),


recursion can be simulated with the use of a stack. The
‘recursive’ calls are simulated by iteration and “local data” is
kept on the stack.

Most languages that support recursion use a stack implicitly.


This can cause problems in language implementations where
this implicit stack has limited size.
A recursive implementation is justified when:
• it is difficult to write an iterative version, and
• the recursion doesn’t solve the same subproblems
repeatedly (resulting in a combinatorial explosion)
Our next example (Towers of Hanoi) is perfect for recursion.
Towers of Hanoi

Goal: Move disks from peg A to peg C.


Rule: May only move one disk at a time, onto a smaller disk
or an empty peg.

A B C
Some initial steps

A B C

A B C
Breaking the goal into subgoals

A B C

A B C
Implementation of Towers of Hanoi

/*
* Prints sequence of moves to move n disks (n >= 0)
* from peg source to peg dest using peg inter
* as intermediate.
*/
void move(int n, char source, char dest, char inter) {
if (n > 0) {
move(n - 1, source, inter, dest);
printf(“Move disk from peg %c to peg %c\n”,
source, dest);
move(n - 1, inter, dest, source);
} /* Otherwise n == 0, so do nothing */
}

What is the flow of execution for:


char A = ‘A’, B = ‘B’, C = ‘C’;
move(3,A,C,B);
Picturing the function calls

move(3,A,C,B) move(2,A,B,C) move(1,A,C,B)


move(0,A,B,C)
A to C
move(0,B,C,A)
A to B
move(0,C,A,B) move(1,C,B,A)
C to B
move(0,A,B,C)
A to C
move(0,B,C,A) move(2,B,C,A) move(1,B,A,C)
B to A
move(0,C,A,B)
B to C
move(0,A,B,C) move(1,A,C,B)
A to C
move(0,B,C,A)

The moves: A to C, A to B, C to B, A to C, B to A, B to C, A to C.
Tail recursion
A recursive function is called tail recursive if it only calls itself
as a final operation:
void f(int a, int b) {
int c, d;
if (<some_condition>) { /* Not real C */
/* some statements */
f(c, d); /* end with a recursive call */
} else { /* handle base case */ }
}

This can always be written without recursion as follows:


void f(int a, int b) {
int c, d;
while (<some_condition>) { /* Not real C */
/* some statements */
a = c; b = d;
}
/* handle base case */
}

Das könnte Ihnen auch gefallen