This maps very naturally to a recursive C++ program (in factorial1.cpp).
#include <iostream> using namespace std; int factorial(int n) { if (n == 0) return 1; return n * factorial(n-1); } main() { int n; cout << "Enter n: "; 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 n: 0 Factorial of 0 is 1 UNIX> a.out Enter n: 5 Factorial of 5 is 120 UNIX>Each time the procedure is called, it allocates new memory for variable n. So, when factorial(5) is called, there will be six instantiations of the procedure, each with its own value of n. For example, add some print statements and change it around a little (in factorial2.cpp).
#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 n: "; cin >> n; cout << "Factorial of " << n << " is " << factorial(n) << endl; return 0; } |
See if you can trace through the output:
UNIX> a.out Enter n: 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>
As with factorial, this maps very cleanly into a recursive procedure:
#include <iostream> using namespace std; int fib(int n) { if (n == 0 || n == 1) return 1; return fib(n-1) + fib(n-2); } main() { int n; cout << "Enter n: "; 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 n: 1 Fib(1) = 1. UNIX> a.out Enter n: 2 Fib(2) = 2. UNIX> a.out Enter n: 4 Fib(4) = 5. UNIX> a.out Enter n: 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 n: 1 Called fib(1). fib(1) equals 1. Fib(1) = 1. UNIX> a.out Enter n: 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 n: 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 n: 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 n: "; 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 n: 2 Fib(2) = 2. UNIX> a.out Enter n: 3 Fib(3) = 3. UNIX> a.out Enter n: 5 Fib(5) = 8. UNIX> a.out Enter n: 7 Fib(7) = 21. UNIX> a.out Enter n: 10 Fib(10) = 89. UNIX> a.out Enter n: 20 Fib(20) = 10946. UNIX> a.out Enter n: 30 Fib(30) = 1346269. UNIX> a.out Enter n: 40 Fib(40) = 165580141. UNIX> a.out Enter n: 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.