Module 13. Recursion

 

Learning Objectives

  • Understand the concept and structure of a recursive function.
  • Identify the base case and recursive case in a recursive function.
  • Implement recursive functions to solve problems like summing a list, factorials, Fibonacci numbers, and GCD.
  • Analyze the benefits and drawbacks of using recursion in programming.
  • Recognize potential performance issues and avoid stack overflow errors in recursive algorithms.

 

 

1. Introduction

 

In a program, a function can invoke another function, which in turn might call yet another function. Additionally, a function has the capability to call itself. Such a function is referred to as a recursive function. For instance, consider the function illustrated in the following program.

 

(Bad Recursion Example)

# This program has a recursive function.

# NOTE: This program causes infinite recursion

The output will be:

Hi. Do you know what a recursive function is?

Hi. Do you know what a recursive function is?

Hi. Do you know what a recursive function is?

…  will be repeated infinitely.  

 

In the above example, the print_message function outputs the string 'Hi. Do you know what a recursive function is?' and then invokes itself again and again. There is no mechanism to terminate the recursive calls. This function acts like an infinite loop because there is no code to halt its repetition. If you execute this program, you will need to press Ctrl+C on the keyboard to stop its execution.

Similar to a loop, a recursive function needs a mechanism to control the number of times it executes. The following code is a revised version of the print_message function. In this version, the print_message function takes an argument that determines how many times the message should be displayed.

(Revised Version)

# This program demonstrates a simple recursive function to print a message a specified number of times.

(Explanation )

  • Main Function: The main function initializes the number of times the message should be printed and calls the print_message function with that number.
  • Print Message Function:
  • Base Case: When times is 0 or less, the function stops calling itself, which prevents infinite recursion.
  • Recursive Case: The function prints the message and then calls itself with times - 1, reducing the number of remaining prints by one each time.

 

Key Concepts

  • Base Case: The condition under which the recursive function stops calling itself. This is crucial to prevent infinite recursion and stack overflow errors.
  • Recursive Case: The part of the function where it calls itself with a modified argument, moving towards the base case.

 

 

  1. Copy Yourself to Do the Rest

 

Recursion is a powerful technique in programming where a function calls itself to solve smaller instances of the same problem. This approach is particularly useful for problems that exhibit self-similarity, meaning the problem can be broken down into smaller, similar sub-problems.

 

Example 1: Summing a List of Numbers

Consider the task of summing a list of numbers using recursion. Here is a step-by-step breakdown of how the recursive function recsum works:

  1. Base Case: The simplest instance of the problem, where no further recursion is needed. In this case, when the index reaches the length of the list, there are no more elements to sum, so the function returns 0.
  2. Recursive Case: The function performs a small part of the work (adding the current element to the sum of the remaining elements) and then calls itself with a smaller instance of the problem (the next index in the list).

(Explanation)

  • Main Function:
  • The main function is the entry point of the program. It prints a message and then uses a for loop to print the first 10 Fibonacci numbers by calling the fib function.
  • Fib Function:
  • Base Cases:
    • When n is 0, the function returns 0.
    • When n is 1, the function returns 1.
  • Recursive Case:
    • For any n greater than 1, the function returns the sum of fib(n-1) and fib(n-2). This is where the function calls itself with smaller values of n.

 

Example 4: Greatest Common Divisor (GCD)

To further illustrate recursion, let's add an example of calculating the greatest common divisor (GCD) using the Euclidean algorithm.

# This program uses recursion to find the GCD of two numbers.

(Explanation)

• Main Function:

    • The main function prompts the user to enter two integers and then calls the gcd function to calculate and print their GCD.

• GCD Function:

    • Base Case: If y is 0, the function returns x as the GCD.
    • Recursive Case: The function calls itself with y and x % y (the remainder when x is divided by y), gradually reducing the problem size until the base case is reached.

 

3. Benefits and Drawbacks of Recursion

 

Recursion offers clarity and elegance by allowing problems to be defined and solved in a self-referential manner, making the code more intuitive and concise. However, it can be less efficient due to the overhead of multiple function calls and risks stack overflow errors in deep recursion scenarios.

 

Benefits:

  • Clarity: Recursive solutions can be more intuitive and easier to understand for problems that naturally fit a recursive structure.
    • For example, problems like traversing a tree, solving the Towers of Hanoi, or generating permutations can be more straightforwardly implemented using recursion.
  • Elegance: Recursive code can be more concise and closely match the problem's definition.
    • Recursion allows a problem to be defined in a self-referential way, which can lead to cleaner and more readable code. For instance, the mathematical definition of the Fibonacci sequence translates directly into a simple recursive function.

Drawbacks:

  • Performance: Recursive functions can be less efficient due to the overhead of multiple function calls.
    • Each recursive call adds a layer to the call stack, which consumes memory and processing time. In cases of deep recursion, this can lead to performance bottlenecks. 
  • Stack Overflow: Deep recursion can lead to stack overflow errors if the base case is not reached within a reasonable number of calls.
    • If the recursion is too deep or the base case is not defined properly, the program may exhaust the call stack's memory, leading to a stack overflow error. This is a common issue in naive implementations of recursive algorithms.

To summarize, recursion is a powerful technique for solving problems that can be broken down into smaller, similar sub-problems. While it offers clarity and elegance, it must be used judiciously to avoid performance issues and stack overflow errors. Understanding the base case and the recursive case is crucial for writing effective recursive functions.  

 

Summary

  1. A recursive function is a function that calls itself to solve smaller instances of the same problem.
  2. An example of bad recursion is a function that calls itself indefinitely without a termination condition, leading to infinite recursion.
  3. A revised recursive function includes a base case to stop the recursion and prevent infinite loops.
  4. The base case in a recursive function is the condition under which the function stops calling itself.
  5. The recursive case in a function is where the function performs part of the work and calls itself with modified arguments.
  6. Recursion is useful for problems with self-similarity, such as summing a list, calculating factorials, and generating Fibonacci numbers.
  7. The recursive process involves the function breaking down a problem into smaller sub-problems until it reaches the base case.
  8. An example of recursion is the Euclidean algorithm for finding the greatest common divisor (GCD) of two numbers.
  9. Recursion offers clarity and elegance in code but can be less efficient due to the overhead of multiple function calls.
  10. While recursion simplifies complex problems, it must be used carefully to avoid performance issues and stack overflow errors.