More Uses For Recursion

Choose (n, k)

Remember choose.cpp? It only gave the right answer on your Mac if n <= 12, because 12! doesn't fit in the space your Mac has for an int. However, the answer for larger problems can still fit easily in an int. For example, 14 choose 7 is only 3432. There is a recursive way to calculate the choose function that will allow you to find the answer for n >12.

Facts

There's a mathematical proof for this, but, frankly, it's boring. I don't want to type it, you don't want to read it. Instead, here's an intuitive way to think about it: Say you distinguish one of the n items in the group. Let's call him Fred. That leaves (n-1) undistinguished items in the group. When you choose k items, either you include Fred or you don't. If you count the number of ways you can choose k items without choosing Fred, then you get (n-1) choose k. Now, if we definitely include Fred, then we only need to choose (k-1) more items, so there are (n-1) choose (k-1) ways to do that. Add them up, and you get the total number of ways to choose k items out of n.

The demo choose-recursive.cpp shows how to write this recursive choose function. Now, if you were to run this, you would notice a couple of things. First off, it does give the correct answer. Second, it gets slower the bigger your numbers are. There is a way to make choose(n, k) run fast even on large numbers, but to do that, you would need some stuff you haven't seen yet. So keep this in mind, cause choose is coming back later. If you're curious why choose takes so long (optional), read my appendix or talk to Professor Cormen during office hours.

Towers of Hanoi

This is part of homework 2. The setup is that there are 3 pegs, which we will call 1, 2, and 3. We start off with n disks on peg 1, and we want to move all of them to peg 2. There are two rules restricting how you may move:

  1. You may move only the top disk on a peg to the top of another peg.
  2. No disk may be placed on top of a smaller disk.

Your task is to write void Hanoi(int n, int fromPeg, int toPeg), which describes how to move disks 1 to n from fromPeg to toPeg. The third peg is used as a spare. If n=1, this is trivial. All we need to do is move disk 1 from fromPeg to toPeg. If there are two disks, it is only marginally more difficult. First we move disk 1 to sparePeg. Then we move disk 2 to toPeg. Finally, we move disk 1 to toPeg. Now, there is nothing special about pegs 1, 2, and 3. If we know how to move anything from peg 1 to peg 2, then we know just as well how to move it from peg 1 to peg 3, or wherever we need to put it. This makes it possible to look at the problem a little differently: If we want to move 2 disks from fromPeg to toPeg, then first we must move disk 1 out of the way, which we know how to do. Then we move disk 2 to toPeg, then we move disk 1 to toPeg. The next example may make the logic clearer. If we want to move three disks fromfromPeg to toPeg, then first we must move the top two disks to sparePeg (using the method we figured out before), where they will be out of the way. Then we move disk 3 to toPeg, which is trivial. Then, we again move (using the same method) the first two disks to toPeg. This is pretty much all you need to know, and is the basis of writing recursive programs: you know how to do a task for some base number (usually 1 or 0). You can do any number n if you know how to do n-1. Therefore, you can figure out 2 from 1, and 3 from 2, and so on.

Greatest Common Divisor

Euclid developed an algorithm to determine the GCD of two numbers which can be written iteratively like this:

GCD(int a, int b)
{
  int r;
  do {
    r = a % b;
    a = b;
    b = r;
    }
  while (r > 0);

  return a;
}

The demo GCD.cpp shows this function in action. With a little observation that GCD (a, b) = GCD (b, a % b) (take my word for it...it can be proven by showing that the two are divisors of each other), we can write a recursive algorithm for calculating the GCD. The way to do it is:

int GCD(int a, int b)
{
  int r;
	
  r = a % b;
  if (r == 0)
    return b;
  else
    return GCD(b, r);
}

See GCDRecursive.cpp. In this case, the iterative method is probably more efficient.

Checkerboard

It is possible to draw the checkerboard recursively. The method used in checkerboardRecursive.cpp is to divide the board into quarters, then recurse on each quarter. When you have divided it so that you are looking at one square, draw it if for that row and column, row % 2 != column % 2.

To Index Previous Next