6.8.2. Recursion V. Iteration

As the previous examples demonstrate, recursion often leads to compact, elegant solutions. We naturally assume that their compactness and elegance also imply a certain efficiency. However, recall that all function calls entail some overhead. First, jumping to and returning from a function's machine instructions contributes a relatively small and predictable overhead. But the second contribution, allocating, filling, and deallocating stack frames, is proportional to the number of local variables - including parameters - even when they don't change from one call to the next. Furthermore, the sequence of recursive function calls is potentially long, leading Sedgewick1 to make the following observations:

Not all programming environments support a general-purpose recursion facility because of [the] intrinsic difficulties involved. Furthermore, when recursion is provided and used, it can be a source of unacceptable inefficiency. For these reasons, we often consider ways of removing recursion. This is quite easy to do when there is only one recursive call involved . . . Recursion removal is much more complicated when there is more than one recursive call. (p. 12)

Two versions of Euclid's greatest common divisor algorithm, one recursive and the other iterative, illustrate some differences and similarities between the two approaches. The recursive version is slightly more compact than the iterative version, but the difference is small owing to the simplicity of both functions. The comparison demonstrates some of recursion's overhead and how iteration can often reduce it.

int gcd(int u, int v)
{
	u = (u < 0) ? -u : u;
	v = (v < 0) ? -v : v;

	if (v == 0)
		return u;
	else
		return gcd(v, u % v);
}
 
 
 
int gcd(int u, int v)
{
	u = (u < 0) ? -u : u;
	v = (v < 0) ? -v : v;

	while (v != 0)
	{
		int t = u % v;
		u = v;
		v = t;
	}
	return u;
}
(a)(b)
Euclid's Algorithm. Euclid's algorithm for finding the greatest common divisor (GCD) of two integers. The conditional statements at the beginning of the functions satisfy the algorithm's requirement of operating on non-negative values.
  1. A recursive version of the gcd function, adapted from Sedgewick1, p. 11. The first call to gcd ensures that u and v are non-negative. Nevertheless, the recursive version incurs this expense for each recursive call. Comparing the arguments in the function call with the parameters in the header exposes the heart of Euclid's algorithm:
    A picture showing the relationship between the arguments in the gcd function call and the parameters in the function header. Argument v in the call is passed to parameter u in the header, and the argument expression u%v in the call is passed to the parameter v.
    • u = v
    • v = u % v
    The gcd function call forms an expression in the return statement. The last activation in the recursive call sequence returns the GCD value to the previous activation. The return operation continues as each activation ends until the GCD is returned to the client, making the initial gcd call.
  2. An iterative (or looping) version of the gcd function, adapted from Sedgewick1, p. 12. The iterative version only checks u and v for non-negative values once. Although it requires a temporary variable (see The Swapping Problem), the while-loop's body performs the same operations as the recursive function call:
    • u = v
    • v = u % v
    Both versions perform the modular operation, so neither version offers an advantage in this respect. However, the iterative version performs the remaining operations with integer assignment (one of a computer's fastest operations), which is much faster than a function call.
Summary of recursion versus iteration.

  1. Sedgewick, R. (1983). Algorithms. Addision-Wesley Publishing Company. Reading, MA.