Robotics C++ Physics II AP Physics B Electronics Java Astronomy Other Courses Summer Session  

Recursion

 

 

 

 

 

 

Introduction

Recursion occurs when a function calls itself to solve a simpler version of the problem.  With each recursive call, the problem is different from, and simpler than, the original problem.

 

Recursion involves the internal use of a stack.  A stack is a data abstraction which works like this: 

 

  New data is "pushed," or added to the top of the stack. 

  When information is removed from the stack it is "popped," or removed from the top of the stack. 

  The recursive calls of a function will be stored on a stack, and manipulated in a similar manner.

 

In summary, you must be able to do two things in order to use recursion.

 

  Identify a series of steps that simplify the problem - lead to the stopping condition.

 

  Identify the stopping condition - when the problem terminates and begins to "unwind" toward a solution.

Factorial Example

The problem of solving factorials is our first example of recursion. 

The factorial operation in mathematics is illustrated below. 

 

1!

= 1    

2!

= 2*1 or 2*1!

3!

=

3*2*1

or

3*2!

4!

=

4*3*2*1

or

4*3!

 

  

Notice that each successive line can be solved in terms of the previous line. 

For example, 4! is equivalent to the problem

 

      4 * 3!

 

Following is a recursive function to solve the factorial problem.  Notice the recursive call is in the last line of the function. The function calls another implementation of itself to solve a smaller version of the problem.

 

int  fact (int n)

 

//  returns the value of n!

 

//  precondition:  n >= 1

 

{

        if (n = = 1)  return 1;               

        else  return  n * fact(n - 1);

}

 

The base case is a fundamental situation where no further problem solving is necessary. 

In the case of finding factorials, the answer of 1! is by definition = 1.  No further work is needed.

 

Suppose we call the function to solve fact(4).  This will result in four calls of function fact.

 

fact(4):  This is not the base case (1 == n), so we return the result of 4 * fact(3). 

This multiplication will be solved until an answer is found for fact (3). 

This leads to the second call of fact to solve fact(3).

 

act(3):  Again, this is not the base case and we return 3 * fact (2). 

 

    This leads to another recursive call to solve fact(2).

 

fact(2):  Still, this is not the base case, we solve 2 * fact(1).

 

fact(1):  Finally we reach the base case which returns the value 1.

Observations

When a recursive call is made, the current computation is temporarily suspended and placed on the stack with all its current information available for later use. 

 

A completely new copy of the function is used to evaluate the recursive call.  When that is completed, the value returned by the recursive call is used to complete the suspended computation.  The suspended computation is removed from the stack and its work now proceeds.

 

When the base case is encountered the recursion will now unwind and result in a final answer.  The expressions below should be read from right to left.

 

fact (4) = 4 * fact (3) = 3 * fact (2) = 2 * fact (1) = 1

 

24 <-- 4 * 6 <-- 3 * 2 <-- 2 * 1

 

     
A picture of the situation.

Items placed on the stack as proceed down

Note what is being returned as popping to go back up (unwind)

 

                 

 

Each box represent a call of function fact.  To solve fact (4) requires four calls of function fact.

 

Notice that when the recursive calls were made inside the else statement, the value fed to the recursive call was (n-1).  This is where the problem is getting smaller and simpler with the eventual goal of solving 1!

Example 1: Factorial

#include "stdafx.h"

#include <iostream>

using namespace std;

int fact (int n);                                  //Name of variable not required by compiler           

int main ( )
{
    int n;                                            //Variables declared at beginning of function
    int answer;

    cout <<"Enter an integer"<<endl;
    cin >> n;

    answer = fact (n);                     //Note  that if variable type included get duplicate definition error
    cout << "The factorial of "<<n<<" is "<< answer<<endl;

    return 0;
}

int fact (int n)
{
    if (n = 1) return 1;                        //Stopping: returns to the stack, not to the calling function in main
    else return n * fact(n - 1);             //Note the simpler version of the problem
}
 

Example 2: Factorial with Formatting

#include <iostream>

using std::cout;
using std::endl;

#include <iomanip>

using std::setw;

unsigned long factorial( unsigned long );

int main()
{
    for ( int i = 0; i <= 10; i++ )
    cout << setw( 2 ) << i << "! = " << factorial( i ) << endl;

    return 0;
}

// Recursive definition of function factorial
unsigned long factorial( unsigned long number )
{
    if ( number <= 1 )                                 // base case
    return 1;
    else // recursive case
    return number * factorial( number - 1 );
}

Output

Bad Implementations

What is wrong here? What is the output?

 

void PrintInt (int k)

{

    cout << k<<' '';

    PrintInt (k + 1);

}

 

This function is called as follows:

 

    PrintInt (1);

 

What is wrong here?

 

void PrintInt (int k)

{

    if (k < 1)

    {

        cout << endl;

    }

    else

    {

        cout << k << ' ';

        PrintInt (k + 1);

    }

}

 

You pick how it is called.

Questions

Is anything wrong with the following?

 

void PrintInt (int k)

//postcondition: print the numbers from k to 10, ending with a new line

{

    if (k > 10)

    {

        cout << endl;

    }

    else

    {

         cout << "k<<10"<<endl;

          PrintInt (k + 1);

      }

}   

Pitfalls of Recursion

If the recursion never reaches the base case, the recursive calls will continue until the computer runs out of memory and the program crashes.  Experienced programmers try to examine the remains of a crash.  The message “stack overflow error” or “heap storage exhaustion” indicates a possible runaway recursion.

 

When programming recursively, you need to make sure that the algorithm is moving toward the base case.  Each successive call of the algorithm must be solving a simpler version of the problem.

 

Any recursive algorithm can be implemented iteratively, but sometimes only with great difficulty.  However, a recursive solution will always run more slowly than an iterative one because of the overhead of opening and closing the recursive calls.