I'm trying to solve a problem that calls for recursive backtracking and my solution produces a stackoverflow error. I understand that this error often indicates a bad termination condition, but my ternimation condition appears correct. Is there anything other than a bad termination condition that would be likely to cause a stackoverflow error? How can I figure out what the problem is?
EDIT: sorry tried to post the code but its too ugly..
As #irreputable says, even if your code has a correct termination condition, it could be that the problem is simply too big for the stack (so that the stack is exhausted before the condition is reached). There is also a third possibility: that your recursion has entered into a loop. For example, in a depth-first search through a graph, if you forget to mark nodes as visited, you'll end up going in circles, revisiting nodes that you have already seen.
How can you determine which of these three situations you are in? Try to make a way to describe the "location" of each recursive call (this will typically involve the function parameters). For instance, if you are writing a graph algorithm where a function calls itself on neighbouring nodes, then the node name or node index is a good description of where the recursive function is. In the top of the recursive function, you can print the description, and then you'll see what the function does, and perhaps you can tell whether it does the right thing or not, or whether it goes in circles. You can also store the descriptions in a HashMap in order to detect whether you have entered a circle.
Instead of using recursion, you could always have a loop which uses a stack. E.g. instead of (pseudo-code):
function sum(n){
if n == 0, return 0
return n + sum(n-1)
}
Use:
function sum(n){
Stack stack
while(n > 0){
stack.push(n)
n--
}
localSum = 0
while(stack not empty){
localSum += stack.pop()
}
return localSum
}
In a nutshell, simulate recursion by saving the state in a local stack.
You can use the -Xss option to give your stack more memory if your problem is too large to fix in the default stack limit size.
As the other fellas already mentioned, there might be few reasons for that:
Your code has problem by nature or in the logic of the recursion. It has to be a stoping condition, base case or termination point for any recursive function.
Your memory is too small to keep the number of recursive calls into the stack. Big Fibonacci numbers might be good example here. Just FYI Fibonacci is as follows (sometimes starts at zero):
1,1,2,3,5,8,13,...
Fn = Fn-1 + Fn-2
F0 = 1, F1 = 1, n>=2
If your code is correct, then the stack is simply too small for your problem. We don't have real Turing machines.
There are two common coding errors that could cause your program to get into an infinite loop (and therefore cause a stack overflow):
Bad termination condition
Bad recursion call
Example:
public static int factorial( int n ){
if( n < n ) // Bad termination condition
return 1;
else
return n*factorial(n+1); // Bad recursion call
}
Otherwise, your program could just be functioning properly and the stack is too small.
Related
I am trying to write an algorithm that determines whether a graph is strongly connected or not. I think my code is almost correct, although I keep getting StackOverFlowError. I personally think because there's a cycle in the graph I'm testing my algorithm with, my code doesn't understand that and comes in a loop. But I'm using an array to see if a node was already visited! So that should not happen! Please help me understand what's wrong with my code. Anyways this is my code:
static void dfs(int src,boolean[] visited,Stack<Integer> stack){
visited[src]=true;
for(Integer i:adj[src]){
if(!visited[i]){
dfs(i,visited,stack);
}
}
stack.push(src);
}
This is how I called my DFS function from main:
Stack<Integer> stack=new Stack<Integer>();
boolean[] visited=new boolean[n+1];
for(int i=1;i<=n;i++){
if(!visited[i]){
g.dfs(i,visited,stack);
}
}
There are two possible explanations:
There is a loop and your loop detection code isn't working.
The graph is too deep; i.e. your code would work if the stack was larger.
Looking at your code, I think that the second explanation is the correct one.
Example: suppose that your graph is actually a chain of N nodes in a line. To reach the last node in the list you need to make recursive calls N deep. For large enough N, that will cause a stack overflow.
I have been coding a recursive algorithm in order to go through different nodes and analyze all the paths in a directed acyclic graph. The thing is that, after some new data has been introduced to the algorithm I get this message Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError. I have looked through different questions about this and it seems that the error is because of insufficient memory. Could anyone help me to solve this problem?
Here I add a picture of the recursive algorithm:
public boolean checkduration(Node node, double dur, int freq){
boolean outcome=true;
currentPath.add(node);
if((dur>minduration)&&(node.getRep()<=node.getMaxRep())){
ArrayList<Node> clone = (ArrayList<Node>) currentPath.clone();
currentPath2=clone;
failingPaths.add(new ImmutableTriple<Double,Integer, ArrayList<Node>> (dur,freq,currentPath2));
currentPath.remove(node);
return false;
}
node.setRep(node.getRep()+1);
for(int i=0;i<node.getEdge().size();i++){
if(!checkduration(node.getEdge().get(i).previousNode,dur+node.getEdge().get(i).timeRelation, freq+node.getEdge().get(i).frequency)){
outcome=false;
}
}
currentPath.remove(node);
node.setRep(node.getRep()-1);
return outcome;
}
The error seems to be in the condition of (if(!checkduration(node.getEdge().get(i).previousNode,dur+node.getEdge().get(i).timeRelation, freq+node.getEdge().get(i).frequency))) but I do not understand why it works with some data and not always as not so much information has been changed.
Any comments, suggestions would be truly helpful. Thanks to everyone
The StackOverflowError is occurring because you are recursing too many times. And if there are too many recursive calls, that means there is a flaw or incorrect assumption in your terminating condition. Here is your terminating condition:
(dur > minduration) && (node.getRep() <= node.getMaxRep())
Since you have not provided enough information with your question for us to analyze your graph or nodes further, I would suggest you take a closer look at this terminating condition, and make sure that every traversal of the graph will eventually fulfill this condition.
The use of the debugger can also help you to step through the traversal and see where the cycle is occurring, and why it fails to satisfy the terminal condition.
Your graph traversal seems inefficient, since you are counting repetitions per each node, how many times you have visited and you limit your effort by adding some maxRep
All you need is the set of visited nodes, rather than increment of a counter for each node, when you visit it.
Does you DAG have many roots? Is the lack of cycles guaranteed, or do you need to detect cycles?
Please also note that you don't need cloning of paths.
I saw this code snippet from my exam, and my first hint would be it will throw StackOverFlowError
for (int i = 10; i > 5; i++) {
if(i == 1000) i = 10;
System.out.println(i);
}
It happens to be that its not. From the code sample, can you please explain why this is not going to throw StackOverFlowError.
To have a StackOverflowError, you have to be adding things to the call stack.
You're adding calls to System.out.println, but they simply don't stack on top of one another, so there would only be one call on the stack at any given time.
Now, an example of StackOverflowError would be recursion that does not sufficiently resolve the previous entries on the call stack; something that simply has too many method calls to itself for a sufficiently large parameter, or creates more calls to itself for every call to itself than it can deal with. (The Ackermann function is a notorious example of this.)
If we define factorial as thus:
public long factorial(long value) {
return value == 0 ? 1 : value * factorial(value - 1);
}
...and give it a sufficiently large value...
System.out.println(factorial(1891279172981L));
...then we won't have enough stack space to handle all 1891279172981 of those entries on to it.
This snippet causes an infinite loop, but not an infinite recursion (since you don't have a method calling itself infinite times). Therefore it will not cause a StackOverflowError.
I'm writing a function that will call itself up to about 5000 times. Ofcourse, I get a StackOverflowError. Is there any way that I can rewrite this code in a fairly simple way?:
void checkBlocks(Block b, int amm) {
//Stuff that might issue a return call
Block blockDown = (Block) b.getRelative(BlockFace.DOWN);
if (condition)
checkBlocks(blockDown, amm);
Block blockUp = (Block) b.getRelative(BlockFace.UP);
if (condition)
checkBlocks(blockUp, amm);
//Same code 4 more times for each side
}
By the way, what is the limitation of how deep we may call the functions?
Use an explicit stack of objects and a loop, rather than the call stack and recursion:
void checkBlocks(Block b, int amm) {
Stack<Block> blocks = new Stack<Block>();
blocks.push(b);
while (!blocks.isEmpty()) {
b = blocks.pop();
Block blockDown = (Block) b.getRelative(BlockFace.DOWN);
if (condition)
blocks.push(block);
Block blockUp = (Block) b.getRelative(BlockFace.UP);
if (condition)
blocks.push(block);
}
}
default stack size in java is 512kb. if you exceed that program will terminate throwing StackOverflowException
you can increase the stack size by passing a JVM argument :
-Xss1024k
now stack size is 1024kb. you may give higher value based on your environment
I don't think we can programmatically change this
You can increase the stack size by using -Xss4m.
You may put your "Block"s into a queue/stack and iterate as long as Blocks are available.
It's obvious that you get StackOverflow with such branching factor of your recursion. In other languages it can be achieved by Tail Call Optimization. But I suppose your problem needs another way to solve.
Ideally, you perform some check on Block. Maybe you can obtain list of all blocks and check each of them iteratively?
In most cases recursion is used in a wrong way. You shouldn't get a stack over flow exception.
Your method has no return type/value.
How do you ensure your initial Block b is valid?
If you are using recursion, answer yourself the following question:
what is my recursion anchor (when do i stop with recursion)
what is my recursion step (how do I reduce my number of calculations)
Example:
n! => n*n-1!
my recursion anchor is n == 2 (result is 2), so I can calculate all results beginnging from this anchor.
my recursion step is n-1 (so each step I get closer to the solution (and in this fact to my recursion anchor))
Trying to perform a binary search on a sorted array of Book objects.
Its not working well, it returns the correct results for some of the objects, but not all.
I went through the loop on paper and it seems that a number can get missed out due to rounding #.5 upwards.
Any ideas how to make this work?
Book found = null;
/*
* Search at the center of the collection. If the reference is less than that,
* search in the upper half of the collection, else, search in the lower half.
* Loop until found else return null.
*/
int top = numberOfBooks()-1;
int bottom = 0;
int middle;
while (bottom <= top && found == null){
middle = (bottom + top)/2;
if (givenRef.compareTo(bookCollection.get(middle).getReference()) == 0) {
found = bookCollection.get(middle);
} else if (givenRef.compareTo(bookCollection.get(middle).getReference()) < 0){
bottom = middle + 1;
} else if (givenRef.compareTo(bookCollection.get(middle).getReference()) > 0){
top = middle - 1;
}
}
return found;
A couple suggestions for you:
there's no need to keep a Book variable. In your loop, just return the book when it's found, and at the end return null. And you can also remove the boolean check for the variable in the while condition.
the middle variable can be scoped inside the loop, no need to have it live longer.
you're doing bookCollection.get(middle).getReference() three times. Consider creating a variable and then using it.
the middle = (bottom + top)/2 is a classic mistake in binary search implementation algorithms. Even Joshua Bloch, who wrote the Java Collection classes, made that error (see this interesting blog post about it). Instead, use (bottom+top) >>> 1, to avoid integer overflow for very large values (you probably wouldn't encounter this error, but it's for the principle).
As for your actual problem statement, rounding would be downwards (integer division), not upwards. To troubleshoot the problem:
are you sure the numberOfBooks() method corresponds to the length of your collection?
are you sure the compareTo() method works as expected for the types you are using (in your code example we do not know what the getReference() return type is)
are you sure your collection is properly sorted according to getReference()?
and finally, are you sure that using givenRef.compareTo(bookCollection.get(middle).getReference()) < 0 is correct? In standard binary search implementations it would be reversed, e.g. bookCollection.get(middle).getReference().compareTo(givenRef) < 0. This might be what donroby mentions, not sure.
In any case, the way to find the error would be to try out different values and see for which the output is correct and for which it isn't, and thus infer what the problem is. You can also use your debugger to help you step through the algorithm, rather than using pencil and paper if you have to run many tests. Even better, as donroby said, write a unit test.
What about Collections.binarySearch()?
All of JRL's suggestions are right, but the actual fail is that your compares are reversed.
I didn't see this immediately myself, but replicating your code into a function (using strings instead of Books), writing a some simple Junit tests and then running them in the debugger made it really obvious.
Write unit tests!
I found the problem.
It turns out i was binary searching my bookCollection arrayList, and NOT the new sroted array i had created - sortedLib.
Silly mistake at my end, but thanks for the input and suggestions!