CS102 Recursion Examples

James S. Plank

Here are two archtypical recursion examples. First is implementing factorial. The formal definition of factorial is:

0! = 1
n! = n * (n-1)!

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> 

Example #2: Fibonacci numbers

The mathematical definition of the Fibonacci number is:

fib(0) = 1
fib(1) = 1
fib(n) = fib(n-1) + fib(n-2)

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:

Let's look at a table of what these variables will look like as i starts at 2, and is incremented through seven:

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:

Note that if you go through those steps every time you increment i, you'll get the values in the table, and when you stop, fi will equal fib(i). So, this lets you implement fib(n) efficiently (This is in fib3.cpp):

#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.