Lab 9: Introduction to Recursion

Instructions: Follow the steps given in each section below. For each question, click the radio button next to the most correct answer. When you are finished, type your name and your partner at the end of the document, write down your answers for your records, then press the Submit button. You may submit just one lab response. If you submit more than one, only the first one counts for a grade.

Contents

A. Purpose

The purpose of this lab is to become familiar with recursion. By the end of the lab, you should have a better understanding of what recursion is and how to use recursive methods in Java programs.

B. What is Recursion?

  1. Recursion is just another looping mechanism in Java. If we want a section of the program to repeat, we can use an explicit looping statement (while, for, or do-while), or we can use recursion. (Using an explicit looping statement to control a loop is called iteration.)

  2. A recursive method is one that calls itself. It almost looks like a circular definition in a dictionary. Program RecursiveCount.java shows an example.

    RecursiveCount.javaDownload (Help)
    // Demonstration of recursion
    
    public class RecursiveCount
    {
      // countTo: Display the counter value, and loop with recursion
      //          until it reaches the limit value
      public static void countTo(int counter, int limit)
      {
        if (counter < limit)
        {
          System.out.print(counter);
          countTo(counter + 1, limit);
        }
      }
      
      public static void main(String[] args)
      {
        // Call the recursive method...
        countTo(1, 10);
      }
    }
    RecursiveCount.javaDownload (Help)

  3. Download program RecursiveCount.java, then compile and run it.

  4. In RecursiveCount.java, the method countTo is recursive, because it calls itself. (Note the call to countTo inside the if statement.) Notice that there is no loop statement in the program, but when you run the program, there is obviously a loop in there.

    Question
    1
    When the program RecursiveCount.java runs, it displays

    1. 12345678910
    2. 987654321
    3. 23456789
    4. 1
    5. 123456789

    Question
    2
    Which of the following changes makes RecursiveCount.java display a number starting with 5?

    1. Change the call in main() to countTo(5)
    2. Change the call in main() to countTo(1, 5)
    3. Change the call in main() to countTo(4, 10)
    4. Change the call in main() to countTo(5, 10)
    5. Change the call in main() to countTo(6, 10)

    Question
    3
    Which of the following changes makes RecursiveCount.java display a number ending with 8?

    1. Change the call in main() to countTo(1, 9)
    2. Change the call in main() to countTo(8, 10)
    3. Change the call in main() to countTo(1, 7)
    4. Change the call in main() to countTo(7, 10)
    5. Change the call in main() to countTo(1, 8)

C. Recursion and Iteration

  1. Sometimes, it's easy to convert a recursive program to an iterative one, and vice-versa. For example, here's a comparison of the recursive and iterative versions of countTo().

    Recursive Iterative
    public static void countTo(int counter,
                               int limit)
    {
      if (counter < limit)
      {
        System.out.print(counter);
        countTo(counter + 1, limit);
      }
    }
    public static void countTo(int counter,
                               int limit)
    {
      for (int i = XXXX; i <= YYYY; i++)
      {
        System.out.print(i);
      }
    }

    Question
    4
    To make the iterative version of countTo work like the recursive version (for any parameter value), the value of XXXX is

    1. 1
    2. limit
    3. 10
    4. counter
    5. 9

    Question
    5
    To make the iterative version of countTo work like the recursive version (for any parameter value), the value of YYYY is

    1. 9
    2. 10
    3. counter
    4. limit
    5. 1

    Question
    6
    Which of these iterative program segments displays the same result as this method when started with d(10)?
    static void d(int a)
    {
      if (a > 2)
      {
        System.out.println(a + " ");
        d(a - 2);
      }
    }
    

    1. for (int a = 10; a >= 2; a = a - 2)
      {
        System.out.println(a + " ");
      }

    2. for (int a = 4; a < 10; a = a + 2)
      {
        System.out.println(a + " ");
      }

    3. for (int a = 10; a < 2; a = a + 2)
      {
        System.out.println(a + " ");
      }

    4. for (int a = 9; a >= 4; a = a - 2)
      {
        System.out.println(a + " ");
      }

    5. for (int a = 10; a > 2; a = a - 2)
      {
        System.out.println(a + " ");
      }

D. Recursion and Turtle Graphics

  1. It's fairly easy to draw intricate designs using recursion and turtle graphics. Let's look at another example. Let's draw this design with turtle graphics:

    Image of a "hook" in turtle graphics

  2. Notice that the turtle ends facing left. Program DrawHook.java shows how to draw this shape.

    DrawHook.javaDownload (Help)
    // Draw a "hook"
    
    import turtlegraphics.*;
    
    class SmartTurtle extends Turtle
    {
      public void drawSquiggle(int size)
      throws TurtleException
      {
        move(size/2);
        turnRight(90);
        move(size/2);
        turnRight(90);
        move(size);
        turnLeft(90);
        move(size/2);
        turnLeft(90);
        move(size/2);
        turnLeft(90);
      }
    
      // turnLeft: Turns the turtle left the given number of
      //           degrees
      public void turnLeft(int degrees)
      throws TurtleException
      {
        this.turnRight(180);
        this.turnRight(180 - degrees);
      }
    
      // Other SmartTurtle methods here ...
    }
    
    public class DrawHook
    {
      public static void main(String[] args)
      throws TurtleException
      {
        SmartTurtle myTurtle = new SmartTurtle();
    
        myTurtle.penUp();
        myTurtle.turnLeft(90);
        myTurtle.move(300);
        myTurtle.turnRight(90);
        myTurtle.penDown();
        myTurtle.drawSquiggle(300);
      }
    }
    DrawHook.javaDownload (Help)

  3. Download DrawHook.java, then compile and run it. You should see this image.

    Image of a "hook" in turtle graphics

  4. Let's use recursion to make each line segment of the squiggle a smaller copy of itself. Change the program in this way.

    1. Add a count parameter to drawSquiggle (after the size parameter). The count parameter should be an int.

    2. Add this stopping rule to the drawSquiggle method.

      if (count == 0)
      {
        move(size);
      }
      

    3. Put the existing lines of the method inside the else part of this if statement.

    4. Change all the existing move statements in the else part of drawSquiggle to

      drawSquiggle(size/2, count - 1);
      
    5. Change the call to drawSquiggle in main to
      myTurtle.drawSquiggle(300, 2);
      

    Question
    7
    After making these changes to DrawHook.java, what image do you see?

    1. Turtle graphics drawing
    2. Turtle graphics drawing
    3. Turtle graphics drawing
    4. Turtle graphics drawing
    5. Turtle graphics drawing

    Question
    8
    Change the call to drawSquiggle() in main so the first argument to drawSquiggle() is 100, that is:
    myTurtle.drawSquiggle(100, 1);
    
    What is the largest value you can use for the second parameter without causing a run-time error?

    1. 4
    2. 6
    3. 12
    4. 10
    5. 8

  5. Nice design, huh?

    Question
    9
    Why does the second parameter to drawSquiggle() have the limit you found in the previous question?

    1. Each time drawSquiggle() calls itself, the turtle turns a larger and larger amount, eventually going over 180 degrees.
    2. The distance to move becomes larger each time drawSquiggle() calls itself, eventually going beyond the edge of the screen.
    3. The distance to move becomes smaller each time drawSquiggle() calls itself, eventually becoming 0.
    4. Each time drawSquiggle() calls itself, the turtle turns a smaller and smaller amount, eventually becoming 0 degrees.
    5. Recursion has a limit of 512 consecutive calls in Java, and the program reaches this.

E. Infinite Recursive Loops

  1. You can have an "infinite" recursive loop, just like you can an infinite while loop or infinite for loop. Remember that the stopping rule controls whether the recursive method calls itself or not. If the stopping rule is missing or not set up correctly, you might end up with a recursive loop that doesn't stop -- at least not for a long time.

  2. Program InfiniteRecursion.java has a recursive loop that doesn't have a stopping rule.

    InfiniteRecursion.javaDownload (Help)
    // Infinite recursive loop
    
    public class InfiniteRecursion
    {
      // recursive: A recursive method with no stopping rule
      static void recursive(int count)
      {
        System.out.println("recursive(" + count + ")");  
        recursive(count + 1);
      }
    
      public static void main(String[] args)
      {
        recursive(1);
      }
    }
    InfiniteRecursion.javaDownload (Help)

  3. Download program InfiniteRecursion.java, then compile and run it. (Press Control-C to stop the program.)

  4. Let's add a stopping rule. Add this if statement (exactly as shown) to the program, as the first statement in the recursive method.

    if (count == 10)
    {
      System.out.println("Done!!");
    }
    

    Question
    10
    After adding the if statement, what does program InfiniteRecursion.java when you run it??

    1. It still continues looping after 10 iterations.
    2. It displays recursive(1) through recursive(12), then stops.
    3. It displays recursive(1) through recursive(11), then stops.
    4. It displays nothing anymore.
    5. It displays recursive(1) through recursive(10), then stops.

  5. There's one small change you'll need to make so the stopping rule works correctly -- that is, that the program displays recursive(1) through recursive(9), then stops.

    Question
    11
    What single change needs to be made so the stopping rule makes the program display recursive(1) through recursive(9), then stop?

    1. Add a while statement to the main method.
    2. Add another parameter (limit) to the method.
    3. Take the count parameter out of the method.
    4. Put the last two lines of the method inside an else clause.
    5. Change the stopping rule so it uses a switch statement instead of an if statement.

F. Efficiency of Recursion

  1. Recursion is a powerful tool -- one that you should consider using instead of while, for, and do-while statements. Recursion can sometimes result in a simpler and smaller program than if you use iterative techniques.

  2. However, you should also be aware of the some of the limitations of recursion. First, in most programming languages, recursion is slower than iterative techniques -- sometimes as much as 5 or 10 times slower! Second (as we'll see in class), recursion takes more memory than iteration -- sometimes a lot more memory.

  3. Let's compare the relative speed of recursion versus iteration in Java. The program TestRecursion.java below has two methods for computing the factorial of a number: iFactorial() and rFactorial(). The program lets you pick the factorial to compute and whether to use the recursive or iterative method. It then shows the running time in milliseconds.

    TestRecursion.javaDownload (Help)
    // This program allows you to time recursive and iterative factorial
    // methods. Each of these methods is so fast, that the computer's
    // clock is not sensitive enough to measure its speed.  Therefore,
    // to measure the running time of iFactorial() or rFactorial(), this
    // program calls each method many times in succession (the
    // LOOP_TIMES constant controls how many times), then divides the
    // running time by LOOP_TIMES for display.
    
    import Keyboard;
    
    public class TestRecursion
    {
      // rFactorial: Returns the factorial of the number.
      //             (Recursive version)
      // Note: The number must be positive.
      static long rFactorial(long number)
      {
        if (number == 0)
        {
          return 1;
        }
        else
        {
          return number * rFactorial(number - 1);
        }
      }
    
      // iFactorial: Returns the factorial of the number.
      //             (Iterative version)
      // Note: The number must be positive.
      static long iFactorial(long number)
      {
        long product = 1;
        for (long i = 1; i <= number; i++)
        {
          product = product * i;
        }
        return product;
      }
    
      // This value controls how many times the program calls the factorial method
      // per test
      static final long LOOP_TIMES = 1000000;
    
      // recursiveTest: Returns how long it takes to call rFactorial(number)
      // in milliseconds
      static double recursiveTest(long number)
      {
          long recursiveStartTime = System.currentTimeMillis();
          for (int i = 0; i < LOOP_TIMES; i++)
          {
            long factorial = rFactorial(number);
          }
          return (double) (System.currentTimeMillis() - recursiveStartTime)
                 / LOOP_TIMES;
      }
      
      // iterativeTest: Returns how long it takes to call iFactorial(number)
      // in milliseconds
      static double iterativeTest(long number)
      {
          long iterativeStartTime = System.currentTimeMillis();
          for (int i = 0; i < LOOP_TIMES; i++)
          {
            long factorial = iFactorial(number);
          }
          return (double) (System.currentTimeMillis() - iterativeStartTime)
                 / LOOP_TIMES;
      }
    
      public static void main(String[] args)
      throws java.io.IOException
      {
        // Run methods quickly, so the just-in-time (JIT) compiler has
        // chance to compile them...
        double value = recursiveTest(2);
        value = iterativeTest(2);
        
        while (true)
        {
          System.out.println();
          long number = Keyboard.readLong("Number to find factorial of (-1 to quit): ",
                                        -1, 100);
          if (number == -1) break;
          
          System.out.println();
          System.out.println("Milliseconds per factorial calculation");
          
          double recursiveTime = recursiveTest(number);
          double iterativeTime = iterativeTest(number);
          System.out.println("  Recursive: " + recursiveTime);
          System.out.println("  Iterative: " + iterativeTime);
    
          System.out.println("Recursion to iterative ratio: "
                             + recursiveTime / iterativeTime);
        }
      }
    }
    TestRecursion.javaDownload (Help)

  4. Download program TestRecursion.java, then compile and run it. Have the program compute factorials of numbers like 10, 20, and 30.

    Question
    12
    How fast is recursion compared to iteration in TestRecursion.java?

    1. Recursion takes about the same time as iteration in this program.
    2. Recursion is about 7 times slower than iteration in this program.
    3. Recursion is about 10 times slower than iteration in this program.
    4. Recursion is about 5 times slower than iteration in this program.
    5. Recursion is about 9 times slower than iteration in this program.


Turn in this Assignment

First name:
Last name:
Partner's last name:

Write down your answers for your records, then press the Submit button below to turn in the lab assignment.