Lecture 11: Recursive functions and efficiency
Objectives of this lecture
q Learn how computer handles recursive functions.
q Learn why recursive functions are generally
inefficient.
q Learn how to use static variables and pointer
parameters with recursive functions.
How recursion works?
q Recursion is very attractive as it can be used to write
more elegant functions that require less number of variables than their
equivalent iterative versions.
q However, recursive functions can be very inefficient
and often make programs to crash. We
explain this by studying how computer handles recursive call.
q When a function (not necessarily recursive) is called
by another function, the computer normally keeps the following information:
Ø
The
address of the instruction following the call – so that the program knows where
to return to when the function call is complete
Ø
The
values of all local and output variables.
q Except for recursive functions, storage can be
allocated for these information at compile time – since the compiler can
determine where each function is called.
This is call static
memory allocation.
q However, for recursive functions, it is not possible
to determine this at compile time since the number of times a function will
call itself can only be know at run-time.
q The allocation is thus done at run-time each time a
function call itself. This is called dynamic memory allocation.
q With each recursive call, an activation record (AR)
holding the necessary information is created and pushed onto the run-time stack
– a special memory area.
Example 1: The factorial function
q The following diagram shows what happened when the
factorial function developed in the previous lecture is called with n=4
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))
|
q An AR is created and pushed onto the stack each time
the function calls itself
q For large value of n, the stack can easily be
exhausted leading to the crash of the program.
q Also, considerable amount of time is taken in pushing
and popping ARs onto and off the stack.
q All these make recursive functions generally less
efficient than their iterative counterpart.
Example 2: Fibonacci Sequence:
This is defined
recursively as:
f0
= 0, f1 =
1 fi+1 = fi
+ fi-1 for i=1,2, ..
i.e. except for f0 and f1, every element is
the sum of its previous two elements: 0, 1, 1, 3, 5, . . .
The recursive
implementation is as follows:
int
fibonacci(int n)
{ if (n<=1)
return n;
else
return (fibonacci(n-1) +
fibonacci(n-2));
}
As the following
table shows, a large number of function calls is required by the above function
in computing the nth fibonacci number.
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
|
q Clearly, even for moderately small numbers, the number
of activation records that will be created is so large that the program can
easily crash.
q This suggests that even though recursion is elegant,
we should be careful in how we use it.
q However, static and pointer variables can be used to
improve the efficiency of some recursive function.
Recursive function involving static and
pointer variables:
Static variables
may be used to retain values of variables from successive calls of a recursive
function.
Example1:
The following program
computes the sum of the series : 1+2+3+4+..
up to the nth
term:
#include
<stdio.h>
int
get_sum(int n)
{ if (n<1)
return 0;
else
return (n+get_sum(n-1));
}
main()
{ int num;
printf("Enter your number>");
scanf("%d",&num);
printf("the sum to %dth term is
%d\n",num,get_sum(num));
return 0;
}
Example2:
The following
modifies the above program to use static variable.
#include
<stdio.h>
int
get_sum(int n)
{ int static sum=0;
if
(n>0)
{
sum+=n;
get_sum(n-1);
}
return sum;
}
main()
{ int num;
printf("Enter your number>");
scanf("%d",&num);
printf("the sum to %dth term is %d\n",num,get_sum(num));
return 0;
}
Example 3:
The following
modifies the program above to use pointer variables:
#include
<stdio.h>
void
get_sum(int n, int *sum)
{ if (n>0)
{
*sum+=n;
get_sum(n-1,sum);
}
}
main()
{ int num,sum;
printf("Enter your number>");
scanf("%d",&num);
get_sum(num,&sum);
printf("the sum to %dth term is
%d\n",num,sum);
return 0;
}
Comparison between iterative and
recursive functions:
q Loops in the iterative case are replaced by if
statement in the recursive case
q The terminating condition for loop becomes the
condition for the if statement
q Variables that are repeatedly changed in the loop can
be made static or passed as extra arguments in the recursive version
Tutorial exercise:
q In the reverse string function, what happens if the
recursive call is placed after printf:
void reverse_str(void)
{ char ch;
scanf(“%c”, &ch);
if (ch != ‘\n’)
printf(“%c”,ch);
reverse_str();
}
q Write a recursive function that prints help exactly n
times