knowledge-kitchen

Recursion (in Java)

Use recursive procedures for recursively-defined data structures.

–Kernighan and Plauger, in The Elements of Programming Style

  1. Overview
  2. Fibonacci Numbers
  3. Flipping Strings Backwards
  4. Calculating Powers
  5. Generalized Pattern
  6. Fractals
  7. Conclusions

Overview

Concept

In mathematics and computer science, recursion is a function or procedure that is defined in terms of itself.

Recursive humor

A few recursive insider jokes might help get a sense for what it is.

Fibonacci Numbers

A sequence

A classic example of a recursive algorithm is the calculation of the Fibonacci Sequence, a mathematical formula known to mathematicians since at least the 4th century B.C in India.

In code

In Java, a recursive formula could be written to calculate the Fibonacci number at any nth position in the sequence.

public static int fibonacci(int n) {
  if (n == 1) return 0;
  if (n == 2) return 1;

  return fibonacci(n - 1) + fibonacci(n - 2);
}

Call stack

The following diagram illustrates the changes to the call stack over time as our algorithm calculates the Fibonacci number at position 5 in the sequence.

Calculating Fibonacci numbers

Flipping Strings Backwards

Another example

Let’s take a simpler example of a recursive algorithm for a function used to flip a string backwards.

  1. If the original string is only one character long, the reversed form is the same as the original. For example, "e" backwards is "e". So simply return the original.

  2. Otherwise, return the last character of the original string concatenated to the reversed version of the remainder (excluding the last character). For example, "nonplussed" backwards is "d" + backwards("nonplusse")

In code

In Java, the recursive string flipping algorithm might look like this:

public static String backwards(String original) {

    // if string is one character or less, return the original
    if (original.length() <= 1) {
      return original;
    }

    // otherwise, return the last character + the backwards of the remainder
    else {
      char lastChar = original.charAt(original.length() - 1);
      String remainder = original.substring(0, original.length() - 1);
      return lastChar + backwards(remainder);
    }

}

Call stack

Understanding recursion can sometimes be assisted by diagramming the step-wise changes to the function call stack, including the arguments and return values from each new function invocation.

Flipping a string backwards

Calculating Powers

Solving power equations

Another illustrative example is to solve power equations recursively. For example, the problem of calculating the value of a base raised to a certain exponent, such as 612.

In code

In Java, the recursive power algorithm might look like this:

public static int power(int base, int exp) {

  if (exp == 0) {
    return 1;
  }

  else {
    return base * power(base, exp-1);
  }

}

Call stack

The changes to the function call stack over time for calculating 52 might look like:

Calculating powers

Generalized Pattern

Concept

A recursive function almost universally follows a general pattern.

Fibonacci example

For example, let’s revisit the Fibonacci algorithm.

public static int fibonacci(int n) {
  // base case: we know the answer
  if (n == 1) return 0;
  if (n == 2) return 1;

  // recursive case: return the sum of two recursive calls
  return fibonacci(n - 1) + fibonacci(n - 2);
}

Flipping string backwards example

And let’s revisit the backwards string algorithm.

public static String backwards(String original) {

    // base case: return the original
    if (original.length() <= 1) {
      return original;
    }

    // recursive case: return the last character + the backwards of the remainder
    else {
      char lastChar = original.charAt(original.length() - 1);
      String remainder = original.substring(0, original.length() - 1);
      return lastChar + backwards(remainder);
    }

}

Calculating powers

The same pattern applies to the power equation.

public static int power(int base, int exp) {

  // base case: return 1 when raised to power 0
  if (exp == 0) {
    return 1;
  }

  // recursive case: decompose the problem including recursion
  else {
    return base * power(base, exp-1);
  }

}

Fractals

Concept

Fractals are a category of algorithmically-generated images that exhibit a high-degree of self-similarity.

Sierpiński triangle

One of the “classic” fractal images is the Sierpiński triangle.

Sierpiński triangle

Koch snowflake

Another is the Koch snowflake.

Koch snowflake

Mandelbrot set

Perhaps the most famous fractal images are those derived from the Mandelbrot set.

Mandelbrot set fractal

Conclusions

Recursion takes some time to get familiar with. However, it can some problems that are inherently recursive in a more intuitive way, with simpler code, than other forms of iteration.