Here are two archetypical recursion examples. First is implementing factorial. The formal definition of factorial is:
This maps very naturally to a recursive C++ program (in factorial1.cpp).
#include <iostream>
using namespace std;
int factorial(int n) // 1, 1, 2, 6, 24, 120, 720, ...
{
if (n == 0) return 1;
return n * factorial(n-1);
}
main()
{
int n;
cout << "Enter a non-negative integer: ";
cin >> n;
cout << "Factorial of " << n << " is " << factorial(n) << endl;
return 0;
}
|
It runs like you'd hope it would:
UNIX> g++ factorial1.cpp UNIX> a.out Enter a non-negative integer: 0 Factorial of 0 is 1 UNIX> a.out Enter a non-negative integer: 5 Factorial of 5 is 120 UNIX>Each time the procedure is called, it allocates new memory for local variable n. So, when factorial(5) is called, there will be six instantiations of the procedure, each with its own copy (and value) of n. For example, add some print statements and change it around a little (in factorial2.cpp).
// 0! = 1
// n! = n * (n-1)!
#include <iostream>
using namespace std;
int factorial(int n)
{
int return_value;
cout << "Called factorial(" << n << ")\n";
if (n == 0) {
return_value = 1;
} else {
return_value = n * factorial(n-1);
}
cout << "Factorial(" << n << ") is returning " << return_value << endl;
return return_value;
}
main()
{
int n;
cout << "Enter a non-negative integer: ";
cin >> n;
cout << "Factorial of " << n << " is " << factorial(n) << endl;
return 0;
}
|
See if you can trace through the output:
UNIX> a.out Enter a non-negative integer: 5 Called factorial(5) Called factorial(4) Called factorial(3) Called factorial(2) Called factorial(1) Called factorial(0) Factorial(0) is returning 1 Factorial(1) is returning 1 Factorial(2) is returning 2 Factorial(3) is returning 6 Factorial(4) is returning 24 Factorial(5) is returning 120 Factorial of 5 is 120 UNIX>
Question:   Can you write factorial1.cpp using iteration rather than recursion?
As with factorial, this maps very cleanly into a recursive procedure:
#include <iostream>
using namespace std;
int fib(int n) // 1, 1, 2, 3, 5, 8, 13, 21, ...
{
if (n == 0 || n == 1) return 1;
return fib(n-1) + fib(n-2);
}
main()
{
int n;
cout << "Enter a non-negative integer: ";
cin >> n;
cout << "Fib(" << n << ") = " << fib(n) << "." << endl;
return 0;
}
|
As with factorial, this works nicely -- try it out (it's in fib1.cpp):
UNIX> a.out Enter a non-negative integer: 1 Fib(1) = 1. UNIX> a.out Enter a non-negative integer: 2 Fib(2) = 2. UNIX> a.out Enter a non-negative integer: 4 Fib(4) = 5. UNIX> a.out Enter a non-negative integer: 6 Fib(6) = 13. UNIX>And take a look at fib2.cpp, which prints out what's going on when you call fib():
UNIX> g++ fib2.cpp
UNIX> a.out
Enter a non-negative integer: 1
Called fib(1).
fib(1) equals 1.
Fib(1) = 1.
UNIX> a.out
Enter a non-negative integer: 2
Called fib(2).
fib(2) -- calling fib(1).
Called fib(1).
fib(1) equals 1.
fib(2) -- calling fib(0).
Called fib(0).
fib(0) equals 1.
fib(2) equals 2.
Fib(2) = 2.
UNIX> a.out
Enter a non-negative integer: 3
Called fib(3).
fib(3) -- calling fib(2).
Called fib(2).
fib(2) -- calling fib(1).
Called fib(1).
fib(1) equals 1.
fib(2) -- calling fib(0).
Called fib(0).
fib(0) equals 1.
fib(2) equals 2.
fib(3) -- calling fib(1).
Called fib(1).
fib(1) equals 1.
fib(3) equals 3.
Fib(3) = 3.
UNIX> a.out
Enter a non-negative integer: 4
Called fib(4).
fib(4) -- calling fib(3).
Called fib(3).
fib(3) -- calling fib(2).
Called fib(2).
fib(2) -- calling fib(1).
Called fib(1).
fib(1) equals 1.
fib(2) -- calling fib(0).
Called fib(0).
fib(0) equals 1.
fib(2) equals 2.
fib(3) -- calling fib(1).
Called fib(1).
fib(1) equals 1.
fib(3) equals 3.
fib(4) -- calling fib(2).
Called fib(2).
fib(2) -- calling fib(1).
Called fib(1).
fib(1) equals 1.
fib(2) -- calling fib(0).
Called fib(0).
fib(0) equals 1.
fib(2) equals 2.
fib(4) equals 5.
Fib(4) = 5.
UNIX>
You'll note, that's a lot of work for calculating fib(4), isn't it? In particular,
try entering 40 for n -- fib(40) takes so long that your program won't finish.
Type CNTL-C to kill the program.
Why does it take so long? Because your recursive program, while elegant, is inefficient. Think about it -- when you call fib(n-1), it will calculate fib(n-2). So when you then call fib(n-2), you are doing a lot of wasted work.
Can we make it faster? Yes, we can -- instead of having a recursive program, we can use the following integer values:
| i | fi | fim1 | fim2 |
| 2 | 2 | 1 | 1 |
| 3 | 3 | 2 | 1 |
| 4 | 5 | 3 | 2 |
| 5 | 8 | 5 | 3 |
| 6 | 13 | 8 | 5 |
| 7 | 21 | 13 | 8 |
This table gives us a clue how to implement fib(n). Start with:
i = 2; fi = 2; fim1 = 1; fim2 = 1;And when we increment i, then we do the following:
#include <iostream>
using namespace std;
int fib(int n)
{
int i, fi, fim1, fim2;
if (n == 0 || n == 1) return 1;
i = 2;
fi = 2;
fim1 = 1;
fim2 = 1;
while (i < n) {
i++;
fim2 = fim1;
fim1 = fi;
fi = fim1 + fim2;
}
return fi;
}
main()
{
int n;
cout << "Enter a non-negative integer: ";
cin >> n;
cout << "Fib(" << n << ") = " << fib(n) << "." << endl;
return 0;
}
|
Run it, and you'll see it works much more quickly:
UNIX> g++ fib3.cpp UNIX> a.out Enter a non-negative integer: 2 Fib(2) = 2. UNIX> a.out Enter a non-negative integer: 3 Fib(3) = 3. UNIX> a.out Enter a non-negative integer: 5 Fib(5) = 8. UNIX> a.out Enter a non-negative integer: 7 Fib(7) = 21. UNIX> a.out Enter a non-negative integer: 10 Fib(10) = 89. UNIX> a.out Enter a non-negative integer: 20 Fib(20) = 10946. UNIX> a.out Enter a non-negative integer: 30 Fib(30) = 1346269. UNIX> a.out Enter a non-negative integer: 40 Fib(40) = 165580141. UNIX> a.out Enter a non-negative integer: 50 Fib(50) = -1109825406. UNIX>You'll note, fib(40) runs very quickly. Also, fib(50) returns a negative number? Why? Because the maximum integer that you can hold is 232-1, which is 2147483647. Once we try to add beyond that number, random things start happening, and that's what happened with fib(50).
If you are confused with this, go into fib3.cpp and print out i,
fi, fim1 fim2 inside the while() loop. That should help
you get the idea of what's going on.