Created 5th August, 2006 06:23 (UTC), last edited 5th December, 2007 03:31 (UTC)

Recursive rights and wrongs

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.

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 ) = v^{n}

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¹ [1We're only going to consider positive integers for both *v* and *n* here, but the equation does of course work for any numbers.].

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 ) = v^{n}

Let's take a look at an extended example whilst we remember that our notation means we are multiplying a number by itself² [2There is of course a shortcut that works for any positive values of *n* and not just integer ones, but this article is about *recursion* and not *logarithms*]:

*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³ [3I'm calling it a *program* because if we were using a pure functional programming language then this would *be* the program. The syntax would change depending on which programming language we were using, but the structure of the program would be essentially the same]:

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⁴ [4Although we don't need both — which can we remove? Try it and see]. 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.

`for`

loopIf 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⁵ [5Of course it depends on how you count *statements*, but the point is that the recursive function contains one block, the `if`

statement. This version contains three different items: the variable declaration; the `for`

loop; and finally the return statement.].

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⁶ [6I've also abridged the results. For a complete table see the end of the article.]. 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*⁷ [7A full explanation is beyond the scope of this article, but will be addressed in another article about algorithmic complexity (which sounds more complex than it is).].

The second question is why are the times so different? To answer that we need to think about what the Javascript interpreter⁸ [8I'm going to call it an interpreter as that's what it logically is even if your platform features a *just-in-time* compiler for Javascript.] 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⁹ [9I've ducked something to do with my results here. If you stop to think about the implementation of Javascript and the use of big floats you'll realise that most of the actual returns from the functions we've written are going to be *NaN* or some other floating point error value as they'll overflow. We could have used some large integer add-on for Javascript, but what we are interested here is the call graphs and memory usage of the different implementation and for that it doesn't really matter that they overflow — each implementation overflows at more or less the same place (differences down to rounding errors) and we check that for numbers that are in our normal range we get the same results.]. 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!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¹⁰ [10Although if the interpreter executed the recursive Javascript call recursively in it's own execution (which it almost certainly doesn't) then you could still exhaust its stack very quickly.]. 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.

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.

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¹¹ [11The fact that the `for`

loop can compete for such high values of *n* shows us just how high the function call overhead is in Javascript. It is doing more than ten times the number of calculations and is only slightly slower.].

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¹² [12I tried for several whole days to come up with something and didn't manage it. Despite first off wondering if it wasn't possible Jules' very clever algorithm in the forums does solve it.] (and using logarithms doesn't count — it's *of this algorithm*).

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.

- 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).

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 |

- After
*n*= 100`power_recurse()`

gets too slow to bother capturing more data. In any case it blows the stack somewhere between*n*= 1,000 and*n*= 10,000. -
`power_for()`

is starting to get too slow to capture results after about*n*= 1,000. - Also included is the built in function
`Math.pow()`

for comparison. Notice that it doesn't get slower for large*n*. There are good reasons for this and they're called logarithms.

© 2002-2019 Kirit & Tai Sælensminde. All forum posts are copyright their respective authors.

Licensed under a Creative Commons License. Non-commercial use is fine so long as you provide attribution.

kirit.com