Functions and an Introduction to Recursion
Recursive function◦ A function that calls itself, either directly, or
indirectly (through another function) Recursion
◦ Base case(s) The simplest case(s), which the function knows
how to handle◦ For all other cases, the function typically divides
the problem into two conceptual pieces A piece that the function knows how to do A piece that it does not know how to do
Slightly simpler or smaller version of the original problem
Consider the following function.
How to compute f(4)?
Discuss:◦ What does this function actually compute?◦ What if f(1)=1 is not defined?
1 )1(
1 1)(
xxxf
xxf
4321
432)1(43)2(4)3()4(
ffff
◦ Recursive call (also called the recursion step) The function launches (calls) a fresh copy of itself to
work on the smaller problem Can result in many more recursive calls, as the
function keeps dividing each new problem into two conceptual pieces
This sequence of smaller and smaller problems must eventually converge on the base case Otherwise the recursion will continue forever
Although we don’t know the steps of how to solve f(n) directly, we find a way to break the size of problem into smaller pieces and solve them individually.
1 )1(
1 1)(
xxxf
xxf
6
Factorial◦ The factorial of a nonnegative integer n, written n! (and pronounced “n factorial”), is the product n · (n – 1) · (n – 2) · … · 1
◦ Recursive definition of the factorial function n! = n · (n – 1)! Example
5! = 5 · 4 · 3 · 2 · 15! = 5 · ( 4 · 3 · 2 · 1)5! = 5 · ( 4! )
7
1 // Fig. 6.29: fig06_29.cpp
2 // Testing the recursive factorial function.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 #include <iomanip>
8 using std::setw;
9
10 unsigned long factorial( unsigned long ); // function prototype
11
12 int main()
13 {
14 // calculate the factorials of 0 through 10
15 for ( int counter = 0; counter <= 10; counter++ )
16 cout << setw( 2 ) << counter << "! = " << factorial( counter )
17 << endl;
18
19 return 0; // indicates successful termination
20 } // end main
First call to factorial function
21
22 // recursive definition of function factorial
23 unsigned long factorial( unsigned long number )
24 {
25 if ( number <= 1 ) // test for base case
26 return 1; // base cases: 0! = 1 and 1! = 1
27 else // recursion step
28 return number * factorial( number - 1 );
29 } // end function factorial 0! = 1 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 10! = 3628800
Base cases simply return 1
Recursive call to factorial function with a slightly smaller problem
Either omitting the base case, or writing the recursion step incorrectly so that it does not converge on the base case, causes “infinite” recursion, eventually exhausting memory. This is analogous to the problem of an infinite loop in an iterative (nonrecursive) solution.
factorial(5)
numbernumber
Return valueReturn value
Return addressReturn address
main()
factorial(4)
factorial(3)
factorial(2)
factorial(1)
21
22 // recursive definition of function factorial
23 unsigned long factorial( unsigned long number )
24 {
25 if ( number <= 1 ) // test for base case
26 return 1; // base cases: 0! = 1 and 1! = 1
27 else // recursion step
28 return number * factorial( number - 1 );
29 } // end function factorial 0! = 1 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 10! = 3628800
Function Call Stack
Discuss:◦ What if there is no base case?
The Fibonacci series◦ 0, 1, 1, 2, 3, 5, 8, 13, 21, …◦ Begins with 0 and 1◦ Each subsequent Fibonacci number is the sum of
the previous two Fibonacci numbers◦ can be defined recursively as follows:
fibonacci(0) = 0 fibonacci(1) = 1 fibonacci(n) = fibonacci(n – 1) + fibonacci(n – 2)
1 // Fig. 6.30: fig06_30.cpp
2 // Testing the recursive fibonacci function.
3 #include <iostream>
4 using std::cout;
5 using std::cin;
6 using std::endl;
7
8 unsigned long fibonacci( unsigned long ); // function prototype
9
10 int main()
11 {
12 // calculate the fibonacci values of 0 through 10
13 for ( int counter = 0; counter <= 10; counter++ )
14 cout << "fibonacci( " << counter << " ) = "
15 << fibonacci( counter ) << endl;
16
17 // display higher fibonacci values
18 cout << "fibonacci( 20 ) = " << fibonacci( 20 ) << endl;
19 cout << "fibonacci( 30 ) = " << fibonacci( 30 ) << endl;
20 cout << "fibonacci( 35 ) = " << fibonacci( 35 ) << endl;
21 return 0; // indicates successful termination
22 } // end main
23
24 // recursive method fibonacci
25 unsigned long fibonacci( unsigned long number )
26 {
27 if ( ( number == 0 ) || ( number == 1 ) ) // base cases
28 return number;
29 else // recursion step
30 return fibonacci( number - 1 ) + fibonacci( number - 2 );
31 } // end function fibonacci fibonacci( 0 ) = 0 fibonacci( 1 ) = 1 fibonacci( 2 ) = 1 fibonacci( 3 ) = 2 fibonacci( 4 ) = 3 fibonacci( 5 ) = 5 fibonacci( 6 ) = 8 fibonacci( 7 ) = 13 fibonacci( 8 ) = 21 fibonacci( 9 ) = 34 fibonacci( 10 ) = 55 fibonacci( 20 ) = 6765 fibonacci( 30 ) = 832040 fibonacci( 35 ) = 9227465
Recursive calls to fibonacci function
Base cases
16
Caution about recursive programs◦ Each level of recursion in function fibonacci has
a doubling effect on the number of function calls i.e., the number of recursive calls that are required
to calculate the nth Fibonacci number is on the order of 2n
20th Fibonacci number would require on the order of 220 or about a million calls
30th Fibonacci number would require on the order of 230 or about a billion calls.
◦ Exponential complexity Can humble even the world’s most powerful
computers
17
Avoid Fibonacci-style recursive programs that result in an exponential “explosion” of calls.
18
Both are based on a control statement◦ Iteration – repetition structure◦ Recursion – selection structure
Both involve repetition◦ Iteration – explicitly uses repetition structure◦ Recursion – repeated function calls
Both involve a termination test◦ Iteration – loop-termination test◦ Recursion – base case
19
Both gradually approach termination◦ Iteration modifies counter until loop-termination
test fails◦ Recursion produces progressively simpler
versions of problem Both can occur infinitely
◦ Iteration – if loop-continuation condition never fails
◦ Recursion – if recursion step does not simplify the problem
1 // Fig. 6.32: fig06_32.cpp
2 // Testing the iterative factorial function.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 #include <iomanip>
8 using std::setw;
9
10 unsigned long factorial( unsigned long ); // function prototype
11
12 int main()
13 {
14 // calculate the factorials of 0 through 10
15 for ( int counter = 0; counter <= 10; counter++ )
16 cout << setw( 2 ) << counter << "! = " << factorial( counter )
17 << endl;
18
19 return 0;
20 } // end main
21
22 // iterative function factorial
23 unsigned long factorial( unsigned long number )
24 {
25 unsigned long result = 1;
26
27 // iterative declaration of function factorial
28 for ( unsigned long i = number; i >= 1; i-- )
29 result *= i;
30
31 return result;
32 } // end function factorial 0! = 1 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 10! = 3628800
Iterative approach to finding a factorial
Negatives of recursion◦ Overhead of repeated function calls
Can be expensive in both processor time and memory space
◦ Each recursive call causes another copy of the function (actually only the function’s variables) to be created Can consume considerable memory
Iteration ◦ Normally occurs within a function◦ Overhead of repeated function calls and extra
memory assignment is omitted
Any problem that can be solved recursively can also be solved iteratively (nonrecursively).
A recursive approach is normally chosen in preference to an iterative approach when the recursive approach more naturally results in a program that is easier to understand and debug. ◦ Another reason to choose a recursive solution is
that an iterative solution is not apparent.
Performance Tip 6.9◦ Avoid using recursion in performance
situations. Recursive calls take time and consume additional memory.
Common Programming Error 6.26◦ Accidentally having a nonrecursive function call
itself, either directly or indirectly (through another function), is a logic error.