Lecture 11: Recursion Chapter15
Objective
·
Distinguish
between recursive and iterative solutions to a problem
·
Detect
runaway (infinite) recursion
·
Explain
how recursion can be used to reverse the order of a process
A recursive approach to a problem is one in which a function calls itself
to arrive at a solution. An iterative approach to a problem is one in
which a function contains a loop that is used to converge to a solution.
Generally, a recursive solution is slightly less efficient, in terms of computer time, than an
iterative one because of the overhead for the extra function calls. In many instances, however,, recursion
enables us to specify a natural, simple solution to a problem that otherwise
would be difficult to solve. For this reason, recursion is an important and
powerful tool in problem solving and programming. Recursion is used widely in
solving problems. That are not numeric, such as proving mathematical theorems,
writing compilers, and in searching and sorting algorithms.
Properties of Recursive Problems and solutions
Problems that can be solved by recursion have the following
characteristics:
*. One or more stopping cases have a simple, nonrecursive solution
steps to solve a recursive problem
1-
Try
to express the problem a s a simpler version of itself.
2-
Determine
the stopping cases(base case)
3-
Determine
the recursive steps(recursive case)
Stopping case: the statement that causes recursion to terminate
Terminating condition: a condition that evaluates a true when a
stopping case is reached.
Recursive step: the step in a program or algorithm that contains a
recursive call
In C, recursive functions typically have the following paradigmatic
form
|
if (test for simple case) { compute a simple solution without using
recursion } else { break the problem down into
subproblems of the same form. solve each of the subproblems by calling
this function recursively reassemble the solutions to the subproblems
into a solution for the whole |
|
|
To use recursion, you must be able to identify simple cases for which
the answer easily determined and a recursive decomposition that allows you to
break any complex instance of the problem into simpler problems of the same
type.
A recursive function must follow tow basic rules:
It must have and ending point
It must make the problem simpler

|
/* compute n! using a
recursive definition */ |
/* iterative solutions computes
n! for n is >=0 */ |
|
int factorial (int n) { if (n ==
0) return 1; else return( n * factorial (n-1)); } |
int
factorial (int n) { int i,
product=1; for
(i=n; i>1; --i) /* n * (n-1) * (n-2)
*/ product=product
*i; return
(product); } |


|
Function call |
n |
n+ sum(n-1) |
Return value |
|
sum(4) |
4 |
4 + 4 + 6 |
4+ 6=10 |
|
sum(3) |
3 |
3 + 3 + 3 |
3+3=6 |
|
sum(2) |
2 |
2 + 2 + 1 |
2+1=3 |
|
sum(1) |
1 |
1 + 1 + 0 |
1+0=1 |
Power function . base exp.
One way to do the calculation is:
Base exp= base *
base * base *
* base
For exp repetitions. A more
compact way is:
In this case the solution to
base exp is a larger case of solving base exp 1. The stopping
condition ( the base case) occurs when exp=0. Since any number raised to the 0
power is 1, we have the following situation
|
Recursive |
Iterative |
|
int power(int base, int exp) { if ( exp =
= 0) return
(1); else return (base * power(bae, exp 1)); } |
int
power_it(int base, int exp) { int count, product; product=base; for ( count = exp-1; count >0; count --) product = product * base; return ( product); } OR |
|
|
int power(int base, int
exp) { int p=1; while(exp !=0) { p *= base; exp --; } return p; } |
Fibonacci Sequence:
The
Fibonacci numbers are a sequence of numbers that have many varied uses
the
Fibonacci sequence 0, 1,
1,2,3,5,8,13,21,34
Fibonacci number Begins with the term 0 and 1 and has the property that each succeeding term is the sum of the two preceding terms)
· · The following functions implements computes the nth Fibonacci number.
|
Iterative
Solution |
Recursive Solution |
|
int fibonacci
(int n) { int
i,sum1=0, sum2=1, sum; if (n<=1) return n; else { for (i=2; i<=n; i++) { sum=sum1+sum2; sum1=sum2; sum2=sum; } return sum; } } |
int fibonacci(int n) { if (n<=1) return n; else return (fibonacci(n-1)
+ fibonacci(n-2)); } |
|
#include
<stdio.h> int main( ) { long result,
number; long
fibonacci(long n); printf("enter
an integer:"); scanf("%ld",&number); result=fibonacci(number); printf("Fibonacci(%ld)=%ld\n",number,result); return 0; } long
fibonacci(long n) { long ans; if( n ==0 || n
==1) return n; else ans=
fibonacci(n-1) + fibonacci (n -2); return ans |
Enter an integer:0 Fibonacci (0) =0 Enter an integer:3 Fibonacci (3) =2 Enter an integer:6 Fibonacci (6) =8 Enter an integer:5 Fibonacci (5) =5 Enter an integer:10 Fibonacci (10) =55
|
Problem:
Write a recursive function that finds the sum of the values in an array x of
size n(element x[0] through x[n-1]).
The
stopping case occurs when n is 1, that is, the sum is x[0] for an array with
one element. If n is not 1, then we must add x[n-1] to the sum we get when we
add the values in the subarray with indices 0 through n-2(a simpler version of
the problem)
|
#include<stdio.h> int findsum( int
x [ ], int n) { if
(n <=1) return x[0]; else return x[n-1] + findsum(x,n-1); } void
main( ) {
int array[3]={5,10,-7}; printf("the
sum of the numbers=%d",findsum(array,3)); } |
|
|
|
|
|
X[0] |
X[1] |
X[2] |
|
5 |
10 |
-7 |
Example
4: Reversing string.
An interesting
feature of recursion is its ability to easily reverse a process. The following program reads a string
from the user and prints it backward.
#include
<stdio.h> void reverse_str(void); main( ) {
printf(input a line: );
reverse_str();
printf(\n\n);
return 0; } void reverse_str(void) {
char ch; scanf(%c, &ch);
if (ch != \n) reverse_str();
printf(%c,ch); } q Notice that in the function reverse_str, if the
character read is not the new line character, the function is invoked again.
q Each call has its own local storage for the variable
ch. The
function calls are stacked by the system until the new line character is
read.
q
Only after the new
line character is read that printing begins starting with the last
character entered.
Recursion relies on an internal (usually hardware) runt-time stack to store the information for keeping track of the recursive calls. What happens if the
recursive call is placed after printf as shown next? void reverse_str(void) {
char ch;
scanf(%c, &ch);
if (ch != \n)
printf(%c,ch); reverse_str(); } |
How Recursion Works: The
Run-Time Stack
Example 1: The factorial function
|
AR5 n=0 return
(1) |
|
AR4 n=1 return
(1*fact(0)) |
|
AR3 n=2 return
(2*fact(1)) |
|
AR2 n=3 return
(3*fact(2)) |
|
AR1 n=4 return
(4*fact(3)) |

Dynamic
memory allocation occurs at run time. With each function call, an activation record
is created and pushed onto the run time stack.
An
activation record contains the state of a given function, that is, the address
of the current instruction and the values of all the local variables, Each time
a function calls itself, an activation record is created and pushed on the run-time stack . The first call thus
appears at the bottom of the sack. The last call( the base case) appears at the
top of the stack. At that time the top activation record is popped from the
stack and the function returns its first value. The process continues until all
activation records are popped from the stack, at which time the recursion stops
int
fibonacci(int n)
{ if (n<=1)
return n;
else
return (fibonacci(n-1) + fibonacci(n-2));
}
n
|
Value
of fibonacci(n)
|
Number
of calls
|
0
|
0
|
1
|
1
|
1
|
1
|
2
|
1
|
3
|
3
|
2
|
5
|
. . .
|
. . .
|
. . .
|
8
|
21
|
67
|
9
|
34
|
109
|
. . .
|
. . .
|
. . .
|
25
|
75,025
|
242,785
|
