3

As I start learning recursion different questions are crossing my mind. Recursion uses more memory for the stack and it's usually slower due to maintaining the stack.

What is the benefit of using recursion if I still can use for loops? We describe actions to be repeated over and over until a condition is true, and we can use both recursion or for loops.

Why would I choose recursive version while I have faster control structures as an option?

10
  • 1
    Depends on the problem statement. Commented Apr 22, 2014 at 19:23
  • It will help you write much shorter code down the line. Commented Apr 22, 2014 at 19:24
  • except for having my code shorter what else I could do ? Commented Apr 22, 2014 at 19:25
  • 3
    Because recursion is much more natural than iteration in certain problems (like visiting tree nodes, for example), and leads to simpler, easier to understand and maintain code. Performance is not the main guiding factor, most of the time. Readability is. Commented Apr 22, 2014 at 19:26
  • 1
    See this question for an example where the problem is conceptually solved with nested loops, but the degree of nesting can change. Using recursion allows you to have variable nesting without having to rewrite the code for each possible case. Commented Apr 22, 2014 at 19:48

8 Answers 8

5

Recursion uses more memory for the stack and it usually slower due to maintaining the stack

That statement is far from being universally true. It applies in situations when you do not need to save the state for more than a fixed number of levels, but that does not cover many important tasks that can be solved recursively. For example, if you want to implement a depth-first search on a graph, you need to make your own data structure to store the state that would otherwise go on the stack.

What is the benefit of using Recursion if I still can use for loop?

You get more clarity when you apply a recursive algorithm to a task that is best understood through recursion, such as processing recursively-defined structures. In cases like that, a loop by itself is no longer sufficient: you need a data structure to go along with your loop.

Why would I choose recursive version while I have faster control structure?

You wouldn't necessarily choose recursion when you could implement the same algorithm with faster control structures that are easy to understand. However, there are situations when you may want to code a recursion to improve readability, even though you know that you can code the algorithm using a loop, with no additional data structures. Modern compilers can detect situations like that, and "rewrite" your recursive code behind the scene to make it use iterations. This lets you have the best of both worlds - a recursive program that matches reader's expectations, and an iterative implementation that does not waste space on the stack.

Unfortunately, demonstrating examples of situations when recursion gives you clear advantages requires knowledge of advanced topics, so many educators take shortcuts by demonstrating recursion using wrong examples, such as factorials and Fibonacci numbers. One relatively simple example is implementing a parser for an expression with parentheses. You can do it in many different ways, with or without recursion, but the recursive way of parsing expressions gives you an amazingly concise solution that is easy to understand.

Sign up to request clarification or add additional context in comments.

Comments

2

The great example of when the recursive solution is better than the itterative one is the tower of Hanoi. Consider the following two solutions -

Recursive (from this question):

public class Hanoi {

    public static void main(String[] args) {
        playHanoi (2,"A","B","C");
    }

    //move n disks from position "from" to "to" via "other"
    private static void playHanoi(int n, String from , String other, String to) {
        if (n == 0)
            return;
        if (n > 0)
        playHanoi(n-1, from, to, other);
        System.out.printf("Move one disk from pole %s to pole %s \n ", from, to);
        playHanoi(n-1, other, from, to);
    }

}

Iterative (copied from RIT):

import java.io.*;
import java.lang.*;

public class HanoiIterative{

    // -------------------------------------------------------------------------
    // All integers needed for program calculations.

    public static int n;         
    public static int numMoves;       
    public static int second = 0;     
    public static int third;          
    public static int pos2;           
    public static int pos3;
    public static int j;
    public static int i;

    public static void main(String args[]) {
     try{
         if( args.length == 1 ){
         System.out.println();
         n = Integer.parseInt(args[0]);         //Sets n to commandline int
         int[] locations = new int[ n + 2 ];    //Sets location size 

         for ( j=0; j < n; j++ ){               //For loop - Initially all
             locations[j] = 0;                  //discs are on tower 1
         }

         locations[ n + 1 ] = 2;                //Final disk destination
         numMoves = 1;                          
         for ( i = 1; i <= n; i++){             //Calculates minimum steps
             numMoves *= 2;                     //based on disc size then
         }                                      //subtracts one. ( standard
         numMoves -= 1;                         //algorithm 2^n - 1 )

         //Begins iterative solution. Bound by min number of steps.
         for ( i = 1; i <= numMoves; i++ ){     
             if ( i%2 == 1 ){                   //Determines odd or even.
             second = locations[1];
             locations[1] = ( locations[1] + 1 ) % 3;
             System.out.print("Move disc 1 to ");
             System.out.println((char)('A'+locations[1]));
             }

             else {                             //If number is even.
             third = 3 - second - locations[1];
             pos2 = n + 1;
             for ( j = n + 1; j >=2; j-- )   //Iterative vs Recursive.
                 if ( locations[j] == second )
                 pos2 = j;
             pos3 = n + 1;
             for ( j = n + 1; j >= 2; j-- )  //Iterative vs Recursive.
                 if ( locations[j] == third )
                 pos3 = j;
             System.out.print("Move disc "); //Assumes something is moving.

             //Iterative set. Much slower here than Recursive.
             if ( pos2 < pos3 ){
                 System.out.print( pos2 );
                 System.out.print(" to ");
                 System.out.println((char)('A' + third));
                 locations[pos2] = third;
             }
             //Iterative set. Much slower here than Recursive.
             else {
                 System.out.print( pos3 );
                 System.out.print(" to ");
                 System.out.println((char)('A' + second));
                 locations[ pos3 ] = second;
             }
             }
         }
         }
     }               //Protects Program Integrity.
     catch( Exception e ){
         System.err.println("YOU SUCK. ENTER A VALID INT VALUE FOR #");
         System.err.println("FORMAT : java HanoiIterative #");
     }               //Protects Program Integrity.
     finally{
         System.out.println();
         System.out.println("CREATED BY: KEVIN SEITER");
         System.out.println();
     }
     }
}//HanoiIterative
//--------------------------------------------------------------------------------

Im guessing you didnt really read that iterative one. I didnt either. Its much more complicated. You change change some stuff here and there, but ultimately its always going to be complicated and there is no way around it. While any recursive algorithm CAN be converted to iterative form, it is sometimes much more complicated code wise, and sometimes even significantly less efficient.

Comments

0

How would you search a directory full of sub directories that are themselves full of sub directories and so on (like JB Nizet stated, tree nodes) or calculate a Fibonacci sequence with less ease than using recursion?

2 Comments

Fibonnaci only requires you to keep the last two previous numbers for calculating the next one, not the entire sequence. For the directory full of subdirectories, the iterative solution is the same as the recursive one, except that you maintain your own stack in the iterative version. You get that for free in the recursive one.
@RobertHarvey that is true, you can output the calculated numbers in the sequence as the recursion occurs though. I did state with less ease than using recursion.
0

All algorithms can be translated from recursive to iterative. Worst case scenario you can explicitly use a stack to keep track of your data (as opposed to the call stack). So if efficiency is really paramount and you know recursion is slowing you down significantly, it's always possible to fall back on the iterative version. Note that some languages have compilers that convert tail recursive methods to their iterative counterparts, e.g., Scala.

The advantage of recursive methods is that most of the time they are much easier to write and understand because they are so intuitive. It is good practice to understand and write recursive programs since many algorithms can be naturally expressed that way. Recursion is just a tool to write expressive and correct programs. And again, once you know your recursive code is correct, it's easier to convert it to its iterative counterpart.

Comments

0

Recursion is usually more elegant and intuitive than other methods. But it's not the universal solution to everything.

Take the Fibonacci sequence for example. You can find the nth term by recursively using the definition of fibonacci number (and the base case with n == 1). But you'll find yourself calculating the mth term ( m < n - 2 ) more than once .

Use instead an array [1, 1] and append the next ith term as the sum of a[i-1] + a[i-2]. You'll find a linear algorithm a lot faster than the other.

BUT you'll love recursion.

It's elegant and often powerful.

Comments

0

Imagine you want to traverse a tree to print something in order. It could be something like:

public void print (){
  if( this == null )
      return;

  left.print();
  System.out.println(value);
  right.print();
}

To make this with a while loop you need to do your own backtracking stack because it has calls which are not in tail position (though one call is). It won't be as easy to understand as this though and IMO technically it would still be recursion since goto+stack is recursion.

If your trees are not too deep you won't blow the stack and the program works. There is no need to do premature optimizations. I even would have increased the JVM's stack before changing this to do it's own stack.

Now in a future version of the runtime even JVM can get tail call optimization just like proper runtimes should have. Then all recursions in tail positions won't grow the stack and then it's no difference from other control structures so you choose which has the most clear syntax.

Comments

0

My understanding is that standard iterative loops are more applicable when your data set is small, has minimal edge cases, and for which the logical conditions are simple for determining how many times to iterate one or more dedicated functions.

More importantly, recursive functions are more useful when applied to more complex nested data structures, in which you may not be able to intuitively or accurately estimate how many times you need to loop, because the amount of times you need dedicated functions to reiterate is based on a handful of conditions, some of which may not be mutually exclusive, and for which you care to specifically deliberate the order of the call stack and an intuitive path to a base case, for ease of readability and debugging***.

Comments

-5

Recursion is recommended for prototype programming for non programmers or junior programmers. For more serious programming you should avoid recursion as much as you can. Please read NASA coding standard

12 Comments

This not only is bad advice, it's not even true. The NASA standard forbids recursion because they are looking for static loop bounds. Controlling recursion in real-time systems is important because an unbounded function has unpredictable running time.
So how can you tell good or bad without knowing use case? You can't prove opposite, why do you put stupid comments? I request you to remove it.
Your answer is a sweeping generality without any basis. The coding standard you cite as evidence is used by a specific domain to meet specific goals that are not generally applicable to the broader C community. Who is recommending recursion for amateur prototype programming? Why should recursion be avoided?
The NASA coding standard you cited explains exactly why they don't allow recursion, as I did in my original comment. JPL writes hard real-time software that controls space systems. You can't allow recursion in hard real-time systems because you can't predict in advance how long the method will take to execute, or how much heap space the method will consume. Stack Overflows cannot happen in real-time systems that control rockets. But none of this applies to the average C programmer, whose programs are just as serious, if not necessarily as life-critical.
I am saying that your advice does not apply to C programmers in general, but only to a specific application of C programming. Your general advice about amateur and professional programmers doesn't apply to anyone.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.