Related
I'm just getting into TCO and I understand the concept of how it reuses a single stack frame rather than creating a new one each time the method calls itself. I intuitively want to compare this structure to a while loop as the loop will continually perform an operation on a set of variables until the condition isn't met.
However, given that TCO uses only one stack frame it seems like performing a sorting algorithm such as quicksort wouldn't be possible using TCO as a reference to the stack frames would be needed once the recursive method "unwinds" back up the call stack once the base case is reached. Without a reference to the number of times the method was called, how does it know which subsequent operation to perform and how many times to perform it?
Given the method body for each call is the same I guess it could just keep a reference to the number of times the method has been called and then a pointer inside the method body of where to start execution but that's just a guess.
Thanks for your help.
TCO can be performed whenever a recursive call is in the final position. Quicksort requires two recursive calls, so clearly they can't both be in that position -- but one of them can, so that tail-call can be converted to a while loop:
Original (pseudocode):
quicksort(i, j) {
return if j <= i
k = getPivot(i, j)
partition(i, j, k)
quicksort(i, k-1) <--- This recursive call can't be changed
quicksort(k+1, j) <--- This recursive call is amenable to TCO
}
After TCO:
quicksort(i, j) {
while (j > i) {
k = getPivot(i, j)
partition(i, j, k)
quicksort(i, k-1) <--- This recursive call is unchanged
i = k+1
}
}
Calling them in the opposite order would also work.
I'm working on a homework problem that asks me this:
Tiven a finite set of numbers, and a target number, find if the set can be used to calculate the target number using basic math operations (add, sub, mult, div) and using each number in the set exactly once (so I need to exhaust the set). This has to be done with recursion.
So, for example, if I have the set
{1, 2, 3, 4}
and target 10, then I could get to it by using
((3 * 4) - 2)/1 = 10.
I'm trying to phrase the algorithm in pseudo-code, but so far haven't gotten too far. I'm thinking graphs are the way to go, but would definitely appreciate help on this. thanks.
This isn't meant to be the fastest solution, but rather an instructive one.
It recursively generates all equations in postfix notation
It also provides a translation from postfix to infix notation
There is no actual arithmetic calculation done, so you have to implement that on your own
Be careful about division by zero
With 4 operands, 4 possible operators, it generates all 7680 = 5 * 4! * 4^3
possible expressions.
5 is Catalan(3). Catalan(N) is the number of ways to paranthesize N+1 operands.
4! because the 4 operands are permutable
4^3 because the 3 operators each have 4 choice
This definitely does not scale well, as the number of expressions for N operands is [1, 8, 192, 7680, 430080, 30965760, 2724986880, ...].
In general, if you have n+1 operands, and must insert n operators chosen from k possibilities, then there are (2n)!/n! k^n possible equations.
Good luck!
import java.util.*;
public class Expressions {
static String operators = "+-/*";
static String translate(String postfix) {
Stack<String> expr = new Stack<String>();
Scanner sc = new Scanner(postfix);
while (sc.hasNext()) {
String t = sc.next();
if (operators.indexOf(t) == -1) {
expr.push(t);
} else {
expr.push("(" + expr.pop() + t + expr.pop() + ")");
}
}
return expr.pop();
}
static void brute(Integer[] numbers, int stackHeight, String eq) {
if (stackHeight >= 2) {
for (char op : operators.toCharArray()) {
brute(numbers, stackHeight - 1, eq + " " + op);
}
}
boolean allUsedUp = true;
for (int i = 0; i < numbers.length; i++) {
if (numbers[i] != null) {
allUsedUp = false;
Integer n = numbers[i];
numbers[i] = null;
brute(numbers, stackHeight + 1, eq + " " + n);
numbers[i] = n;
}
}
if (allUsedUp && stackHeight == 1) {
System.out.println(eq + " === " + translate(eq));
}
}
static void expression(Integer... numbers) {
brute(numbers, 0, "");
}
public static void main(String args[]) {
expression(1, 2, 3, 4);
}
}
Before thinking about how to solve the problem (like with graphs), it really helps to just look at the problem. If you find yourself stuck and can't seem to come up with any pseudo-code, then most likely there is something that is holding you back; Some other question or concern that hasn't been addressed yet. An example 'sticky' question in this case might be, "What exactly is recursive about this problem?"
Before you read the next paragraph, try to answer this question first. If you knew what was recursive about the problem, then writing a recursive method to solve it might not be very difficult.
You want to know if some expression that uses a set of numbers (each number used only once) gives you a target value. There are four binary operations, each with an inverse. So, in other words, you want to know if the first number operated with some expression of the other numbers gives you the target. Well, in other words, you want to know if some expression of the 'other' numbers is [...]. If not, then using the first operation with the first number doesn't really give you what you need, so try the other ops. If they don't work, then maybe it just wasn't meant to be.
Edit: I thought of this for an infix expression of four operators without parenthesis, since a comment on the original question said that parenthesis were added for the sake of an example (for clarity?) and the use of parenthesis was not explicitly stated.
Well, you didn't mention efficiency so I'm going to post a really brute force solution and let you optimize it if you want to. Since you can have parantheses, it's easy to brute force it using Reverse Polish Notation:
First of all, if your set has n numbers, you must use exactly n - 1 operators. So your solution will be given by a sequence of 2n - 1 symbols from {{your given set}, {*, /, +, -}}
st = a stack of length 2n - 1
n = numbers in your set
a = your set, to which you add *, /, +, -
v[i] = 1 if the NUMBER i has been used before, 0 otherwise
void go(int k)
{
if ( k > 2n - 1 )
{
// eval st as described on Wikipedia.
// Careful though, it might not be valid, so you'll have to check that it is
// if it evals to your target value great, you can build your target from the given numbers. Otherwise, go on.
return;
}
for ( each symbol x in a )
if ( x isn't a number or x is a number but v[x] isn't 1 )
{
st[k] = x;
if ( x is a number )
v[x] = 1;
go(k + 1);
}
}
Generally speaking, when you need to do something recursively it helps to start from the "bottom" and think your way up.
Consider: You have a set S of n numbers {a,b,c,...}, and a set of four operations {+,-,*,/}. Let's call your recursive function that operates on the set F(S)
If n is 1, then F(S) will just be that number.
If n is 2, F(S) can be eight things:
pick your left-hand number from S (2 choices)
then pick an operation to apply (4 choices)
your right-hand number will be whatever is left in the set
Now, you can generalize from the n=2 case:
Pick a number x from S to be the left-hand operand (n choices)
Pick an operation to apply
your right hand number will be F(S-x)
I'll let you take it from here. :)
edit: Mark poses a valid criticism; the above method won't get absolutely everything. To fix that problem, you need to think about it in a slightly different way:
At each step, you first pick an operation (4 choices), and then
partition S into two sets, for the left and right hand operands,
and recursively apply F to both partitions
Finding all partitions of a set into 2 parts isn't trivial itself, though.
Your best clue about how to approach this problem is the fact that your teacher/professor wants you to use recursion. That is, this isn't a math problem - it is a search problem.
Not to give too much away (it is homework after all), but you have to spawn a call to the recursive function using an operator, a number and a list containing the remaining numbers. The recursive function will extract a number from the list and, using the operation passed in, combine it with the number passed in (which is your running total). Take the running total and call yourself again with the remaining items on the list (you'll have to iterate the list within the call but the sequence of calls is depth-first). Do this once for each of the four operators unless Success has been achieved by a previous leg of the search.
I updated this to use a list instead of a stack
When the result of the operation is your target number and your list is empty, then you have successfully found the set of operations (those that traced the path to the successful leaf) - set the Success flag and unwind. Note that the operators aren't on a list nor are they in the call: the function itself always iterates over all four. Your mechanism for "unwinding" the operator sequence from the successful leaf to get the sequence is to return the current operator and number prepended to the value returned by recursive call (only one of which will be successful since you stop at success - that, obviously, is the one to use). If none are successful, then what you return isn't important anyhow.
Update This is much harder when you have to consider expressions like the one that Daniel posted. You have combinatorics on the numbers and the groupings (numbers due to the fact that / and - are order sensitive even without grouping and grouping because it changes precedence). Then, of course, you also have the combinatorics of the operations. It is harder to manage the differences between (4 + 3) * 2 and 4 + (3 * 2) because grouping doesn't recurse like operators or numbers (which you can just iterate over in a breadth-first manner while making your (depth-first) recursive calls).
Here's some Python code to get you started: it just prints all the possible expressions, without worrying too much about redundancy. You'd need to modify it to evaluate expressions and compare to the target number, rather than simply printing them.
The basic idea is: given a set S of numbers, partition S into two subsets left and right in all possible ways (where we don't care about the order or the elements in left and right), such that left and right are both nonempty. Now for each of these partitions, find all ways of combining the elements in left (recursively!), and similarly for right, and combine the two resulting values with all possible operators. The recursion bottoms out when a set has just one element, in which case there's only one value possible.
Even if you don't know Python, the expressions function should be reasonably easy to follow; the splittings function contains some Python oddities, but all it does is to find all the partitions of the list l into left and right pieces.
def splittings(l):
n = len(l)
for i in xrange(2**n):
left = [e for b, e in enumerate(l) if i & 2**b]
right = [e for b, e in enumerate(l) if not i & 2**b]
yield left, right
def expressions(l):
if len(l) == 1:
yield l[0]
else:
for left, right in splittings(l):
if not left or not right:
continue
for el in expressions(left):
for er in expressions(right):
for operator in '+-*/':
yield '(' + el + operator + er + ')'
for x in expressions('1234'):
print x
pusedo code:
Works(list, target)
for n in list
tmp=list.remove(n)
return Works(tmp,target+n) or Works(tmp,target-n) or Works(tmp, n-target) or ...
then you just have to put the base case in. I think I gave away to much.
"1. The program will calculate the values using repetitive execution (loops)."
Recursion is repetitive execution but, i do not think it's a loop, do you think if I used recursion it would follow the guideline above?
No. In fact, it looks like the assignment is specifically asking for the "opposite" of recursion, iteration.
Loops are fundamentally about iteration, which is different to recursion. The main difference is that an iteration uses a constant amount of space, whereas recursion uses more space the deeper the recursion goes. For example, here are iterative and recursive procedures to compute the sum of the numbers from 1 to n
def sum_iter(n):
x = 0
for i in range(1,n+1):
x += i
return x
def sum_recursive(n):
if n == 0:
return 0
else:
return n + sum_recursive(n-1)
If you run these with a very large argument, you will run out of stack space (a "stack overflow") on the recursive version, but the iterative version will work fine.
There is a special kind of recursion called tail recursion, which is where the function doesn't have to do anything with the value from a recursive call except pass it to the caller. In this case you don't need to keep track of the stack - you can just jump straight to the top. This is called tail call optimization. A tail recursive function to calculate the sum of the integers 1 to n looks like
def sum_tailrec(n):
def helper(s,i):
if i == 0:
return s
else:
return helper(s+i, i-1)
return helper(0, n)
In this case people often refer to the function helper as an iterative recursion, because (with tail call optimization) it is only using a constant amount of space.
This is all a bit moot, because Python doesn't have tail call optimization, but some languages do.
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
The community reviewed whether to reopen this question 5 months ago and left it closed:
Not suitable for this site We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
I'm having major trouble understanding recursion at school. Whenever the professor is talking about it, I seem to get it but as soon as I try it on my own it completely blows my brains.
I was trying to solve Towers of Hanoi all night and completely blew my mind. My textbook has only about 30 pages in recursion so it is not too useful. Does anyone know of books or resources that can help clarify this topic?
How do you empty a vase containing five flowers?
Answer: if the vase is not empty, you take out one flower
and then you empty a vase containing four flowers.
How do you empty a vase containing four flowers?
Answer: if the vase is not empty, you take out one flower
and then you empty a vase containing three flowers.
How do you empty a vase containing three flowers?
Answer: if the vase is not empty, you take out one flower
and then you empty a vase containing two flowers.
How do you empty a vase containing two flowers?
Answer: if the vase is not empty, you take out one flower
and then you empty a vase containing one flower.
How do you empty a vase containing one flower?
Answer: if the vase is not empty, you take out one flower
and then you empty a vase containing no flowers.
How do you empty a vase containing no flowers?
Answer: if the vase is not empty, you take out one flower
but the vase is empty so you're done.
That's repetitive. Let's generalize it:
How do you empty a vase containing N flowers?
Answer: if the vase is not empty, you take out one flower
and then you empty a vase containing N-1 flowers.
Hmm, can we see that in code?
void emptyVase( int flowersInVase ) {
if( flowersInVase > 0 ) {
// take one flower and
emptyVase( flowersInVase - 1 ) ;
} else {
// the vase is empty, nothing to do
}
}
Hmm, couldn't we have just done that in a for loop?
Why, yes, recursion can be replaced with iteration, but often recursion is more elegant.
Let's talk about trees. In computer science, a tree is a structure made up of nodes, where each node has some number of children that are also nodes, or null. A binary tree is a tree made of nodes that have exactly two children, typically called "left" and "right"; again the children can be nodes, or null. A root is a node that is not the child of any other node.
Imagine that a node, in addition to its children, has a value, a number, and imagine that we wish to sum all the values in some tree.
To sum value in any one node, we would add the value of node itself to the value of its left child, if any, and the value of its right child, if any. Now recall that the children, if they're not null, are also nodes.
So to sum the left child, we would add the value of child node itself to the value of its left child, if any, and the value of its right child, if any.
So to sum the value of the left child's left child, we would add the value of child node itself to the value of its left child, if any, and the value of its right child, if any.
Perhaps you've anticipated where I'm going with this, and would like to see some code? OK:
struct node {
node* left;
node* right;
int value;
} ;
int sumNode( node* root ) {
// if there is no tree, its sum is zero
if( root == null ) {
return 0 ;
} else { // there is a tree
return root->value + sumNode( root->left ) + sumNode( root->right ) ;
}
}
Notice that instead of explicitly testing the children to see if they're null or nodes, we just make the recursive function return zero for a null node.
So say we have a tree that looks like this (the numbers are values, the slashes point to children, and # means the pointer points to null):
5
/ \
4 3
/\ /\
2 1 # #
/\ /\
## ##
If we call sumNode on the root (the node with value 5), we will return:
return root->value + sumNode( root->left ) + sumNode( root->right ) ;
return 5 + sumNode( node-with-value-4 ) + sumNode( node-with-value-3 ) ;
Let's expand that in place. Everywhere we see sumNode, we'll replace it with the expansion of the return statement:
sumNode( node-with-value-5);
return root->value + sumNode( root->left ) + sumNode( root->right ) ;
return 5 + sumNode( node-with-value-4 ) + sumNode( node-with-value-3 ) ;
return 5 + 4 + sumNode( node-with-value-2 ) + sumNode( node-with-value-1 )
+ sumNode( node-with-value-3 ) ;
return 5 + 4
+ 2 + sumNode(null ) + sumNode( null )
+ sumNode( node-with-value-1 )
+ sumNode( node-with-value-3 ) ;
return 5 + 4
+ 2 + 0 + 0
+ sumNode( node-with-value-1 )
+ sumNode( node-with-value-3 ) ;
return 5 + 4
+ 2 + 0 + 0
+ 1 + sumNode(null ) + sumNode( null )
+ sumNode( node-with-value-3 ) ;
return 5 + 4
+ 2 + 0 + 0
+ 1 + 0 + 0
+ sumNode( node-with-value-3 ) ;
return 5 + 4
+ 2 + 0 + 0
+ 1 + 0 + 0
+ 3 + sumNode(null ) + sumNode( null ) ;
return 5 + 4
+ 2 + 0 + 0
+ 1 + 0 + 0
+ 3 + 0 + 0 ;
return 5 + 4
+ 2 + 0 + 0
+ 1 + 0 + 0
+ 3 ;
return 5 + 4
+ 2 + 0 + 0
+ 1
+ 3 ;
return 5 + 4
+ 2
+ 1
+ 3 ;
return 5 + 4
+ 3
+ 3 ;
return 5 + 7
+ 3 ;
return 5 + 10 ;
return 15 ;
Now see how we conquered a structure of arbitrary depth and "branchiness", by considering it as the repeated application of a composite template? each time through our sumNode function, we dealt with only a single node, using a single if/then branch, and two simple return statements that almost wrote themsleves, directly from our specification?
How to sum a node:
If a node is null
its sum is zero
otherwise
its sum is its value
plus the sum of its left child node
plus the sum of its right child node
That's the power of recursion.
The vase example above is an example of tail recursion. All that tail recursion means is that in the recursive function, if we recursed (that is, if we called the function again), that was the last thing we did.
The tree example was not tail recursive, because even though that last thing we did was to recurse the right child, before we did that we recursed the left child.
In fact, the order in which we called the children, and added the current node's value didn't matter at all, because addition is commutative.
Now let's look at an operation where order does matter. We'll use a binary tree of nodes, but this time the value held will be a character, not a number.
Our tree will have a special property, that for any node, its character comes after (in alphabetical order) the character held by its left child and before (in alphabetical order) the character held by its right child.
What we want to do is print the tree in alphabetical order. That's easy to do, given the tree special property. We just print the left child, then the node's character, then right child.
We don't just want to print willy-nilly, so we'll pass our function something to print on. This will be an object with a print( char ) function; we don't need to worry about how it works, just that when print is called, it'll print something, somewhere.
Let's see that in code:
struct node {
node* left;
node* right;
char value;
} ;
// don't worry about this code
class Printer {
private ostream& out;
Printer( ostream& o ) :out(o) {}
void print( char c ) { out << c; }
}
// worry about this code
int printNode( node* root, Printer& printer ) {
// if there is no tree, do nothing
if( root == null ) {
return ;
} else { // there is a tree
printNode( root->left, printer );
printer.print( value );
printNode( root->right, printer );
}
Printer printer( std::cout ) ;
node* root = makeTree() ; // this function returns a tree, somehow
printNode( root, printer );
In addition to the order of operations now mattering, this example illustrates that we can pass things into a recursive function. The only thing we have to do is make sure that on each recursive call, we continue to pass it along. We passed in a node pointer and a printer to the function, and on each recursive call, we passed them "down".
Now if our tree looks like this:
k
/ \
h n
/\ /\
a j # #
/\ /\
## i#
/\
##
What will we print?
From k, we go left to
h, where we go left to
a, where we go left to
null, where we do nothing and so
we return to a, where we print 'a' and then go right to
null, where we do nothing and so
we return to a and are done, so
we return to h, where we print 'h' and then go right to
j, where we go left to
i, where we go left to
null, where we do nothing and so
we return to i, where we print 'i' and then go right to
null, where we do nothing and so
we return to i and are done, so
we return to j, where we print 'j' and then go right to
null, where we do nothing and so
we return to j and are done, so
we return to h and are done, so
we return to k, where we print 'k' and then go right to
n where we go left to
null, where we do nothing and so
we return to n, where we print 'n' and then go right to
null, where we do nothing and so
we return to n and are done, so
we return to k and are done, so we return to the caller
So if we just look at the lines were we printed:
we return to a, where we print 'a' and then go right to
we return to h, where we print 'h' and then go right to
we return to i, where we print 'i' and then go right to
we return to j, where we print 'j' and then go right to
we return to k, where we print 'k' and then go right to
we return to n, where we print 'n' and then go right to
We see we printed "ahijkn", which is indeed in alphabetical order.
We manage to print an entire tree, in alphabetical order, just by knowing how to print a single node in alphabetical order. Which was just (because our tree had the special property of ordering values to the left of alphabetically later values) knowing to print the left child before printing the node's value, and to print the right child after printing the node's value.
And that's the power of recursion: being able to do whole things by knowing only how to do a part of the whole (and knowing when to stop recursing).
Recalling that in most languages, operator || ("or") short-circuits when its first operand is true, the general recursive function is:
void recurse() { doWeStop() || recurse(); }
Luc M comments:
SO should create a badge for this kind of answer. Congratulations!
Thanks, Luc! But, actually, because I edited this answer more than four times (to add the last example, but mostly to correct typos and polish it -- typing on a tiny netbook keyboard is hard), I can't get any more points for it. Which somewhat discourages me from putting as much effort into future answers.
See my comment here on that: https://stackoverflow.com/questions/128434/what-are-community-wiki-posts-in-stackoverflow/718699#718699
Your brain blew up because it got into an infinite recursion. That's a common beginner mistake.
Believe it or not, you already understand recursion, you're just being dragged down by a common, but faulty metaphor for a function: a small box with stuff that comes in and out.
Think instead of a task or procedure, such as "find out more about recursion on the net". That's recursive and you have no problem with it. To complete this task you might:
a) Read a Google's result page for "recursion"
b) Once you've read it, follow the first link on it and...
a.1)Read that new page about recursion
b.1)Once you've read it, follow the first link on it and...
a.2)Read that new page about recursion
b.2)Once you've read it, follow the first link on it and...
As you can see, you've been doing recursive stuff for a long time without any problems.
For how long would you keep doing that task? Forever until your brain blows up? Of course not, you will stop at a given point, whenever you believe you have completed the task.
There's no need to specify this when asking you to "find out more about recursion on the net", because you are a human and you can infer that by yourself.
Computers can't infer jack, so you must include an explicit ending: "find out more about recursion on the net, UNTIL you understand it or you have read a maximum of 10 pages".
You also inferred that you should start at Google's result page for "recursion", and again that's something a computer can't do. The complete description of our recursive task must also include an explicit starting point:
"find out more about recursion on the net, UNTIL you understand it or you have read a maximum of 10 pages and starting at www.google.com/search?q=recursion"
To grok the whole thing, I suggest you try any of these books:
Common Lisp: A Gentle Introduction to Symbolic Computation. This is the cutest non-mathematical explanation of recursion.
The little schemer.
To understand recursion, all you have to do is look on the label of your shampoo bottle:
function repeat()
{
rinse();
lather();
repeat();
}
The problem with this is that there is no termination condition, and the recursion will repeat indefinitely, or until you run out of shampoo or hot water (external termination conditions, similar to blowing your stack).
If you want a book that does a good job of explaining recursion in simple terms, take a look at Gödel, Escher, Bach: An Eternal Golden Braid by Douglas Hofstadter, specifically Chapter 5. In addition to recursion it does a nice job of explaining a number of complex concepts in computer science and math in an understandable way, with one explanation building on another. If you haven't had much exposure to these sorts of concepts before, it can be a pretty mindblowing book.
This is more of a complaint than a question. Do you have a more specific question on recursion? Like multiplication, it's not a thing people write a lot about.
Speaking of multiplication, think of this.
Question:
What's a*b?
Answer:
If b is 1, it's a.
Otherwise, it's a+a*(b-1).
What's a*(b-1)? See the above question for a way to work it out.
Actually you use recursion to reduce the complexity of your problem at hand. You apply recursion until you reach a simple base case that can be solved easily. With this you can solve the last recursive step. And with this all other recursive steps up to your original problem.
I think this very simple method should help you understand recursion. The method will call itself until a certain condition is true and then return:
function writeNumbers( aNumber ){
write(aNumber);
if( aNumber > 0 ){
writeNumbers( aNumber - 1 );
}
else{
return;
}
}
This function will print out all numbers from the first number you'll feed it till 0. Thus:
writeNumbers( 10 );
//This wil write: 10 9 8 7 6 5 4 3 2 1 0
//and then stop because aNumber is no longer larger then 0
What bassicly happens is that writeNumbers(10) will write 10 and then call writeNumbers(9) which will write 9 and then call writeNumber(8) etc. Until writeNumbers(1) writes 1 and then calls writeNumbers(0) which will write 0 butt will not call writeNumbers(-1);
This code is essentially the same as:
for(i=10; i>0; i--){
write(i);
}
Then why use recursion you might ask, if a for-loop does essentially the same. Well you mostly use recursion when you would have to nest for loops but won't know how deep they are nested. For example when printing out items from nested arrays:
var nestedArray = Array('Im a string',
Array('Im a string nested in an array', 'me too!'),
'Im a string again',
Array('More nesting!',
Array('nested even more!')
),
'Im the last string');
function printArrayItems( stringOrArray ){
if(typeof stringOrArray === 'Array'){
for(i=0; i<stringOrArray.length; i++){
printArrayItems( stringOrArray[i] );
}
}
else{
write( stringOrArray );
}
}
printArrayItems( stringOrArray );
//this will write:
//'Im a string' 'Im a string nested in an array' 'me too' 'Im a string again'
//'More nesting' 'Nested even more' 'Im the last string'
This function could take an array which could be nested into a 100 levels, while you writing a for loop would then require you to nest it 100 times:
for(i=0; i<nestedArray.length; i++){
if(typeof nestedArray[i] == 'Array'){
for(a=0; i<nestedArray[i].length; a++){
if(typeof nestedArray[i][a] == 'Array'){
for(b=0; b<nestedArray[i][a].length; b++){
//This would be enough for the nestedAaray we have now, but you would have
//to nest the for loops even more if you would nest the array another level
write( nestedArray[i][a][b] );
}//end for b
}//endif typeod nestedArray[i][a] == 'Array'
else{ write( nestedArray[i][a] ); }
}//end for a
}//endif typeod nestedArray[i] == 'Array'
else{ write( nestedArray[i] ); }
}//end for i
As you can see the recursive method is a lot better.
I'll try to explain it with an example.
You know what n! means? If not: http://en.wikipedia.org/wiki/Factorial
3! = 1 * 2 * 3 = 6
here goes some pseudocode
function factorial(n) {
if (n==0) return 1
else return (n * factorial(n-1))
}
So let's try it:
factorial(3)
is n 0?
no!
so we dig deeper with our recursion:
3 * factorial(3-1)
3-1 = 2
is 2 == 0?
no!
so we go deeper!
3 * 2 * factorial(2-1)
2-1 = 1
is 1 == 0?
no!
so we go deeper!
3 * 2 * 1 * factorial(1-1)
1-1 = 0
is 0 == 0?
yes!
we have a trivial case
so we have
3 * 2 * 1 * 1 = 6
i hope the helped you
Recursion
Method A, calls Method A calls Method A. Eventually one of these method A's won't call and exit, but it's recursion because something calls itself.
Example of recursion where I want to print out every folder name on the hard drive: (in c#)
public void PrintFolderNames(DirectoryInfo directory)
{
Console.WriteLine(directory.Name);
DirectoryInfo[] children = directory.GetDirectories();
foreach(var child in children)
{
PrintFolderNames(child); // See we call ourself here...
}
}
A recursive function is simply a function that calls itself as many times as it needs to do so. It's useful if you need to process something multiple times, but you're unsure how many times will actually be required. In a way, you could think of a recursive function as a type of loop. Like a loop, however, you'll need to specify conditions for the process to be broken otherwise it'll become infinite.
Which book are you using?
The standard textbook on algorithms that is actually good is Cormen & Rivest. My experience is that it teaches recursion quite well.
Recursion is one of the harder parts of programming to grasp, and while it does require instinct, it can be learned. But it does need a good description, good examples, and good illustrations.
Also, 30 pages in general is a lot, 30 pages in a single programming language is confusing. Don't try to learn recursion in C or Java, before you understand recursion in general from a general book.
http://javabat.com is a fun and exciting place to practice recursion. Their examples start fairly light and work through extensive (if you want to take it that far). Note: Their approach is learn by practicing. Here is a recursive function that I wrote to simply replace a for loop.
The for loop:
public printBar(length)
{
String holder = "";
for (int index = 0; i < length; i++)
{
holder += "*"
}
return holder;
}
Here is the recursion to do the same thing. (notice we overload the first method to make sure it is used just like above). We also have another method to maintain our index (similar to the way the for statement does it for you above). The recursive function must maintain their own index.
public String printBar(int Length) // Method, to call the recursive function
{
printBar(length, 0);
}
public String printBar(int length, int index) //Overloaded recursive method
{
// To get a better idea of how this works without a for loop
// you can also replace this if/else with the for loop and
// operationally, it should do the same thing.
if (index >= length)
return "";
else
return "*" + printBar(length, index + 1); // Make recursive call
}
To make a long story short, recursion is a good way to write less code. In the latter printBar notice that we have an if statement. IF our condition has been reached, we will exit the recursion and return to the previous method, which returns to the previous method, etc. If I sent in a printBar(8), I get ********. I am hoping that with an example of a simple function that does the same thing as a for loop that maybe this will help. You can practice this more at Java Bat though.
The truly mathematical way to look at building a recursive function would be as follows:
1: Imagine you have a function that is correct for f(n-1), build f such that f(n) is correct.
2: Build f, such that f(1) is correct.
This is how you can prove that the function is correct, mathematically, and it's called Induction. It is equivalent to have different base cases, or more complicated functions on multiple variables). It is also equivalent to imagine that f(x) is correct for all x
Now for a "simple" example. Build a function that can determine if it is possible to have a coin combination of 5 cents and 7 cents to make x cents. For example, it's possible to have 17 cents by 2x5 + 1x7, but impossible to have 16 cents.
Now imagine you have a function that tells you if it's possible to create x cents, as long as x < n. Call this function can_create_coins_small. It should be fairly simple to imagine how to make the function for n. Now build your function:
bool can_create_coins(int n)
{
if (n >= 7 && can_create_coins_small(n-7))
return true;
else if (n >= 5 && can_create_coins_small(n-5))
return true;
else
return false;
}
The trick here is to realize that the fact that can_create_coins works for n, means that you can substitute can_create_coins for can_create_coins_small, giving:
bool can_create_coins(int n)
{
if (n >= 7 && can_create_coins(n-7))
return true;
else if (n >= 5 && can_create_coins(n-5))
return true;
else
return false;
}
One last thing to do is to have a base case to stop infinite recursion. Note that if you are trying to create 0 cents, then that is possible by having no coins. Adding this condition gives:
bool can_create_coins(int n)
{
if (n == 0)
return true;
else if (n >= 7 && can_create_coins(n-7))
return true;
else if (n >= 5 && can_create_coins(n-5))
return true;
else
return false;
}
It can be proven that this function will always return, using a method called infinite descent, but that isn't necessary here. You can imagine that f(n) only calls lower values of n, and will always eventually reach 0.
To use this information to solve your Tower of Hanoi problem, I think the trick is to assume you have a function to move n-1 tablets from a to b (for any a/b), trying to move n tables from a to b.
Simple recursive example in Common Lisp:
MYMAP applies a function to each element in a list.
1) an empty list has no element, so we return the empty list - () and NIL both are the empty list.
2) apply the function to the first list, call MYMAP for the rest of the list (the recursive call) and combine both results into a new list.
(DEFUN MYMAP (FUNCTION LIST)
(IF (NULL LIST)
()
(CONS (FUNCALL FUNCTION (FIRST LIST))
(MYMAP FUNCTION (REST LIST)))))
Let's watch the traced execution. On ENTERing a function, the arguments are printed. On EXITing a function, the result is printed. For each recursive call, the output will be indented on level.
This example calls the SIN function on each number in a list (1 2 3 4).
Command: (mymap 'sin '(1 2 3 4))
1 Enter MYMAP SIN (1 2 3 4)
| 2 Enter MYMAP SIN (2 3 4)
| 3 Enter MYMAP SIN (3 4)
| | 4 Enter MYMAP SIN (4)
| | 5 Enter MYMAP SIN NIL
| | 5 Exit MYMAP NIL
| | 4 Exit MYMAP (-0.75680256)
| 3 Exit MYMAP (0.14112002 -0.75680256)
| 2 Exit MYMAP (0.9092975 0.14112002 -0.75680256)
1 Exit MYMAP (0.841471 0.9092975 0.14112002 -0.75680256)
This is our result:
(0.841471 0.9092975 0.14112002 -0.75680256)
To explain recursion to a six-year-old, first explain it to a five-year-old, and then wait a year.
Actually, this is a useful counter-example, because your recursive call should be simpler, not harder. It would be even harder to explain recursion to a five-year old, and though you could stop the recursion at 0, you have no simple solution for explaining recursion to a zero-year-old.
To solve a problem using recursion, first sub-divide it into one or more simpler problems that you can solve in the same way, and then when the problem is simple enough to solve without further recursion, you can return back up to higher levels.
In fact, that was a recursive definition of how to solve a problem with recursion.
Children implicitly use recursion, for instance:
Road trip to Disney World
Are we there yet?(no)
Are we there yet?(Soon)
Are we there yet?(Almost...)
Are we there yet?(SHHHH)
Are we there yet?(!!!!!)
At which point the child falls asleep...
This countdown function is a simple example:
function countdown()
{
return (arguments[0] > 0 ?
(
console.log(arguments[0]),countdown(arguments[0] - 1)) :
"done"
);
}
countdown(10);
Hofstadter's Law applied to software projects is also relevant.
The essence of human language is, according to Chomsky, the ability of finite brains to produce what he considers to be infinite grammars. By this he means not only that there is no upper limit on what we can say, but that there is no upper limit on the number of sentences our language has, there's no upper limit on the size of any particular sentence. Chomsky has claimed that the fundamental tool that underlies all of this creativity of human language is recursion: the ability for one phrase to reoccur inside another phrase of the same type. If I say "John's brother's house", I have a noun, "house", which occurs in a noun phrase, "brother's house", and that noun phrase occurs in another noun phrase, "John's brother's house". This makes a lot of sense, and it's an interesting property of human language.
References
Recursion and Human Thought
When working with recursive solutions, I always try to:
Establish the base case first i.e.
when n = 1 in a solution to factorial
Try to come up with a general rule
for every other case
Also there are different types of recursive solutions, there's the divide and conquer approach which is useful for fractals and many others.
It would also help if you could work on simpler problems first just to get the hang of it. Some examples are solving for the factorial and generating the nth fibonacci number.
For references, I highly recommend Algorithms by Robert Sedgewick.
Hope that helps. Good luck.
A recursive function is like a spring you compress a bit on each call. On each step, you put a bit of information (current context) on a stack. When the final step is reached, the spring is released, collecting all values (contexts) at once!
Not sure this metaphor is effective... :-)
Anyway, beyond the classical examples (factorial which is the worst example since it is inefficient and easily flattened, Fibonacci, Hanoi...) which are a bit artificial (I rarely, if ever, use them in real programming cases), it is interesting to see where it is really used.
A very common case is to walk a tree (or a graph, but trees are more common, in general).
For example, a folder hierarchy: to list the files, you iterate on them. If you find a sub-directory, the function listing the files call itself with the new folder as argument. When coming back from listing this new folder (and its sub-folders!), it resumes its context, to the next file (or folder).
Another concrete case is when drawing a hierarchy of GUI components: it is common to have containers, like panes, to hold components which can be panes too, or compound components, etc. The painting routine calls recursively the paint function of each component, which calls the paint function of all the components it holds, etc.
Not sure if I am very clear, but I like to show real world use of teaching material, as it was something I was stumbling upon in the past.
Ouch. I tried to figure out the Towers of Hanoi last year. The tricky thing about TOH is it's not a simple example of recursion - you have nested recursions which also change the roles of towers on each call. The only way I could get it to make sense was to literally visualize the movement of the rings in my mind's eye, and verbalize what the recursive call would be. I would start with a single ring, then two, then three. I actually ordered the game on the internet. It took me maybe two or three days of cracking my brains to get it.
Think a worker bee. It tries to make honey. It does its job and expects other worker bees to make rest of the honey. And when the honeycomb is full, it stops.
Think it as magic. You have a function that has the same name with the one you are trying to implement and when you give it the subproblem, it solves it for you and the only thing you need to do is to integrate the solution of your part with the solution it gave you.
For example, we want to calculate the length of a list. Lets call our function magical_length and our magical helper with magical_length
We know that if we give the sublist which does not have the first element, it will give us the length of the sublist by magic. Then only thing we need to think is how to integrate this information with our job. The length of the first element is 1 and magic_counter gives us the length of sublist n-1, therefore total length is (n-1) + 1 -> n
int magical_length( list )
sublist = rest_of_the_list( list )
sublist_length = magical_length( sublist ) // you can think this function as magical and given to you
return 1 + sublist_length
However this answer is incomplete because we didn't consider what happens if we give an empty list. We thought that the list we have always has at least one element. Therefore we need to think about what should be the answer if we are given an empty list and answer is obviously 0. So add this information to our function and this is called base/edge condition.
int magical_length( list )
if ( list is empty) then
return 0
else
sublist_length = magical_length( sublist ) // you can think this function as magical and given to you
return 1 + sublist_length
Is there a performance hit if we use a loop instead of recursion or vice versa in algorithms where both can serve the same purpose? Eg: Check if the given string is a palindrome.
I have seen many programmers using recursion as a means to show off when a simple iteration algorithm can fit the bill.
Does the compiler play a vital role in deciding what to use?
Loops may achieve a performance gain for your program. Recursion may achieve a performance gain for your programmer. Choose which is more important in your situation!
It is possible that recursion will be more expensive, depending on if the recursive function is tail recursive (the last line is recursive call). Tail recursion should be recognized by the compiler and optimized to its iterative counterpart (while maintaining the concise, clear implementation you have in your code).
I would write the algorithm in the way that makes the most sense and is the clearest for the poor sucker (be it yourself or someone else) that has to maintain the code in a few months or years. If you run into performance issues, then profile your code, and then and only then look into optimizing by moving over to an iterative implementation. You may want to look into memoization and dynamic programming.
Comparing recursion to iteration is like comparing a phillips head screwdriver to a flat head screwdriver. For the most part you could remove any phillips head screw with a flat head, but it would just be easier if you used the screwdriver designed for that screw right?
Some algorithms just lend themselves to recursion because of the way they are designed (Fibonacci sequences, traversing a tree like structure, etc.). Recursion makes the algorithm more succinct and easier to understand (therefore shareable and reusable).
Also, some recursive algorithms use "Lazy Evaluation" which makes them more efficient than their iterative brothers. This means that they only do the expensive calculations at the time they are needed rather than each time the loop runs.
That should be enough to get you started. I'll dig up some articles and examples for you too.
Link 1: Haskel vs PHP (Recursion vs Iteration)
Here is an example where the programmer had to process a large data set using PHP. He shows how easy it would have been to deal with in Haskel using recursion, but since PHP had no easy way to accomplish the same method, he was forced to use iteration to get the result.
http://blog.webspecies.co.uk/2011-05-31/lazy-evaluation-with-php.html
Link 2: Mastering Recursion
Most of recursion's bad reputation comes from the high costs and inefficiency in imperative languages. The author of this article talks about how to optimize recursive algorithms to make them faster and more efficient. He also goes over how to convert a traditional loop into a recursive function and the benefits of using tail-end recursion. His closing words really summed up some of my key points I think:
"recursive programming gives the programmer a better way of organizing
code in a way that is both maintainable and logically consistent."
https://developer.ibm.com/articles/l-recurs/
Link 3: Is recursion ever faster than looping? (Answer)
Here is a link to an answer for a stackoverflow question that is similar to yours. The author points out that a lot of the benchmarks associated with either recursing or looping are very language specific. Imperative languages are typically faster using a loop and slower with recursion and vice-versa for functional languages. I guess the main point to take from this link is that it is very difficult to answer the question in a language agnostic / situation blind sense.
Is recursion ever faster than looping?
Recursion is more costly in memory, as each recursive call generally requires a memory address to be pushed to the stack - so that later the program could return to that point.
Still, there are many cases in which recursion is a lot more natural and readable than loops - like when working with trees. In these cases I would recommend sticking to recursion.
Typically, one would expect the performance penalty to lie in the other direction. Recursive calls can lead to the construction of extra stack frames; the penalty for this varies. Also, in some languages like Python (more correctly, in some implementations of some languages...), you can run into stack limits rather easily for tasks you might specify recursively, such as finding the maximum value in a tree data structure. In these cases, you really want to stick with loops.
Writing good recursive functions can reduce the performance penalty somewhat, assuming you have a compiler that optimizes tail recursions, etc. (Also double check to make sure that the function really is tail recursive---it's one of those things that many people make mistakes on.)
Apart from "edge" cases (high performance computing, very large recursion depth, etc.), it's preferable to adopt the approach that most clearly expresses your intent, is well-designed, and is maintainable. Optimize only after identifying a need.
Recursion is better than iteration for problems that can be broken down into multiple, smaller pieces.
For example, to make a recursive Fibonnaci algorithm, you break down fib(n) into fib(n-1) and fib(n-2) and compute both parts. Iteration only allows you to repeat a single function over and over again.
However, Fibonacci is actually a broken example and I think iteration is actually more efficient. Notice that fib(n) = fib(n-1) + fib(n-2) and fib(n-1) = fib(n-2) + fib(n-3). fib(n-1) gets calculated twice!
A better example is a recursive algorithm for a tree. The problem of analyzing the parent node can be broken down into multiple smaller problems of analyzing each child node. Unlike the Fibonacci example, the smaller problems are independent of each other.
So yeah - recursion is better than iteration for problems that can be broken down into multiple, smaller, independent, similar problems.
Your performance deteriorates when using recursion because calling a method, in any language, implies a lot of preparation: the calling code posts a return address, call parameters, some other context information such as processor registers might be saved somewhere, and at return time the called method posts a return value which is then retrieved by the caller, and any context information that was previously saved will be restored. the performance diff between an iterative and a recursive approach lies in the time these operations take.
From an implementation point of view, you really start noticing the difference when the time it takes to handle the calling context is comparable to the time it takes for your method to execute. If your recursive method takes longer to execute then the calling context management part, go the recursive way as the code is generally more readable and easy to understand and you won't notice the performance loss. Otherwise go iterative for efficiency reasons.
I believe tail recursion in java is not currently optimized. The details are sprinkled throughout this discussion on LtU and the associated links. It may be a feature in the upcoming version 7, but apparently it presents certain difficulties when combined with Stack Inspection since certain frames would be missing. Stack Inspection has been used to implement their fine-grained security model since Java 2.
http://lambda-the-ultimate.org/node/1333
There are many cases where it gives a much more elegant solution over the iterative method, the common example being traversal of a binary tree, so it isn't necessarily more difficult to maintain. In general, iterative versions are usually a bit faster (and during optimization may well replace a recursive version), but recursive versions are simpler to comprehend and implement correctly.
Recursion is very useful is some situations. For example consider the code for finding the factorial
int factorial ( int input )
{
int x, fact = 1;
for ( x = input; x > 1; x--)
fact *= x;
return fact;
}
Now consider it by using the recursive function
int factorial ( int input )
{
if (input == 0)
{
return 1;
}
return input * factorial(input - 1);
}
By observing these two, we can see that recursion is easy to understand.
But if it is not used with care it can be so much error prone too.
Suppose if we miss if (input == 0), then the code will be executed for some time and ends with usually a stack overflow.
In many cases recursion is faster because of caching, which improves performance. For example, here is an iterative version of merge sort using the traditional merge routine. It will run slower than the recursive implementation because of caching improved performances.
Iterative implementation
public static void sort(Comparable[] a)
{
int N = a.length;
aux = new Comparable[N];
for (int sz = 1; sz < N; sz = sz+sz)
for (int lo = 0; lo < N-sz; lo += sz+sz)
merge(a, lo, lo+sz-1, Math.min(lo+sz+sz-1, N-1));
}
Recursive implementation
private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi)
{
if (hi <= lo) return;
int mid = lo + (hi - lo) / 2;
sort(a, aux, lo, mid);
sort(a, aux, mid+1, hi);
merge(a, aux, lo, mid, hi);
}
PS - this is what was told by Professor Kevin Wayne (Princeton University) on the course on algorithms presented on Coursera.
Using recursion, you're incurring the cost of a function call with each "iteration", whereas with a loop, the only thing you usually pay is an increment/decrement. So, if the code for the loop isn't much more complicated than the code for the recursive solution, loop will usually be superior to recursion.
Recursion and iteration depends on the business logic that you want to implement, though in most of the cases it can be used interchangeably. Most developers go for recursion because it is easier to understand.
It depends on the language. In Java you should use loops. Functional languages optimize recursion.
Recursion has a disadvantage that the algorithm that you write using recursion has O(n) space complexity.
While iterative aproach have a space complexity of O(1).This is the advantange of using iteration over recursion.
Then why do we use recursion?
See below.
Sometimes it is easier to write an algorithm using recursion while it's slightly tougher to write the same algorithm using iteration.In this case if you opt to follow the iteration approach you would have to handle stack yourself.
If you're just iterating over a list, then sure, iterate away.
A couple of other answers have mentioned (depth-first) tree traversal. It really is such a great example, because it's a very common thing to do to a very common data structure. Recursion is extremely intuitive for this problem.
Check out the "find" methods here:
http://penguin.ewu.edu/cscd300/Topic/BSTintro/index.html
Recursion is more simple (and thus - more fundamental) than any possible definition of an iteration. You can define a Turing-complete system with only a pair of combinators (yes, even a recursion itself is a derivative notion in such a system). Lambda calculus is an equally powerful fundamental system, featuring recursive functions. But if you want to define an iteration properly, you'd need much more primitives to start with.
As for the code - no, recursive code is in fact much easier to understand and to maintain than a purely iterative one, since most data structures are recursive. Of course, in order to get it right one would need a language with a support for high order functions and closures, at least - to get all the standard combinators and iterators in a neat way. In C++, of course, complicated recursive solutions can look a bit ugly, unless you're a hardcore user of FC++ and alike.
I would think in (non tail) recursion there would be a performance hit for allocating a new stack etc every time the function is called (dependent on language of course).
it depends on "recursion depth".
it depends on how much the function call overhead will influence the total execution time.
For example, calculating the classical factorial in a recursive way is very inefficient due to:
- risk of data overflowing
- risk of stack overflowing
- function call overhead occupy 80% of execution time
while developing a min-max algorithm for position analysis in the game of chess that will analyze subsequent N moves can be implemented in recursion over the "analysis depth" (as I'm doing ^_^)
Recursion? Where do I start, wiki will tell you “it’s the process of repeating items in a self-similar way"
Back in day when I was doing C, C++ recursion was a god send, stuff like "Tail recursion". You'll also find many sorting algorithms use recursion. Quick sort example: http://alienryderflex.com/quicksort/
Recursion is like any other algorithm useful for a specific problem. Perhaps you mightn't find a use straight away or often but there will be problem you’ll be glad it’s available.
In C++ if the recursive function is a templated one, then the compiler has more chance to optimize it, as all the type deduction and function instantiations will occur in compile time. Modern compilers can also inline the function if possible. So if one uses optimization flags like -O3 or -O2 in g++, then recursions may have the chance to be faster than iterations. In iterative codes, the compiler gets less chance to optimize it, as it is already in the more or less optimal state (if written well enough).
In my case, I was trying to implement matrix exponentiation by squaring using Armadillo matrix objects, in both recursive and iterative way. The algorithm can be found here... https://en.wikipedia.org/wiki/Exponentiation_by_squaring.
My functions were templated and I have calculated 1,000,000 12x12 matrices raised to the power 10. I got the following result:
iterative + optimisation flag -O3 -> 2.79.. sec
recursive + optimisation flag -O3 -> 1.32.. sec
iterative + No-optimisation flag -> 2.83.. sec
recursive + No-optimisation flag -> 4.15.. sec
These results have been obtained using gcc-4.8 with c++11 flag (-std=c++11) and Armadillo 6.1 with Intel mkl. Intel compiler also shows similar results.
Mike is correct. Tail recursion is not optimized out by the Java compiler or the JVM. You will always get a stack overflow with something like this:
int count(int i) {
return i >= 100000000 ? i : count(i+1);
}
You have to keep in mind that utilizing too deep recursion you will run into Stack Overflow, depending on allowed stack size. To prevent this make sure to provide some base case which ends you recursion.
Using just Chrome 45.0.2454.85 m, recursion seems to be a nice amount faster.
Here is the code:
(function recursionVsForLoop(global) {
"use strict";
// Perf test
function perfTest() {}
perfTest.prototype.do = function(ns, fn) {
console.time(ns);
fn();
console.timeEnd(ns);
};
// Recursion method
(function recur() {
var count = 0;
global.recurFn = function recurFn(fn, cycles) {
fn();
count = count + 1;
if (count !== cycles) recurFn(fn, cycles);
};
})();
// Looped method
function loopFn(fn, cycles) {
for (var i = 0; i < cycles; i++) {
fn();
}
}
// Tests
var curTest = new perfTest(),
testsToRun = 100;
curTest.do('recursion', function() {
recurFn(function() {
console.log('a recur run.');
}, testsToRun);
});
curTest.do('loop', function() {
loopFn(function() {
console.log('a loop run.');
}, testsToRun);
});
})(window);
RESULTS
// 100 runs using standard for loop
100x for loop run.
Time to complete: 7.683ms
// 100 runs using functional recursive approach w/ tail recursion
100x recursion run.
Time to complete: 4.841ms
In the screenshot below, recursion wins again by a bigger margin when run at 300 cycles per test
If the iterations are atomic and orders of magnitude more expensive than pushing a new stack frame and creating a new thread and you have multiple cores and your runtime environment can use all of them, then a recursive approach could yield a huge performance boost when combined with multithreading. If the average number of iterations is not predictable then it might be a good idea to use a thread pool which will control thread allocation and prevent your process from creating too many threads and hogging the system.
For example, in some languages, there are recursive multithreaded merge sort implementations.
But again, multithreading can be used with looping rather than recursion, so how well this combination will work depends on more factors including the OS and its thread allocation mechanism.
I found another differences between those approaches.
It looks simple and unimportant, but it has a very important role while you prepare for interviews and this subject arises, so look closely.
In short:
1) iterative post-order traversal is not easy - that makes DFT more complex
2) cycles check easier with recursion
Details:
In the recursive case, it is easy to create pre and post traversals:
Imagine a pretty standard question: "print all tasks that should be executed to execute the task 5, when tasks depend on other tasks"
Example:
//key-task, value-list of tasks the key task depends on
//"adjacency map":
Map<Integer, List<Integer>> tasksMap = new HashMap<>();
tasksMap.put(0, new ArrayList<>());
tasksMap.put(1, new ArrayList<>());
List<Integer> t2 = new ArrayList<>();
t2.add(0);
t2.add(1);
tasksMap.put(2, t2);
List<Integer> t3 = new ArrayList<>();
t3.add(2);
t3.add(10);
tasksMap.put(3, t3);
List<Integer> t4 = new ArrayList<>();
t4.add(3);
tasksMap.put(4, t4);
List<Integer> t5 = new ArrayList<>();
t5.add(3);
tasksMap.put(5, t5);
tasksMap.put(6, new ArrayList<>());
tasksMap.put(7, new ArrayList<>());
List<Integer> t8 = new ArrayList<>();
t8.add(5);
tasksMap.put(8, t8);
List<Integer> t9 = new ArrayList<>();
t9.add(4);
tasksMap.put(9, t9);
tasksMap.put(10, new ArrayList<>());
//task to analyze:
int task = 5;
List<Integer> res11 = getTasksInOrderDftReqPostOrder(tasksMap, task);
System.out.println(res11);**//note, no reverse required**
List<Integer> res12 = getTasksInOrderDftReqPreOrder(tasksMap, task);
Collections.reverse(res12);//note reverse!
System.out.println(res12);
private static List<Integer> getTasksInOrderDftReqPreOrder(Map<Integer, List<Integer>> tasksMap, int task) {
List<Integer> result = new ArrayList<>();
Set<Integer> visited = new HashSet<>();
reqPreOrder(tasksMap,task,result, visited);
return result;
}
private static void reqPreOrder(Map<Integer, List<Integer>> tasksMap, int task, List<Integer> result, Set<Integer> visited) {
if(!visited.contains(task)) {
visited.add(task);
result.add(task);//pre order!
List<Integer> children = tasksMap.get(task);
if (children != null && children.size() > 0) {
for (Integer child : children) {
reqPreOrder(tasksMap,child,result, visited);
}
}
}
}
private static List<Integer> getTasksInOrderDftReqPostOrder(Map<Integer, List<Integer>> tasksMap, int task) {
List<Integer> result = new ArrayList<>();
Set<Integer> visited = new HashSet<>();
reqPostOrder(tasksMap,task,result, visited);
return result;
}
private static void reqPostOrder(Map<Integer, List<Integer>> tasksMap, int task, List<Integer> result, Set<Integer> visited) {
if(!visited.contains(task)) {
visited.add(task);
List<Integer> children = tasksMap.get(task);
if (children != null && children.size() > 0) {
for (Integer child : children) {
reqPostOrder(tasksMap,child,result, visited);
}
}
result.add(task);//post order!
}
}
Note that the recursive post-order-traversal does not require a subsequent reversal of the result. Children printed first and your task in the question printed last. Everything is fine. You can do a recursive pre-order-traversal (also shown above) and that one will require a reversal of the result list.
Not that simple with iterative approach! In iterative (one stack) approach you can only do a pre-ordering-traversal, so you obliged to reverse the result array at the end:
List<Integer> res1 = getTasksInOrderDftStack(tasksMap, task);
Collections.reverse(res1);//note reverse!
System.out.println(res1);
private static List<Integer> getTasksInOrderDftStack(Map<Integer, List<Integer>> tasksMap, int task) {
List<Integer> result = new ArrayList<>();
Set<Integer> visited = new HashSet<>();
Stack<Integer> st = new Stack<>();
st.add(task);
visited.add(task);
while(!st.isEmpty()){
Integer node = st.pop();
List<Integer> children = tasksMap.get(node);
result.add(node);
if(children!=null && children.size() > 0){
for(Integer child:children){
if(!visited.contains(child)){
st.add(child);
visited.add(child);
}
}
}
//If you put it here - it does not matter - it is anyway a pre-order
//result.add(node);
}
return result;
}
Looks simple, no?
But it is a trap in some interviews.
It means the following: with the recursive approach, you can implement Depth First Traversal and then select what order you need pre or post(simply by changing the location of the "print", in our case of the "adding to the result list"). With the iterative (one stack) approach you can easily do only pre-order traversal and so in the situation when children need be printed first(pretty much all situations when you need start print from the bottom nodes, going upwards) - you are in the trouble. If you have that trouble you can reverse later, but it will be an addition to your algorithm. And if an interviewer is looking at his watch it may be a problem for you. There are complex ways to do an iterative post-order traversal, they exist, but they are not simple. Example:https://www.geeksforgeeks.org/iterative-postorder-traversal-using-stack/
Thus, the bottom line: I would use recursion during interviews, it is simpler to manage and to explain. You have an easy way to go from pre to post-order traversal in any urgent case. With iterative you are not that flexible.
I would use recursion and then tell: "Ok, but iterative can provide me more direct control on used memory, I can easily measure the stack size and disallow some dangerous overflow.."
Another plus of recursion - it is simpler to avoid / notice cycles in a graph.
Example (preudocode):
dft(n){
mark(n)
for(child: n.children){
if(marked(child))
explode - cycle found!!!
dft(child)
}
unmark(n)
}
It may be fun to write it as recursion, or as a practice.
However, if the code is to be used in production, you need to consider the possibility of stack overflow.
Tail recursion optimization can eliminate stack overflow, but do you want to go through the trouble of making it so, and you need to know you can count on it having the optimization in your environment.
Every time the algorithm recurses, how much is the data size or n reduced by?
If you are reducing the size of data or n by half every time you recurse, then in general you don't need to worry about stack overflow. Say, if it needs to be 4,000 level deep or 10,000 level deep for the program to stack overflow, then your data size need to be roughly 24000 for your program to stack overflow. To put that into perspective, a biggest storage device recently can hold 261 bytes, and if you have 261 of such devices, you are only dealing with 2122 data size. If you are looking at all the atoms in the universe, it is estimated that it may be less than 284. If you need to deal with all the data in the universe and their states for every millisecond since the birth of the universe estimated to be 14 billion years ago, it may only be 2153. So if your program can handle 24000 units of data or n, you can handle all data in the universe and the program will not stack overflow. If you don't need to deal with numbers that are as big as 24000 (a 4000-bit integer), then in general you don't need to worry about stack overflow.
However, if you reduce the size of data or n by a constant amount every time you recurse, then you can run into stack overflow when n becomes merely 20000. That is, the program runs well when n is 1000, and you think the program is good, and then the program stack overflows when some time in the future, when n is 5000 or 20000.
So if you have a possibility of stack overflow, try to make it an iterative solution.
As far as I know, Perl does not optimize tail-recursive calls, but you can fake it.
sub f{
my($l,$r) = #_;
if( $l >= $r ){
return $l;
} else {
# return f( $l+1, $r );
#_ = ( $l+1, $r );
goto &f;
}
}
When first called it will allocate space on the stack. Then it will change its arguments, and restart the subroutine, without adding anything more to the stack. It will therefore pretend that it never called its self, changing it into an iterative process.
Note that there is no "my #_;" or "local #_;", if you did it would no longer work.
"Is there a performance hit if we use a loop instead of
recursion or vice versa in algorithms where both can serve the same purpose?"
Usually yes if you are writing in a imperative language iteration will run faster than recursion, the performance hit is minimized in problems where the iterative solution requires manipulating Stacks and popping items off of a stack due to the recursive nature of the problem. There are a lot of times where the recursive implementation is much easier to read because the code is much shorter,
so you do want to consider maintainability. Especailly in cases where the problem has a recursive nature. So take for example:
The recursive implementation of Tower of Hanoi:
def TowerOfHanoi(n , source, destination, auxiliary):
if n==1:
print ("Move disk 1 from source",source,"to destination",destination)
return
TowerOfHanoi(n-1, source, auxiliary, destination)
print ("Move disk",n,"from source",source,"to destination",destination)
TowerOfHanoi(n-1, auxiliary, destination, source)
Fairly short and pretty easy to read. Compare this with its Counterpart iterative TowerOfHanoi:
# Python3 program for iterative Tower of Hanoi
import sys
# A structure to represent a stack
class Stack:
# Constructor to set the data of
# the newly created tree node
def __init__(self, capacity):
self.capacity = capacity
self.top = -1
self.array = [0]*capacity
# function to create a stack of given capacity.
def createStack(capacity):
stack = Stack(capacity)
return stack
# Stack is full when top is equal to the last index
def isFull(stack):
return (stack.top == (stack.capacity - 1))
# Stack is empty when top is equal to -1
def isEmpty(stack):
return (stack.top == -1)
# Function to add an item to stack.
# It increases top by 1
def push(stack, item):
if(isFull(stack)):
return
stack.top+=1
stack.array[stack.top] = item
# Function to remove an item from stack.
# It decreases top by 1
def Pop(stack):
if(isEmpty(stack)):
return -sys.maxsize
Top = stack.top
stack.top-=1
return stack.array[Top]
# Function to implement legal
# movement between two poles
def moveDisksBetweenTwoPoles(src, dest, s, d):
pole1TopDisk = Pop(src)
pole2TopDisk = Pop(dest)
# When pole 1 is empty
if (pole1TopDisk == -sys.maxsize):
push(src, pole2TopDisk)
moveDisk(d, s, pole2TopDisk)
# When pole2 pole is empty
else if (pole2TopDisk == -sys.maxsize):
push(dest, pole1TopDisk)
moveDisk(s, d, pole1TopDisk)
# When top disk of pole1 > top disk of pole2
else if (pole1TopDisk > pole2TopDisk):
push(src, pole1TopDisk)
push(src, pole2TopDisk)
moveDisk(d, s, pole2TopDisk)
# When top disk of pole1 < top disk of pole2
else:
push(dest, pole2TopDisk)
push(dest, pole1TopDisk)
moveDisk(s, d, pole1TopDisk)
# Function to show the movement of disks
def moveDisk(fromPeg, toPeg, disk):
print("Move the disk", disk, "from '", fromPeg, "' to '", toPeg, "'")
# Function to implement TOH puzzle
def tohIterative(num_of_disks, src, aux, dest):
s, d, a = 'S', 'D', 'A'
# If number of disks is even, then interchange
# destination pole and auxiliary pole
if (num_of_disks % 2 == 0):
temp = d
d = a
a = temp
total_num_of_moves = int(pow(2, num_of_disks) - 1)
# Larger disks will be pushed first
for i in range(num_of_disks, 0, -1):
push(src, i)
for i in range(1, total_num_of_moves + 1):
if (i % 3 == 1):
moveDisksBetweenTwoPoles(src, dest, s, d)
else if (i % 3 == 2):
moveDisksBetweenTwoPoles(src, aux, s, a)
else if (i % 3 == 0):
moveDisksBetweenTwoPoles(aux, dest, a, d)
# Input: number of disks
num_of_disks = 3
# Create three stacks of size 'num_of_disks'
# to hold the disks
src = createStack(num_of_disks)
dest = createStack(num_of_disks)
aux = createStack(num_of_disks)
tohIterative(num_of_disks, src, aux, dest)
Now the first one is way easier to read because suprise suprise shorter code is usually easier to understand than code that is 10 times longer. Sometimes you want to ask yourself is the extra performance gain really worth it? The amount of hours wasted debugging the code. Is the iterative TowerOfHanoi faster than the Recursive TowerOfHanoi? Probably, but not by a big margin. Would I like to program Recursive problems like TowerOfHanoi using iteration? Hell no. Next we have another recursive function the Ackermann function:
Using recursion:
if m == 0:
# BASE CASE
return n + 1
elif m > 0 and n == 0:
# RECURSIVE CASE
return ackermann(m - 1, 1)
elif m > 0 and n > 0:
# RECURSIVE CASE
return ackermann(m - 1, ackermann(m, n - 1))
Using Iteration:
callStack = [{'m': 2, 'n': 3, 'indentation': 0, 'instrPtr': 'start'}]
returnValue = None
while len(callStack) != 0:
m = callStack[-1]['m']
n = callStack[-1]['n']
indentation = callStack[-1]['indentation']
instrPtr = callStack[-1]['instrPtr']
if instrPtr == 'start':
print('%sackermann(%s, %s)' % (' ' * indentation, m, n))
if m == 0:
# BASE CASE
returnValue = n + 1
callStack.pop()
continue
elif m > 0 and n == 0:
# RECURSIVE CASE
callStack[-1]['instrPtr'] = 'after first recursive case'
callStack.append({'m': m - 1, 'n': 1, 'indentation': indentation + 1, 'instrPtr': 'start'})
continue
elif m > 0 and n > 0:
# RECURSIVE CASE
callStack[-1]['instrPtr'] = 'after second recursive case, inner call'
callStack.append({'m': m, 'n': n - 1, 'indentation': indentation + 1, 'instrPtr': 'start'})
continue
elif instrPtr == 'after first recursive case':
returnValue = returnValue
callStack.pop()
continue
elif instrPtr == 'after second recursive case, inner call':
callStack[-1]['innerCallResult'] = returnValue
callStack[-1]['instrPtr'] = 'after second recursive case, outer call'
callStack.append({'m': m - 1, 'n': returnValue, 'indentation': indentation + 1, 'instrPtr': 'start'})
continue
elif instrPtr == 'after second recursive case, outer call':
returnValue = returnValue
callStack.pop()
continue
print(returnValue)
And once again I will argue that the recursive implementation is much easier to understand. So my conclusion is use recursion if the problem by nature is recursive and requires manipulating items in a stack.
I'm going to answer your question by designing a Haskell data structure by "induction", which is a sort of "dual" to recursion. And then I will show how this duality leads to nice things.
We introduce a type for a simple tree:
data Tree a = Branch (Tree a) (Tree a)
| Leaf a
deriving (Eq)
We can read this definition as saying "A tree is a Branch (which contains two trees) or is a leaf (which contains a data value)". So the leaf is a sort of minimal case. If a tree isn't a leaf, then it must be a compound tree containing two trees. These are the only cases.
Let's make a tree:
example :: Tree Int
example = Branch (Leaf 1)
(Branch (Leaf 2)
(Leaf 3))
Now, let's suppose we want to add 1 to each value in the tree. We can do this by calling:
addOne :: Tree Int -> Tree Int
addOne (Branch a b) = Branch (addOne a) (addOne b)
addOne (Leaf a) = Leaf (a + 1)
First, notice that this is in fact a recursive definition. It takes the data constructors Branch and Leaf as cases (and since Leaf is minimal and these are the only possible cases), we are sure that the function will terminate.
What would it take to write addOne in an iterative style? What will looping into an arbitrary number of branches look like?
Also, this kind of recursion can often be factored out, in terms of a "functor". We can make Trees into Functors by defining:
instance Functor Tree where fmap f (Leaf a) = Leaf (f a)
fmap f (Branch a b) = Branch (fmap f a) (fmap f b)
and defining:
addOne' = fmap (+1)
We can factor out other recursion schemes, such as the catamorphism (or fold) for an algebraic data type. Using a catamorphism, we can write:
addOne'' = cata go where
go (Leaf a) = Leaf (a + 1)
go (Branch a b) = Branch a b