Recursion is a simple and powerful technique that makes solving many problems very simple, but this comes at a price. Normally we prefer to have normal looping constructs in our software instead, but not for the obvious reason. A Javascript file is available that contains all of the examples together with a few functions for performing timing and making sure they all give the same answers. The file expects to be run from the command line. On Windows open a command prompt and type: CScript “path\powers.js” Replace path with the full path you saved the file to. If you use some other platform then hopefully you already know how to execute the script. For those not on Windows you will also need to change all occurrences of WScript.Echo (there aren't very many) to whichever function displays output on your platform.

I don't know if Jack Altiere is right when he says many programmers are afraid of recursion, but if it's true I expect they fall into two categories:

Those who have not learned what recursion is yet. Those who have learned that it is often better not to use it.

Jack's short introduction is probably good for those in category one, but if you want to ever consider yourself an experienced programmer you should be trying to get into category two. This article is intended to help you get to category two as quickly as possible.

Some simple recursion

Recursive functions are easy. The only thing that you have to do to write a recursive function is to have it call itself. That's pretty easy and you may think that there isn't much reason to use it, but it can be very powerful. It turns out that there are all sorts of things that we might want to do that are easier to think of in terms of recursion. We won't go into all of them here, but if you look through your code you'll probably find that you have plenty of examples of them.

We're going to study a mathematical function that can be handled in all sorts of interesting ways — integer power calculations. I'm going to use Javascript (even though it wouldn't normally be my first choice for this sort of testing) because more people are likely to be able to run the tests themselves if I do so.

power( v, n ) = vn

For those that have forgotten your basic maths notations this means that we take some number v and multiply it by itself n times. If we take v as 4 and n as 2 we get 16 because 4 × 4 = 16 .

We also need to be aware of a couple of special rules. Anything multiplied by itself no times is one. This seems counter-intuitive, but it makes all the maths work out. Another rule is that anything multiplied by itself once is just the same as itself. We can write these as:

power( v, 0 ) = 1

power( v, 1 ) = v

We should put these together to get a complete definition:

power( v, 0 ) = 1

power( v, 1 ) = v

power( v, n ) = vn

Let's take a look at an extended example whilst we remember that our notation means we are multiplying a number by itself :

n = 0 ≔ 1

n = 1 ≔ 4 = 4

n = 2 ≔ 4 × 4 = 16

n = 3 ≔ 4 × 4 × 4 = 64

n = 4 ≔ 4 × 4 × 4 × 4 = 256

n = 5 ≔ 4 × 4 × 4 × 4 × 4 = 1024

There is a rather striking pattern here which we can express quite simply in an improved program :

power( v, 0 ) = 1

power( v, 1 ) = v

power( v, n ) = v × power( v, n - 1 )

It is this last part of our definition that makes it recursive. We have defined power() in terms of itself and that's all that a recursive function is — a function that calls itself. We can write this in Javascript pretty simply too:

function power_recurse( value, power ) { if ( power == 0 ) return 1; else if ( power == 1 ) return value; else return value * power_recurse( value, power - 1 ); }

The three parts to the if statement correspond perfectly to the three parts of our definition of power() . The first two are called base cases because they do not involve any extra call to power_recurse() . The last is the recursive call because, well I'm sure you can work it out.

There's a couple of things that we should notice about this:

We have to have the base cases in there . Without the base cases the function would continue on forever with power becoming a steadily larger negative value. When we call the recursive function we must alter the arguments in some way. Specifically we have to alter the arguments that are involved in the base case. If we don't do this then the base cases can never trigger and we'll also end up in an infinite loop.

For every recursive function that you ever write these two items must always work together, although the interaction isn't always so obvious. If you want to see an example where finding the base case is hard take a quick look near the end of this article.

Converting to a for loop

In the jargon of computer science the recursive version of power() is written in a functional style and the for loop version is in a procedural style. The difference between the two terms is more than just a change in how iteration (that is looping) is done. In (pure) functional programming looping is always done recursively by defining functions in terms of themselves (either directly or indirectly). Procedural programming on the other hand uses looping constructs ( for , while or do/while in Javascript — goto is also a form of looping construct but it's not mentioned in polite company any more). The more general terms for how these sorts of programs are constructed are declarative programming (of which functional is a sub-type) and imperative programming (for which procedural is a close synonym). The main difference between them is that the recursive/ functional/ declarative viewpoint is concerned with what is being computed whilst looping constructs/ procedural/ imperative programming is concerned with how the computation is performed. Procedural programming is much more common than pure functional programming partly because most procedural programming languages also support functional idioms. The most famous declarative language that you're likely to use from day to day is SQL . In case you're wondering object-oriented programming is yet another way of structuring software. As you can probably imagine there is much difference in opinion about where exactly to draw the lines between all of these terms (and the myriad more in use).

If we re-examine the pattern we can see that there is another solution that we shouldn't overlook too. Here is the pattern again:

n = 0 ≔ 1

n = 1 ≔ 4 = 4

n = 2 ≔ 4 × 4 = 16

n = 3 ≔ 4 × 4 × 4 = 64

n = 4 ≔ 4 × 4 × 4 × 4 = 256

n = 5 ≔ 4 × 4 × 4 × 4 × 4 = 1024

We can see that we're just multiplying n times. Writing this in Javascript is a breeze:

function power_for( value, power ) { var result = 1; for ( var i = 1; i <= power; i++ ) result *= value; return result; }

The base cases here are a little more subtle to spot and when you go through how it works you will notice that the second base case is missing altogether. We don't need it and skipping it makes the function much easier to work with.

The other thing to notice is that whereas the recursive version was a single statement, this one uses three .

To start with there doesn't seem to be much to choose between them. Functional purists would argue that the recursive function is more elegant, whilst many other programmers would probably argue that the for loop more clearly expresses how the calculation is done.

It turns out though that there is a good reason to prefer the for loop over the recursive function in Javascript. Below is a table of execution times (n is the power that we are requesting be calculated). I've had to repeat the calculations many thousands of times in order to be able to measure the time with any sort of accuracy . Even so there is a lot of noise in the data, but the pattern is still clear enough.

power_recurse power_for 0 235ms 282ms 1 203ms 343ms 2 515ms 360ms 3 547ms 375ms 10 2078ms 485ms 20 4375ms 703ms 30 6672ms 875ms 100 22875ms 3407ms

The first question is why did I choose to plot the times against n and not v? The answer is that n is the control variable. That is, the execution pattern of both functions depends on n and not v. Try working out how they would run with a few different values for v and n and you will see that it doesn't matter what v is, the number of times the recursive function calls itself or the number of times the for is executed never changes. Both change dramatically with any change in n .

The second question is why are the times so different? To answer that we need to think about what the Javascript interpreter is doing.

For power_recurse() , each time the function is called it must copy the two variables value and power . When we request a power of zero then this function call overhead is about the same for both versions and because of the way we have structured the recursive function (with two base cases) then this is the same for a power of one. But once we ask for a power of two then there are twice as many function calls in the recursive version as compared to the for loop.

So, although both versions are doing essentially the same amount of work in the same way the extra work in the function call overhead makes the for loop much faster . Now, interesting as this is there are two reasons for this result:

The operation at the heart of the calculation, the multiplication, is practically free compared to everything else we're doing. This skews the results dramatically. If we slowed the multiply down by a factor of a hundred then I doubt we would see any difference between them. The Javascript interpreter isn't all that smart (more on this later).

What this means is that although the recursive function incurs an overhead in the function calls and this takes longer this is not the reason why we prefer for loops!

Stack space and heap space

The memory that your computer program uses comes in two flavours and they behave quite differently (actually there are three if we include the memory that the program code sits in, but let's ignore that as it isn't important for this discussion).

The first, and the biggest, is the heap. This is where nearly all of the data that your programs use live. The stack on the other hand is small and is used for remembering where to return to after function calls, the function call parameters and the function's local variables. I'm sure you can see where this is leading.

The for loop version is only ever going to use one function call no matter how big the power we ask for is. The recursive version on the other hand has to remember function return points and variables on each call. For a power of one hundred that's one hundred times as much memory! If the power is big enough we can get Javascript to run out of memory using a recursive function which the for loop could never do.

And this is the reason why we normally prefer to write a loop rather than a recursive function when we can.

Even if the Javascript interpreter uses its heap for the Javascript stack the principle still holds — it just means that the power needed to blow the program up has to be bigger . On my machine power_recurse() exhausts the stack somewhere between n = 1,000 and n = 10,000. These are not high numbers. Even if the stack frame for each function call was one kilobyte (and it's probably actually a few hundredths of that size) then this represents only between one and ten megabytes.

For programming languages like C, C++ and Java though the stack is normally tiny in comparison to the heap. My computer at the moment has a gigabyte of RAM and a further 1½ gigabytes of swap space. My C++ compiler though has a default stack size of only one megabyte (which is still a lot larger than I was expecting), but still represents over three orders of magnitude.

This memory usage overhead of recursive functions is the real reason why recursive functions are often replaced with loops wherever possible. There are two circumstances under which recursive functions have to be very carefully controlled:

In library code, especially low-level libraries, the function call and stack overhead are probably completely unacceptable. If the control variable is under user control (either the programmer using your function, or even worse the end user sitting at the computer running the program) then you have not only a software crash waiting to happen but, for server software, a potential vector for attack. It may be possible to inject code (similar to buffer overruns) or to use it for denial of service.

Of course recursive functions aren't all doom and gloom and there are good reasons to use them anyway. In many circumstances recursive functions are the simplest way to express an algorithm and for many recursive functions we can see that we have to pay the extra memory overhead anyway.

The other sort of recursion

What we've seen so far is a special type of recursion, one where only a single recursive call is made. The way that they are used is very almost tail recursion, and some compilers will be able to turn these functions into proper tail recursive forms in order to use a technique called tail call optimisation. This isn't the place to go into this in any more detail, but the outcome is that a good compiler is able to convert these into a for loop.

There is however another type of algorithm that is very easy (almost trivial) to implement recursively but is extremely hard to write as a loop and even when we do manage it we find that the memory overhead is about the same.

I'm going to stick to the example we've been using so far in order to show the form of the recursive calls even though a bit of even more clever maths will enable us to turn the algorithm into a loop.

Divide and conquer

Let's look at our pattern of multiplication again.

n = 0 ≔ 1

n = 1 ≔ 4 = 4

n = 2 ≔ 4 × 4 = 16

n = 3 ≔ 4 × 4 × 4 = 64

n = 4 ≔ 4 × 4 × 4 × 4 = 256

n = 5 ≔ 4 × 4 × 4 × 4 × 4 = 1024

If you look carefully you can see that we can split each of these in two. Let's start at n = 2 and go from there:

n = 0 ≔ 1

n = 1 ≔ 4 = 4

n = 2 ≔ 4 × 4 = 16 ≔ ( n = 1 ≔ 4 = 4 ) × ( n = 1 ≔ 4 = 4 )

n = 3 ≔ 4 × 4 × 4 = 64 ≔ ( n = 1 ≔ 4 = 4 ) × ( n = 2 ≔ 4 × 4 = 16 )

n = 4 ≔ 4 × 4 × 4 × 4 = 256 ≔ ( n = 2 ≔ 4 × 4 = 16 ) × ( n = 2 ≔ 4 × 4 = 16 )

n = 5 ≔ 4 × 4 × 4 × 4 × 4 = 1024 ≔ ( n = 2 ≔ 4 × 4 = 16 ) × ( n = 3 ≔ 4 × 4 × 4 = 64 )

Each of the powers can be expressed as powers of about half half their size. Can we make use of this in our program in any way? It turns out that this is pretty easy to do with our functional version, especially if we first put in a slight twist:

n = 0 ≔ 1

n = 1 ≔ 4 = 4

n = 2 ≔ 4 × 4 = 16 ≔ ( n = 1 ≔ 4 = 4 ) × ( n = 1 ≔ 4 = 4 )

n = 3 ≔ 4 × 4 × 4 = 64 ≔ ( n = 1 ≔ 4 = 4 ) × ( n = 1 ≔ 4 = 4 ) × 4

n = 4 ≔ 4 × 4 × 4 × 4 = 256 ≔ ( n = 2 ≔ 4 × 4 = 16 ) × ( n = 2 ≔ 4 × 4 = 16 )

n = 5 ≔ 4 × 4 × 4 × 4 × 4 = 1024 ≔ ( n = 2 ≔ 4 × 4 = 16 ) × ( n = 2 ≔ 4 × 4 = 16 ) × 4

What we've done is to split it into symetric calls of half the power with odd versions taking an extra factor of × 4. Written functionally this gives us:

power( v, 0 ) = 1

power( v, 1 ) = v

if ( modulus( n, 2 ) = 0 ) power( v, n ) = power( v, n / 2 ) × power( v, n / 2 )

if ( modulus( n, 2 ) = 1 ) power( v, n ) = power( v, n / 2 ) × power( v, n / 2 ) × v

I'm assuming that there is a modulus function that performs the standard mathematical operation and that the division by two is an integer division so it throws away the fraction.

Can we implement this in Javascript? Actually it's pretty easy to do so recursively. Again this is the natural way to turn our insight into working software and we end up with this new version:

function power_recurse_quicker( value, power ) { if ( power == 0 ) return 1; else if ( power == 1 ) return value; else { var r = power_recurse_quicker( value, Math.floor( power / 2 ) ); if ( power %2 ) return value * r * r; else return r * r; } }

Let's see what this does. The two base conditions are identical to our earlier version so that's pretty easy. As in the functional re-write above the only change is in our implementation for larger powers. We do our recursive call to calculate the answer for half the power and then use this, maybe with an extra factor of our value (if the power is odd) to calculate the requested result.

Does it work out any faster? If we think about what it will do now then it will recurse a lot less. If we start at n = 64 it will go to n = 32, then n = 16, n = 8, n = 4, n = 2 and finally hit the base case at n = 1. The other recursive version would go from n = 64 to n = 63 then n = 62 etc. until it also hit the base case at n = 1. The for loop would work up from n = 1 to n = 2 and then n = 3 etc. until it got to n = 64, but both of these would need to do 64 calculations to work out a sixty fourth power. This new version only does seven calls and six multiplications!

Let's look to see how that translates into the timings.

power_recurse power_recurse_quicker power_for 0 235ms 203ms 282ms 1 187ms 203ms 343ms 2 469ms 515ms 360ms 3 547ms 641ms 375ms 10 2078ms 1453ms 485ms 20 4375ms 1562ms 703ms 30 6672ms 1547ms 875ms 100 22875ms 2297ms 3407ms

We can see that it is slow at the lower values (they're all pretty quick on the base cases). By the time we get to n = 10 though it is beating the speed of the old recursive version and by the time we get to n = 100 it is about ⅓ faster than the for and a whopping ten times faster than the old recursive function .

I have a procedural version of this algorithm that I'm saving for another article. Have a think about how you might implement it. A special prize if you manage to come up with a non-recursive implementation of this algorithm that doesn't require any extra storage — it is not easy (and using logarithms doesn't count — it's of this algorithm).

Summary

Recursion is a very powerful tool and it often enables us to spot algorithms that we wouldn't otherwise see. We should however be very careful of using it in production software, especially where the parameters for the recursion ultimately derive from user input, or any other non-trusted source.

As well as a vector for certain types of denial of service type attacks it could also allow the propogation of worms.

Even where we have an algorithm that is very hard to express without recursion it may still be worth it in some circumstances simply because the heap is normally so much larger than the stack.

And finally here is a non-obvious recursive program. Try to work out what it does and how it does it. There is a clue in the the function name.

function gcd( value1, value2 ) { if ( value2 == 0 ) return value1; else return gcd( value2, value1 % value2 ); }

The download file (see top of article) includes this as gcd.js which will show you a table of results to help give you a clue.

See also

Wikipedia on recursion and [http://en.wikipedia.org/wiki/Tail_recursion tail recursion].

An example of recursion on Reddit.

Scheme requires tail-recursion to be automatically handled by the compiler. Note a subtle point here: the compiler must handle it not because of the function call overhead but in order to guarantee that the memory overhead is the same for any call depth. This amounts to a guarantee that you cannot run out of memory and in turn this means it needs a mechanical way to convert the tail recursion to a loop.

An example of the sort of procedure you may have to go through in order to get your compiler to convert your tail recursion into a loop (or if you're lucky your compiler does it for you).

Full results table

power_recurse power_recurse_quicker power_for power_builtin 0 235ms 203ms 282ms 281ms 1 187ms 203ms 343ms 500ms 2 469ms 515ms 360ms 391ms 3 547ms 641ms 375ms 484ms 4 844ms 750ms 422ms 484ms 5 1031ms 859ms 406ms 500ms 6 1125ms 1079ms 469ms 438ms 7 1203ms 906ms 437ms 437ms 8 1562ms 1437ms 500ms 485ms 9 1594ms 1485ms 484ms 500ms 10 2078ms 1453ms 485ms 468ms 20 4375ms 1562ms 703ms 485ms 30 6672ms 1547ms 875ms 484ms 40 8375ms 1860ms 1219ms 453ms 50 10125ms 1968ms 1593ms 516ms 60 11906ms 1985ms 1750ms 453ms 70 15047ms 2031ms 2000ms 500ms 80 17172ms 2109ms 2704ms 391ms 90 19469ms 2078ms 3093ms 453ms 100 22875ms 2297ms 3407ms 484ms 200 2422ms 6421ms 531ms 300 3203ms 10000ms 344ms 400 3922ms 14313ms 344ms 500 4110ms 18281ms 312ms 600 4781ms 23250ms 313ms 700 4859ms 27844ms 406ms 800 4360ms 32859ms 391ms 900 4453ms 34672ms 531ms 1000 4453ms 39421ms 344ms 2000 5047ms 359ms 3000 4812ms 453ms 4000 5516ms 375ms 5000 5344ms 344ms 6000 5937ms 594ms 7000 5578ms 484ms 8000 5828ms 391ms 9000 5313ms 328ms 10000 5672ms 422ms