16A. Understanding Recursion

Recursive thinking

Here are a few hints to help you write recursive function definitions.

  1. Always have a clear and precise contract before you embark on writing a definition of a recursive function. Put this contract into your toolbox right away so that you can consider using the function that you are writing.

  2. Concentrate on facts.

  3. Break the problem down into cases. There must be at least one case that does not use recursion.

  4. When solving more complicated cases, imagine that the function that you are working on is already available, and assume that it works according to its contract. Do not think about how the recursive calls work. The contract is all you need.

  5. Only use your function recursively to solve smaller inputs than the one that you started with.

  6. Recursion is a substitute for a loop. Do not attempt to use both recursion and a loop in the same function

  7. Until you have a good understanding of recursion, do not change the value of any variable. If you think that you need to change the value of a variable, then you do not understand recursion. Ask for help if you need it.

Programmers have found that recursive function definitions tend to be highly reliable. If you are careful when establishing the basic facts, then your function definitions work the first time you try them more often than you think you have a right to.

Hand simulation of recursion 2: avoiding unnecessary details

We have seen scan algorithms solved using loops. We wrote a function to add up the values in in an array earlier using a loop. Here is the same function witten using recursion.

  // sum(A,n) returns A[0] + A[1] + ... + A[n-1].

  int sum(int A[], int n)
  {
    if(n == 0)
    {
      return 0;
    }
    else
    {
      return sum(A, n-1) + A[n-1];
    }
  }

That is still a scan algorithm, just looked at from another angle. Let's check whether it is correct.

It is easy to check the first case: if there are no values in the array, then their sum is 0. That means sum(A, 0) works correctly. So let's turn our attention to the second case. Notice that, when n > 0,

sum(A, n) = sum(A, n−1) + A[n−1]  from the second case of sum
= (A[0] + A[1] + … + A[n−2]) + A[n−1]  because sum(A, n−1) is assumed to work
= A[0] + A[1] + … + A[n−1]  

The hand simulation shows that sum(A, n) returns the correct result assuming that sum(A, n−1) returns the correct result. Then we know:

  1. sum(A, 0) works correctly.
  2. So sum(A, 1) works correctly.
  3. So sum(A, 2) works correctly.
  4. So sum(A, 2) works correctly.

and so on.

Exercises

  1. Write a C++ definition of function sum(a, b) that returns a + (a+1) + ... + b. For example, sum(2,5) = 2 + 3 + 4 + 5 = 14.

    More precisely, sum(a, b) returns the sum of all integers that are greater than or equal to a and less than or equal to b. For this question do not use any kind of loop. Use recursion instead.

    Write facts before you write the function definition that is based on those facts.

    Answer

  2. Using recursion, write a definition of function reverse(A, a, b), which reverses the order of the A[a], … A[b], where A is an array of ints. This function changes what is in array A.

    Hint. Draw a picture of an example array. If a < b, so there are at least two values in A[a], … A[b], consider swapping the contents of A[a] and A[b]. Do that in your picture. Now, what do you need to do to finish the job?

    If there are fewer than two values, what do you need to do?

    Answer

  3. We use positional notation to write numbers. In base 10, there is a position for 1's, a position for 10's, a position for 100's, etc., with a position for each power of 10.

    Computers also use positional notation, but they use base 2 instead of 10 (binary notation). There is a position for 1's, a position for 2's, a position for 4's, etc., with a position for each power of 2. The binary representation of 6 is 110 since 6 = 4 + 2.

    Write a function that takes a nonnegative integer argument n and writes the binary representation of n on the standard output. For example, if n is 6 it writes 110 and if n is 13 it writes 1101.

    Hints.

    1. If n < 2 then the binary representation of n is just n.

      For n ≥ 2, you will want to work at the low-order end of a number. The binary representation of n is the binary representation of n/2 followed by n%2. For example, if bin(n) is the binary string that represents integer n, then

        bin(1)  = "1".
        bin(3)  = "11"    = bin(1)  followed by 1.
        bin(6)  = "110"   = bin(3)  followed by 0.
        bin(12) = "1100"  = bin(6)  followed by 0.
        bin(25) = "11001" = bin(12) followed by 1.
      

      Notice that you are not asked to define bin(n), and you are do not need to create strings. Your function should write the binary number to the standard output.

    2. If you use those ideas with a loop, you will get the bits in reverse order. The easy way to handle that is to use recursion.

    Answer

  4. Write a complete program that reads a decimal integer and writes the equivalent binary integer. Both the input and the output are in standard order, starting with the highest-order digit. The input comes from the standard input and the output goes to the standard output.

    Use type long for the integers.

    Answer