java complex if statement causes StackOverflowError [duplicate] - java

This question already has answers here:
What is a StackOverflowError?
(16 answers)
Closed 4 years ago.
I made a recursive method to find the least number of rallies that could be played before one or another team wins with given points team should score to win - k, current points of two teams - x and y.
So it looked like
public static int scores(int k, int x, int y, int rally) {
if (x==k || y==k)
return rally;
else {
rally++;
return Math.min(scores(k, x + 1, y, rally), scores(k, x,y+1,rally));
}
}
When I called this method with custom values in main method
scores(5,0,0,0)
It worked fine. But when I changed IF statement to check that the winner has at least two-point margin
if ((x==k || y==k) && Math.abs(x-y)>=2)
The program showed java.lang.StackOverflowError
I am extremely bad at this, please help me

Take note that you never increase k value, that means that if x/y == k and difference isn't 2 points, it will pass on and x/y will never equal k again.
I will imagine that something like this, should work
public static int scores(int k, int x, int y, int rally) {
if ((x>=k || y>=k) && (Math.abs(x-y))>=2)
return rally;
else {
rally++;
return Math.min(scores(k, x + 1, y, rally), scores(k, x,y+1,rally));
}
}
Edit: As pointed out in the comments, there is another issue with this code, that causes SO when opposite players get one point each all the time.
You can fix the stack overflow error that occurs when X and y alternate in "winning" by keeping track of the minimum found so far:
public static int scores(int k, int x, int y, int rally) {
return scores(k, x, y, rally, Integer.MIN_VALUE);
}
public static int scores(int k, int x, int y, int rally, int minSoFar) {
if (rally >= minSoFar || ((x>=k || y>=k) && (Math.abs(x-y))>=2))
return rally;
else {
rally++;
minSoFar = Math.min(minSoFar, scores(k, x+1, y, rally));
minSoFar = Math.min(minSoFar, scores(k, x, y+1, rally));
return minSoFar;
}
}
But it should be noted that the minimum path will always be the one where X always wins (or Y always wins). So:
return Math.max(2, k);
Is a much easier way to express the result.

Related

Assuming initial position is (1, 1), find out if you can reach (x, y) by the following given rules at each step [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
You are given the coordinates (x,y). Initially, you are at (1,1) and
are required to go to (x,y) using the following rule: If the current
position is (a,b) then in the next move, you can move only to (a+b,b)
or (a,a+b). Write a program to check if you can reach (x,y) using
only the described moves.
I have tried to solve this problem that I mentioned above in Java through recursion. But the program was not working for some test cases. I could not find what is wrong. But I know it is a brute force method.
import java.util.*;
class Solution{
static boolean sol(int a, int b, int x, int y){
if( a == x && b == y)
return true;
else if(a > x || b > y)
return false;
else{
if(sol(a+b, b, x, y)) return true;
if(sol(a, a+b, x, y)) return true;
}
return false;
}
static String ans(int x, int y){
if(sol(1, 1, x, y)) return "Yes";
return "No";
}
public static void main(String[] args) {
int x, int y;
Scanner sc = new Scanner(System.in);
x = sc.nextInt();
y = sc.nextInt();
System.out.println(ans(x, y));
}
}
This is the code that I wrote. Can someone tell me an efficient for this solving this and tell me what's wrong with my code?
Your question says:
If the current position is (a,b) then in the next move, you can move only to (a+b,b) or (a,a+b).
Your mistakes are (highlighted inline in bold):
if(sol(a+b, a, x, y)) return true; // a should be b
if(sol(a, a+b, x, y)) return false; // false should be true
Correct both points with (highlighted inline in bold):
if(sol(a+b, b, x, y)) return true;
if(sol(a, a+b, x, y)) return true;
OR simplify by combining them:
if(sol(a+b, b, x, y) || sol(a, a+b, x, y)) return true;
With above suggestion full code would be:
import java.util.*;
class Solution {
static boolean sol(int a, int b, int x, int y) {
if(a == x && b == y)
return true;
else if(a > x || b > y)
return false;
else if(sol(a+b, b, x, y) || sol(a, a+b, x, y)) // fix applied here
return true;
return false;
}
static String ans(int x, int y) {
if(sol(1, 1, x, y)) return "Yes";
return "No";
}
public static void main(String[] args) {
int x, int y;
Scanner sc = new Scanner(System.in);
x = sc.nextInt();
y = sc.nextInt();
System.out.println(ans(x, y));
}
}
Edit for optimized solution than brute force
We are starting with (1, 1) and any next step of (a, b) is (a+b, b) or (a, a+b) so x > 0 and y > 0 is the required condition for any step (x, y).
Now, let's say,
Step k: (a, b)
Step k+1: (a2, b2) = (a+b, b) OR (a, a+b)
So if we want to derive step#k from step#k+1 then we can say (a, b) can be one of these two: (a2 - b2, b2) OR (a2, b2 - a2). We can easily remove one option as we know that a > 0 and b > 0.
If (a2 - b2) > 0 then (a, b) must be (a2 - b2, b2).
Similarly, if (b2 - a2) > 0 then (a, b) must be (a2, b2 - a2).
If a2 == b2 (and not 1) then a2 - b2 and b2 - a2 will be 0 which is impossible as a > 0 and b > 0 is required condition.
So, we will start from destination (x, y) and try to reach (1, 1) by above observation in linear time. The gist will be:
static boolean solve(int x, int y) {
if (x == 1 && y == 1) {
return true;
}
if (x == y || x < 1 || y < 1) {
return false;
}
if (x > y) {
return solve(x - y, y);
} else {
return solve(x, y - x);
}
}
Above solution is recursive and Ole V.V. has a good point in this answer about possible StackOverflowError if input numbers are large and stack memory allocation is relatively less. Based on his suggestion of the need of iterative solution, I am providing below gist for iterative approach:
static boolean solve(int x, int y) {
while (x > 0 && y > 0) {
if (x == y) {
return (x == 1); // x and y can be same if they are 1, otherwise impossible
}
if (x > y) {
x = x - y;
} else {
y = y - x;
}
}
return false;
}
Optimized iterative solution
but still, the code isn't working. It's giving me memory limit
exceeded error. Is there any other logic for this program?
On my Java your program with the fixes from the answer from Viral Lalakia crashes with a stack overflow if either input is 10 000 or more. The limit may be adjusted by adjusting the memory allocation for your JVM, but in practice you cannot increase memory to whatever you might like. The effective solution is to avoid recursion and code an iterative solution.
My key idea is to iterate backward from the destination (x, y). Viral Lalakia’s optimized solution also goes backward from the destination, which saves time, but as long as recursion is used, I suspect that it doesn’t save space, which is what the recursive program is running out of.
So in a loop find the previous coordinates from x and y.
If x < y the previous coordinates must have been (x, y - x). So subtract x from y.
If conversely x > y do the opposite subtraction.
If x == y there are no possible previous coordinates. If we are at the starting point (1, 1), we know that a solution exists. Return true. If not, return false.
For each time through the loop check whether the number that you have decreased has become less than 1. If it has, there cannot be a solution. Return false.
BTW, the algorithm is the same as the algorithm for finding the greatest common divisor of x and y. So the very easy solution would be BigInteger.valueOf(x).gcd(BigInteger.valueOf(y)) and compare the result to BigInteger.ONE to determine whether a path exists.
Link: Answer by Viral Lalakia containing an optimized recursive solution
Viral Lalakia has already pointed out the logical problems you had, however, the current approach wastes a lot of memory.
Explanation: You are using recursion, which is implicit usage of the stack, where your called methods and their status is stored. If x and y are VERY large numbers, then you will get a stack overflow issue (no pun intended!).
Solution
We know that
initially you are on point (1, 1)
your (a, b) position is either (1, 1), (a' + b, b) or (a, a + b')
if a > b, then a = a' + b, so, the previous position is (a - b, b)
if a < b, then b = a + b', so, the previous position is (a, b - a)
if a == b, then you are either on the initial position of (1, 1), or, you cannot determine by the sheer values what was the previous state
This allows you to save a LOT of memory, assuming that you implement this iteratively. The ideas you need to comply to are as follows:
if you have reached (x, y), then the algorithm ends, no matter what happened, as the question is whether it's solvable, you do not need to store the actual solution
when you perform a step, you need to know whether you have added one of the coordinates to the other, or you have taken a step back
if the point looks like (a, a), then you need to store what the previous value was in a stack, as well as the direction you have taken, so you can go back to it, positions of (a, a) must be treated in a special manner
if you are NOT just after a backwards move, then try to increase the left coordinate first
if you are just after a backwards move and before taking a backwards move, either a > b was true, or a == b was true with the condition that in the stack I have mentioned earlier the direction was in the left, then increment the second coordinate instead of the first one and change the direction of the top entry of the stack if it exists
if you are just after a backwards move and either a < b was true, or a == b was true with the condition that in the stack the first coordinate is higher than the second, then the next move will be backwards as well
if you perform a backwards move and the position before the backwards move was like (a, a), then you can remove the corresponding entry from your stack
if, after a backward move you reach (1, 1), then you can end the algorithm, regardless of what the direction was, because having a step of (2, 1) is identical with having the step of (1, 2), including the subtrees, due to the commutative nature of adding
Take a look at:
if(sol(a, a+b, x, y)) return false;
That condition should return true as mentioned in the problem that "you can move only to (a+b,b) or (a,a+b)".
import java.util.*;
class Solution{
static boolean sol(int a, int b, int x, int y){
if( a == x && b == y)
return true;
else if(a > x || b > y)
return false;
else{
if(sol(a+b, a, x, y)) return true;
if(sol(a, a+b, x, y)) return true;
}
return false;
}
static String ans(int x, int y){
if(sol(1, 1, x, y)) return "Yes";
return "No";
}
public static void main(String[] args) {
int x,y;
Scanner sc = new Scanner(System.in);
x = sc.nextInt();
y = sc.nextInt();
System.out.println(ans(x, y));
}
}
OPTIMAL APPROACH:
Use a two-dimensional matrix (x * y) to know whether that cell is visited or not and if visited to store the result.
Not visited -> -1
visited and possible to reach to (x,y) from that position -> 1
visited and possible to reach to (x,y) from that position -> 0
import java.util.*;
class Solution{
static boolean sol(int a, int b, int x, int y, int[][] dp){
if( a == x && b == y)
return true;
else if(a > x || b > y)
return false;
else if(dp[a][b] == 1)
return true;
else if(dp[a][b] == 0)
return false;
else if(sol(a+b, a, x, y, dp) || sol(a, a+b, x, y, dp)){
dp[a][b] = 1;
return true;
}
dp[a][b] = 0;
return false;
}
static String ans(int x, int y, int[][] dp){
if(sol(1, 1, x, y, dp)) return "Yes";
return "No";
}
public static void main(String[] args) {
int x,y;
int[][] dp;
Scanner sc = new Scanner(System.in);
x = sc.nextInt();
y = sc.nextInt();
dp = new int[x+1][y+1];
for(int[] row : dp)
Arrays.fill(row,-1);
System.out.println(ans(x, y, dp));
}
}

Difficulty in writing a recursive solution to "Rat in a maze" problem

My question is essentially a doubt about recursion. I was solving the classic "Rat in a Maze" DFS traversal problem. My input was an n*n int array a[][] where for indices i and j, a[i][j] could either be 0 or 1. 0 meant the hypothetical rat couldn't visit the element and 1 meant it could. The rat could only go downwards("D") or rightwards("R"). The task was to output all movement Strings like RDRDRD that represented the rat's movement through the maze. The rat starts from a[0][0] and must reach a[n-1][n-1]. The input was the maze itself.
I wrote the following code
public boolean isSafe(int x, int y, int[][] a, int n)
{
if(x >= 0 && x < n && y >= 0 && y < n && a[x][y] == 1)
return true;
else
return false;
}
public ArrayList<String> printPath(int[][] a, int n)
{
ArrayList<String> res = new ArrayList<String>();
solve(0,0,new String(), res,a,n);
return res;
}
public void solve(int x, int y, String sol, ArrayList<String> res ,
int[][]a, int n)
{
if(x == n-1 && y == n-1)
{
res.add(sol);
return;
}
y++;
if(isSafe(x,y,a,n))
{
solve(x,y,sol + "R",res,a,n);
}
else
y--;
x++;
if(isSafe(x,y,a,n))
{
solve(x,y,sol+"D",res,a,n);
}
else
x--;
}`
where isSafe check whether a movement is permitted, printPath is a helper function for printing the output and solve is the recursive function used to traverse the maze.a represents the maze array as a 2-D array.
For the input
{1 0 0 0
1 1 0 1
0 1 0 0
0 1 1 1}
I get the following output
DRDDRR DDDRR
Obviously the second string represents an incorrect result.
However, when I changed the solve function like so
public void solve(int x, int y, String sol, ArrayList<String> res,
int[][]a, int n)
{
if(x == n-1 && y == n-1)
{
res.add(sol);
return;
}
if(!isSafe(x,y,a,n))
return;
solve(x+1,y,sol + "D",res,a,n);
solve(x,y+1,sol + "R",res,a,n);
return;
}
I get the correct output. What I am failing to understand is what resulted in the incorrect output in my previous solution, as to me the two solutions are logically similar.
I know it's a long read, but any insight would be greatly appreciated.
In the first solution the variable increment y++ is only undone if the call to isSafe with the incremented value comes back negative and is carried over to the check of x if it was true. This means that the down check on a field that has a valid neighbor to the right, in particular the field [1][0], will be performed with the incremented value of y instead of the correct one.
If you modify the first solution like this
y++;
if(isSafe(x,y,a,n)){
solve(x,y,sol + "R",res,a,n);
}
y--;
the first solution will work correctly as does the second one. In the second solution the increment is only done on the function argument, not a local variable.
A general advice is to not modify your input. And it is the case that your problem comes from just that. Here I modified your code so it doesn't do that. It's much more readable in my opinion and now you're sure of what x or y value you're using.:
if (isSafe(x, y + 1, a, n)) {
solve(x, y + 1, sol + "R", res, a, n);
}
if (isSafe(x + 1, y, a, n)) {
solve(x + 1, y, sol + "D", res, a, n);
}

What is wrong with my Java recursive function?

I'm trying to write a relatively straightforward recursive program in Java to compute all the possible ways to traverse a 4x4 matrix (not necessarily traveling through every spot), starting at the top left and ending in the bottom right spaces. I use a 2-D array to do this, marking off visited spaces with "1"s as I go.
It's been a while since I've worked recursively and I can't seem to get the output I expect. The output from the code below is "2" - obviously, the result should be much higher. I know there's something tiny I'm overlooking. Can someone tell me what it is?
public static void main(String[] args) {
int[][] matrix = new int[4][4];
int result = moveRobot(matrix, 0, 0);
System.out.print(result + "");
}
public static int moveRobot(int[][] matrix, int x, int y) {
if (x == 3 && y == 3) {
return 1;
} else if (x < 0 || y < 0 || x > 3 || y > 3) {
return 0;
} else if (matrix[x][y] == 1) {
return 0;
} else {
matrix[x][y] = 1;
return moveRobot(matrix, x, y+1) + moveRobot(matrix, x+1, y) + moveRobot(matrix, x, y-1) +
moveRobot(matrix, x-1, y);
}
}
The problem is that the matrix is not copied but passed by value of the reference to it. Every time you modify it such in matrix[x][y] = 1 other successive code paths will see the modification instead that working on an unmodified state.
For example here:
moveRobot(matrix, x, y+1) + moveRobot(matrix, x+1, y)
Entering the first call will modify matrix, so in second moveRobot call you'd end up with 1 in matrix[x][y+1] while that's not what you want.

Multiplication using increments

My assignment is to write a recursive function to multiply two numbers together, using only an addition function, ++, and --. My addition function is:
public static int peanoplus(int x, int y) {
if(y==0) return x;
else return peanoplus(++x,--y);
}
What I have so far for my multiplication function is:
public static int peanotimes(int x, int y)
{
if(y==0) return x;
else return peanotimes(peanoplus(x,x),--y);
}
I am not exactly sure what to put in the first parameter for the peanotimes function. Right now the issue is that I'm doubling the number, rather than adding it to the original number. I know that I need to maintain the x variable so that the recursive calls can continue adding the original number (instead of doubling every time), but then where would I actually add the numbers?
I found this which is very similar to my question, but even with those tips I am unable to find a solution.
if( y == 0 || x == 0 ) { return 0; }
else { return peanoplus(x, peanotimes(x,--y)); }
This version closest matches the formal Peano axiom of x * S(y) = x + (x * y)
public static int peanotimes(int x, int y)
{
if (y == 0) {
return 0; // terminate recursion, NB: not "x"
} else {
return peanoplus(x, peanotimes(x, --y));
}
}

Recursive method - Java

Addition information:
Chip doesn't support multiplication, only addition. I should work around this problem by creating a recursive method, mult(), that performs multiplication
of x and y by adding x to itself y times. Its arguments are x and y and its return
value is the product of x and y. I should then write the method and a main() to
call it.
It's pure logical thinking, but I get lost every time I try to think what to do.
I am stuck at the math part..
What I have, that doesn't work and I know the math is wrong, but I am not good at this:
public static void mult(int x, int y) {
x = 0;
y = 0;
if (y > 0) {
for (int i = 0; i < y; i++) {
x = x * (x * y);
return mult(x, y);
}
}
}
When I hear "recursion", I expect to see two things:
A function calling itself with modified arguments each time.
A stopping condition right at the top that tells the function when to stop, avoiding an infinite stack.
So where are yours? Start with writing those down in words before you write code.
One possibility is to use an accumulator which will store the current value of the multiplication. I replace missing statements by ??? :
public static void main(String []args){
System.out.println(mult(2,5));
}
public static int mult(int x, int y) {
if(???) return ???;
else return multAcc(???,???,???);
}
private static int multAcc(int x, int y, int acc){
if(???) return ???;
else return multAcc(???, ???, ???);
}
... by adding x to itself y times.
You could actually do that, instead of multiplying. Oh, and maybe if you don't set both x and y to zero, you would have something to add ;-)
One last thing: If you want a recursive solution, you don't need the for-loop.
Java has no TCO by design, so using recursion for linear (not tree-like) processes is very bad idea. Especially for such task, which will most likely become a bottleneck in your program. Use loop instead.
Oh, it must be recursive anyway? Looks like a homework task. Do it yourself then.
All you need to remember is that a multiplication is a repeated addition (assuming that both operands are >= 0), so we have:
The base case is when y is zero
If y is not zero, then add x one more time, and subtract 1 from y
Notice that as long as y is positive, it'll eventually have a value of zero. So basically we keep adding x a total number of y times; this is what I mean:
public static int mult(int x, int y) {
if (y == 0)
return 0;
return x + mult(x, y-1);
}
The same code can be written in a tail-recursive style, too - meaning: there's nothing to do after the recursive call returns, and this is important for certain languages that support a so-called tail-call optimization:
public static int mult(int x, int y, int accumulator) {
if (y == 0)
return accumulator;
return mult(x, y-1, x + accumulator);
}
The above will get called as follows, noticing that the last parameter is always initialized in zero:
mult(10, 5, 0)
=> 50
public static int mult(int x, int y) {
if (y == 0) {
return 0;
}
if (y > 0) {
return x + mult(x, y - 1);
} else {
return -x + mult(x, y + 1);
}
}
this was the solution by the way

Categories