I have this Knight's Tour code but am getting a java.lang.StackOverflowError on line 37. I'm not really sure where to go from here to fix it. I think the code in my main has something to do with it but I'm not sure what to do with it at this point. Any help is greatly appreciated thanks.
public class Main {
public static void main(String[] args) {
Main tour = new Main();
tour.solveKnightTour();
}
private static int chessboard[][];
boolean a = true;
public Main() {
chessboard = new int[8][8];
}
private void matrixChessBoard() {
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
System.out.printf("%5d", chessboard[i][j]);
}
System.out.println();
}
}
static boolean tour(int advance, int horizontal, int vertical, int xMove[], int yMove[]) {
int i = 0;
int moveHoriz = 0;
int moveVert = 0;
boolean a = true;
chessboard[horizontal][vertical] = advance;
if (advance == 63) {
for (int t = 0; t < 8; t++) {
for (int u = 0; u < 8; u++) {
System.out.printf("%5d", chessboard[t][u]);
}
System.out.println("\n");
}
} else {
for (int j = 0; j < 8; j++) {
if ((horizontal + xMove[j] < 8 & (vertical + yMove[j]) >= 0 & (vertical + yMove[i]) < 8)
& (horizontal + xMove[i]) >= 0){
if (chessboard[horizontal + xMove[i]][vertical + yMove[i]] == -1){
//line 37 if (tour(moveHoriz, moveVert, advance + 1, xMove, yMove)){
break;
}
}
}
}
a = false;
chessboard[horizontal][vertical] = -1;
}
return a;
}
public boolean solveKnightTour() {
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
chessboard[x][y] = -1;
}
}
int xMove[] = { 2, 1, -1, -2, -2, -1, 1, 2 };
int yMove[] = { 1, 2, 2, 1, -1, -2, -2, -1 };
chessboard[0][0] = 0;
if (!tour(0, 0, 1, xMove, yMove)) {
return false;
} else {
matrixChessBoard();
}
return true;
}
}
Consider a stripped down version of your tour() method:
static boolean tour(int advance, int horizontal, int vertical, ...) {
int moveHoriz = 0;
int moveVert = 0;
if (false) {
...
} else {
if (true)
tour(moveHoriz, moveVert, advance + 1, ...);
}
}
Called with tour(0, 0, 1, ...), the function will enter with advance=0. Various assignments will be done, various tests will be made, and then the tour(moveHoriz, moveVert, advance+1, ...) statement will be executed, which evaluates to the call tour(0, 0, 1, ...). Repeat until stack overflow.
moveHoriz is passed to the advance parameter
moveVert is passed to the horizontal parameter
advance+1 is passed to the vertical parameter
This is clearly not what you intended. Since advance+1 is not passed to advance, the if (advance == 63) condition will always evaluate to false, and you never break out of the recursion.
Related
I have an assignment to implementthe matrix class and a method that checks if the matrix is symmetric.
The method has to be recursive and then I have to calculate its complexity. After I have to transform the recursive function to its iterative version.
For now this is how my matrix class looks like:
public class Matrix<T> {
private int m, n;
private T[][] data;
public Matrix(int m, int n) {
this.m = m;
this.n = n;
}
public Matrix(T[][] data) {
this.data = data;
}
public boolean isSymmetric() {
if (m != n) {
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (data[i][j] != data[j][i]) {
return false;
}
}
}
return true;
}
return false;
}
public boolean isSymmetric(int i, int j) {
if (i < 0 && j < 0) return true;
else {
return isSymmetric(i - 1, j - 1);
}
}
public T get(int i, int j) {
return data[i][j];
}
public void set(int i, int j, T value) {
data[i][j] = value;
}
public int[] getSize() {
return new int[]{m, n};
}
#Override
public String toString() {
String rep = "";
for (int i = 0; i < m; i++) {
rep += "( ";
for (int j = 0; j < n; j++) {
rep += data[i][j].toString() + "\t";
}
rep += ")\n";
}
return rep;
}
}
I have a iterative version of the isSymmetric() function, but I cannot get the recursive one.
You can start check from maximum row and col each recursion will be decrese it by 1 until 0;
public class Matrix<T> {
private final int height;
private final int width;
private final T[][] data;
public Matrix(T[][] data) {
height = data.length;
width = data[0].length;
this.data = data; // it brakes OOP: encapsulation
}
public boolean isSymmetricIterative() {
if (height != width)
return false;
for (int row = 0; row < height; row++)
for (int col = 0; col < width; col++)
if (data[row][col] != data[col][row])
return false;
return true;
}
public boolean isSymmetricRecursive() {
return isSymmetricRecursive(height - 1, width - 1);
}
private boolean isSymmetricRecursive(int row, int col) {
return row < 0 && col < 0 || data[row][col] == data[col][row] && isSymmetricRecursive(row - 1, col - 1);
}
}
In the recursive version, you forgot to add a check to see if the two elements are equal. Without it, there is no case where the method would return false.
public boolean isSymmetric() {
return m == n && isSymmetric(m - 1, n - 1);
}
public boolean isSymmetric(int i, int j) {
if (i < 0 && j < 0) return true;
else if (data[i][j] != data[j][i]) return false;
else {
return isSymmetric(i - 1, j - 1);
}
}
Symmetric matrix is a square matrix that is equal to its transpose. Let's assume that we have a square matrix.
To avoid unnecessary iterations and to reduce the time complexity, you can iterate over the indices only in the lower left corner of the matrix and compare the corresponding elements with those in the upper right corner m[i][j]==m[j][i], excluding the main diagonal, until the first mismatch.
Try it online!
// let's assume that we have a square matrix
public static void main(String[] args) {
int[][] matrix = {
{1, 2, 3, 4, 5},
{2, 3, 4, 5, 6},
{3, 4, 5, 6, 7},
{4, 5, 6, 7, 8},
{5, 6, 7, 8, 9}};
System.out.println(isSymmetric(matrix)); // true
System.out.println(isSymmetricR(matrix, 1, 0)); // true
}
// iterative version
static boolean isSymmetric(int[][] matrix) {
return IntStream.range(1, matrix.length)
.allMatch(i -> IntStream.range(0, i)
//intermediate output
.peek(j -> System.out.println("i=" + i + ",j=" + j))
.allMatch(j -> matrix[i][j] == matrix[j][i]));
}
// recursive version
static boolean isSymmetricR(int[][] matrix, int i, int j) {
if (i < matrix.length && j < i) {
//intermediate output
System.out.println("i=" + i + ",j=" + j);
if (matrix[i][j] == matrix[j][i]) {
if (j == i - 1) {
return isSymmetricR(matrix, i + 1, 0);
} else {
return isSymmetricR(matrix, i, j + 1);
}
} else {
// the first mismatch, the
// matrix is not symmetric
return false;
}
} else {
// if reached this point,
// the matrix is symmetric
return true;
}
}
The intermediate output is the same in both cases:
i=1,j=0
i=2,j=0
i=2,j=1
i=3,j=0
i=3,j=1
i=3,j=2
i=4,j=0
i=4,j=1
i=4,j=2
i=4,j=3
See also: How to remove rows and columns containing only zeros from a 2d matrix?
Please help me understand what I am doing wrong with my code. I am trying to get the shortest path using BFS to solve the problem but it's either giving me -1 or 2. It should give me 6 as the answer. What am I doing wrong? This is the problem:
Given a chess board, find the shortest distance(minimum number of steps) taken by a knight to reach given destination from given source.
For example, N = 8 (8 x 8 board), Source = (7, 0) Destination = (0, 7)
Minimum number of steps required is 6
My code is below:
class Point {
int x, y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
}
class knightShortestPath {
int N = 8;
public static boolean visited[][];
public boolean isPositionValid(int x, int y){
if( x < 0 || y < 0 || x > this.N || y > this.N){
return false;
}
return true;
}
public void createChessBoard(int N) {
this.N = N;
visited = new boolean[this.N][this.N];
for (int i = 0; i < this.N; i++) {
for (int j = 0; j < this.N; j++) {
visited[i][j] = false;
}
}
}
public int BFS(Point source, Point destination) {
int row[] = {2, 2, -2, -2, 1, 1, -1, -1};
int col[] = {1, -1, 1, -1, 2, -2, 2, -2};
Queue<Point> queue = new LinkedList<>();
queue.offer(source);
visited[source.x][source.y] = true;
int minimumNumSteps = 0;
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
Point pt = queue.poll();
if (pt.x == destination.x && pt.y == destination.y) {
return minimumNumSteps;
}
for (int j = 0; j < size; j++) {
Point next = new Point(pt.x + row[i], pt.y + col[j]);
if (isPositionValid(pt.x + row[i], pt.y + col[j]) && !visited[i][j]) {
visited[i][j] = true;
queue.offer(next);
}
}
}
minimumNumSteps++;
}
return minimumNumSteps;
}
public static void main(String[] args) {
knightShortestPath position = new knightShortestPath();
position.createChessBoard(8);
Point src = new Point(0,7);
Point dest = new Point(7,0);
System.out.println("The minimum number of steps are: " + position.BFS(src, dest)); //answer is 6
}
}
First thing: I have no idea how you can end up with a negative value. You never decrease minimumNumSteps after initializing it with 0. An overflow possibly? Seems weird to me ..
Besides that, I see two issues:
The two for loops are incorrect. You currently iterate over the queue.size(). What you want to do instead is iterating over all children of the current node.
Poll the current point outside of the for loops.
So:
while(!queue.isEmpty()) {
Point pt = queue.poll();
// check if it's target
// ...
for (int i = 0; i < row.length; i++) {
// ...
for (int j = 0; j < col.length; j++) {
// ...
}
}
}
Another note: When the queue is empty and you have not reached the goal, there is no solution. Currently, you are returning some value that may be interpreted falsely.
An array is defined to be complete if all its elements are greater than 0 and all even numbers that are less than the maximum even number are in the array.
For example {2, 3, 2, 4, 11, 6, 10, 9, 8} is complete because
a. all its elements are greater than 0
b. the maximum even integer is 10 c. all even numbers that are less than 10 (2, 4, 6, 8) are in the array.
But {2, 3, 3, 6} is not complete because the even number 4 is missing. {2, 3, 4, 3, 6} is not complete because it contains a negative number.
Write a function named isComplete that returns 1 if its array argument is a complete array. Otherwise it returns 0
This is a question i have to solve but i am not able to find good logic for this questions . Here I have find maximum even numbers from the array in first loop and in second loop, i have to check all the even numbers less than the maximum even numbers and i am stuck on this. please help me ,,,,,,,,,,my code is below
public class Complete {
public static void main(String[] args) {
System.out.println(isComplete(new int[]{2,3,2,4,11,6,10,9,8}));
System.out.println(isComplete(new int[]{2,3,3,6}));
System.out.println(isComplete(new int[]{2,-3,4,3,6}));
}
private static int isComplete(int[] i) {
int set = 1;
int maxeven = 0;
int count = 0;
for(int a = 0; a < i.length; a++) {
if(i[a] < 0) {
set = 0;
break;
}
if(i[a]%2 == 0 && i[a] > maxeven) {
maxeven = i[a];
}
}
for (int c = 2; c <= maxeven; c=c+2) {
for( int b = 0; b<i.length; b++) {
if (c == i[b]) {
count++;
}
}
if (count > 0) {
set = 1;
} else {
set = 0;
break;
}
}
return set;
}
}
I have find maximum even numbers from the array in first loop and in
second loop, i have to check all the even numbers less than the
maximum even numbers
I have followed this logic and wrote this. I am checking the validity of condtion 1 (All elements >0) and in the same loop finding the largest even number.
In the second loop, I am checking whether all the even numbers lesser than the largest even nuber is present.
public class Complete {
public static void main(String[] args) {
System.out.println(isComplete(new int[] { 2, 3, 2, 4, 11, 6, 10, 9, 8 }));
System.out.println(isComplete(new int[] { 2, 3, 3, 6 }));
System.out.println(isComplete(new int[] { 2, -3, 4, 3, 6 }));
}
private static int isComplete(int[] i) {
int maxEven = 0;
for (int element : i) {
if (element <= 0) {
return 0;
}
if (element % 2 == 0) {
if (element > maxEven) {
maxEven = element;
}
}
}
for (int a = 2; a < maxEven; a = a + 2) {
if (!hasElement(i, a)) {
return 0;
}
}
return 1;
}
private static boolean hasElement(int[] i, int a) {
for (int element : i) {
if (element == a) {
return true;
}
}
return false;
}
}
Set count = 0 in the second loop, like this:
for(int c = 2; c<=maxeven;c=c+2){
count = 0;
for(int b = 0;b<i.length;b++){
That should solve your problem. Without count = 0, count will be >0 after you've looked for "2", so count>0 will be true when you look for "4" even if there are not "4"s.
int isComplete(int[] a) {
int max = 0;
for (int el: a) {
if (el <= 0) {
return 0;
}
if (el % 2 == 0) {
if (el > max) {
max = el;
}
}
}
int req = (max / 2) - 1;
int p = 0;
for (int c = 2; c < max; c = c + 2) {
for (int j = 0; j < a.length; j++) {
if (a[j] == c) {
p++;
}
}
}
if (req == p) {
return 1;
} else
return 0;
}
I got a question about recursion for an entry exam of a job, but I failed to do it within 2 hours. I am very curious about how to do this after the pre-exam but I cannot work out a solution.
You can imagine there is a coin pusher with size n*m (2D array).
Each operation (moving up or down or left or right) will throw away one row or one column of coins
The question requires me to find the shortest possible moves that remains k coins at last. If it is impossible to remain k coins at last, then return -1
I stuck on how to determine the next move when there is more than one operation that having the same maximum number of coins (same value to be thrown away)
I believe that I need to calculate recursively that simulates all future possible moves to determine the current move operation.
But I do not know how to implement this algorithm, can anyone help?
Thank you!
Question :
There is a rectangular chessboard containing N‘M cells. each of
which either has one coin or nothing.
You can move all the coins together in one direction (such as up,
down, left, and right), but each time you can move these coins by
only one cell.
If any coins fall out of the chessboard, they must be thrown away.
If it is required to keep K coins on the board, what is the minimum
moves you have to take?
Output -1 if you can not meet this requirement.
The first line of the input are two positive
integers n, representing the size of the board.
For the next n line(s), each line has m numbers of
characters, with 'o' indicating a coin, '.' indicates an empty grid.
The last line is a positive integer k,
indicating the number of coins to be retained.
30% small input: 1 <= n,m <= 5, 0 < k < 25
40% medium input: 1 <= n,m <= 10, 0 < k < 100
30% large input: 1 <= n,m <= 100, 0 < k < 10000
sample input:
3 4
.o..
oooo
..o.
3
sample output:
2
My temporary answer
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;
public class main {
String[][] inputArray;
int n;
int m;
int k;
int totalCoin = 0;
int step = 0;
public static void main(String[] args) {
main temp = new main();
temp.readData();
}
public void readData() {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
inputArray = new String [n][m];
sc.nextLine(); // skipping
for (int i = 0; i < n; i++) {
String temp = sc.nextLine();
for (int j = 0; j < m; j++) {
if ((temp.charAt(j) + "").equals("o")) totalCoin++;
inputArray[i][j] = temp.charAt(j) + "";
}
}
k = sc.nextInt();
int result = 0;
if (totalCoin >= k) {
result = findMaxAndMove();
System.out.println(result);
}
}
public String findNextMove() {
Map<String,Integer> tempList = new HashMap<String,Integer>();
tempList.put("up", up());
tempList.put("down", down());
tempList.put("left", left());
tempList.put("right", right());
Map.Entry<String, Integer> maxEntry = null;
for (Entry<String,Integer> temp : tempList.entrySet()) {
if (maxEntry == null || temp.getValue() > maxEntry.getValue()) {
maxEntry = temp;
}
}
Map<String,Integer> maxList = new HashMap<String,Integer>();
for (Entry<String,Integer> temp : tempList.entrySet()) {
if (temp.getValue() == maxEntry.getValue()) {
maxList.put(temp.getKey(), temp.getValue());
}
}
// return maxList.entrySet().iterator().next().getKey();
if (maxList.size() > 1) {
// how to handle this case when more than 1 operations has the same max value???????????
return ??????????????
}
else {
return maxList.entrySet().iterator().next().getKey();
}
//
}
public int findMaxAndMove() {
int up = up();
int down = down();
int left = left();
int right = right();
if ((totalCoin - up) == k) {
step++;
return step;
}
if ((totalCoin - down) == k) {
step++;
return step;
}
if ((totalCoin - left) == k) {
step++;
return step;
}
if ((totalCoin - right) == k) {
step++;
return step;
}
if (totalCoin - up < k && totalCoin - down < k && totalCoin - left < k && totalCoin - right < k) return -1;
else {
switch (findNextMove()) {
case "up" :
totalCoin -= up;
this.moveUp();
break;
case "down" :
totalCoin -= down;
this.moveDown();
break;
case "left" :
totalCoin -= left;
this.moveLeft();
break;
case "right" :
totalCoin -= right();
this.moveRight();
break;
}
step++;
return findMaxAndMove(); // going to next move
}
}
public String[] createBlankRow() {
String[] temp = new String[m];
for (int i = 0; i < m; i++) {
temp[i] = ".";
}
return temp;
}
public int up() {
int coinCounter = 0;
for (int i = 0; i < m; i++) {
if (inputArray[0][i].equals("o")) {
coinCounter++;
}
}
return coinCounter;
}
public void moveUp() {
// going up
for (int i = 0; i < n - 1; i++) {
inputArray[i] = inputArray[i + 1];
}
inputArray[n-1] = createBlankRow();
}
public int down() {
int coinCounter = 0;
for (int i = 0; i < m; i++) {
if (inputArray[n-1][i].equals("o")) {
coinCounter++;
}
}
return coinCounter;
}
public void moveDown() {
// going down
for (int i = n-1; i > 1; i--) {
inputArray[i] = inputArray[i - 1];
}
inputArray[0] = createBlankRow();
}
public int left() {
int coinCounter = 0;
for (int i = 0; i < n; i++) {
if (inputArray[i][0].equals("o")) {
coinCounter++;
}
}
return coinCounter;
}
public void moveLeft() {
// going left
for (int i = 0; i < n; i++) {
for (int j = 0; j < m-1; j++) {
inputArray[i][j] = inputArray[i][j+1];
}
inputArray[i][m-1] = ".";
}
}
public int right() {
int coinCounter = 0;
for (int i = 0; i < n; i++) {
if (inputArray[i][m-1].equals("o")) {
coinCounter++;
}
}
return coinCounter;
}
public void moveRight() {
// going right
for (int i = 0; i < n; i++) {
for (int j = m-1; j > 0; j--) {
inputArray[i][j] = inputArray[i][j-1];
}
inputArray[i][0] = ".";
}
}
public void printboard() {
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
System.out.print(inputArray[i][j]);
}
System.out.println();
}
}
}
I suspect you didn't find the right algorithm to solve the problem. To find a solution, not only the reachable boards with some special coin count are of concern but all. You must build up a tree of reachable boards. Each node in this tree is connected to its child nodes by an operation. That's were recursion enters the scene. You stop
when you reach your goal (mark this branch as possible solution) or
when it got impossible to reach the goal with further operations (too few coins left)
when the next operation would reach a board already visited in this branch.
In this tree all shortest branches marked as possible solution are the actual solutions. If no branch is marked as possible solution there is no solution and you have to output -1.
Here is my solution
public class CoinsMover {
public static List<String> getMinMoves(Character[][] board, int k, List<String> moves) {
if (!movesAreValid(moves, board)) {
return null;
}
int currentAmountOfCoins = getCoinsOnBoard(board);
// All good no need to move any thing
if (currentAmountOfCoins == k) {
moves.add("done");
return moves;
}
// Moved to much wrong way
if (currentAmountOfCoins < k) {
return null;
}
List<String> moveRight = getMinMoves(moveRight(board), k, getArrayWithApendded(moves, "right"));
List<String> moveLeft = getMinMoves(moveLeft(board), k, getArrayWithApendded(moves, "left"));
List<String> moveUp = getMinMoves(moveUp(board), k, getArrayWithApendded(moves, "up"));
List<String> moveDown = getMinMoves(moveDown(board), k, getArrayWithApendded(moves, "down"));
List<List<String>> results = new ArrayList<>();
if (moveRight != null) {
results.add(moveRight);
}
if (moveLeft != null) {
results.add(moveLeft);
}
if (moveUp != null) {
results.add(moveUp);
}
if (moveDown != null) {
results.add(moveDown);
}
if (results.isEmpty()) {
return null;
}
List<String> result = results.stream().sorted(Comparator.comparing(List::size)).findFirst().get();
return result;
}
private static boolean movesAreValid(List<String> moves, Character[][] board) {
long ups = moves.stream().filter(m -> m.equals("up")).count();
long downs = moves.stream().filter(m -> m.equals("down")).count();
long lefts = moves.stream().filter(m -> m.equals("left")).count();
long rights = moves.stream().filter(m -> m.equals("right")).count();
boolean verticalIsFine = ups <= board.length && downs <= board.length;
boolean horizontalIsFine = lefts <= board[0].length && rights <= board[0].length;
return verticalIsFine && horizontalIsFine;
}
private static List<String> getArrayWithApendded(List<String> moves, String move) {
List<String> result = new ArrayList<>(moves);
result.add(move);
return result;
}
private static Character[][] moveRight(Character[][] board) {
Character result[][] = new Character[board.length][board[0].length];
// Cleaning left column
for (int i = 0; i < board.length; i++)
result[i][0] = '.';
for (int row = 0; row < board.length; row++) {
for (int column = 0; column < board[row].length - 1; column++) {
result[row][column + 1] = board[row][column];
}
}
return result;
}
private static Character[][] moveLeft(Character[][] board) {
Character result[][] = new Character[board.length][board[0].length];
// Cleaning right column
for (int i = 0; i < board.length; i++)
result[i][board[i].length - 1] = '.';
for (int row = 0; row < board.length; row++) {
for (int column = 1; column < board[row].length; column++) {
result[row][column - 1] = board[row][column];
}
}
return result;
}
private static Character[][] moveDown(Character[][] board) {
Character result[][] = new Character[board.length][board[0].length];
// Cleaning upper row
for (int i = 0; i < board[board.length - 1].length; i++)
result[0][i] = '.';
for (int row = board.length - 1; row > 0; row--) {
result[row] = board[row - 1];
}
return result;
}
private static Character[][] moveUp(Character[][] board) {
Character result[][] = new Character[board.length][board[0].length];
// Cleaning upper row
for (int i = 0; i < board[board.length - 1].length; i++)
result[board.length - 1][i] = '.';
for (int row = 0; row < board.length - 1; row++) {
result[row] = board[row + 1];
}
return result;
}
private static int getCoinsOnBoard(Character[][] board) {
int result = 0;
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
if (board[i][j] == 'o') {
result++;
}
}
}
return result;
}
public static void main(String... args) {
Character[][] mat = {{'.', 'o', '.', '.'}, {'o', 'o', 'o', 'o'}, {'.', '.', 'o', '.'}};
List<String> result = getMinMoves(mat, 3, new ArrayList<>());
if (result == null) {
System.out.println(-1);//output [right, right, done]
}
System.out.println(result);
}
}
I admit it was hard for me to search for duplicates so instead I am using a list of string which wrights the path you need to take to get to the solution. Now let's look at stopping condition first if current moves are invalid return null example of invalid moves is if you have a table with 4 columns in you moved right 5 times same goes for rows and moves up/down. Second, if board holds the neede amount we are done. And the last if board hold less we have failed. So now what algorithm is trying to do is to step in each direction in search of result and from here proceed recursivly.
You can find the solution below. A few points to note.
Whenever you see a problem that mentions moving an array in 1 direction, it's always a good idea to define an array of possible directions and loop through it in the recursive function. This would prevent you from confusing yourself.
The idea is to count coins by row and column so that you have a way to find out the remaining coins after each move in linear time.
The remaining job is to just loop through the possible directions in your recursive functions to find a possible solution.
As the recursive function may run in a circle and come back to one of the previous locations, you should/can improve the recursive function further by maintaining a Map cache of previous partial solutions using the curRowIdx and curColIdx as key.
public static void main(String[] args) {
char[][] board = {{'.', 'o', '.', '.'},
{'o', 'o', 'o', 'o'},
{'.', '.', 'o', '.'}};
CoinMoveSolver solver = new CoinMoveSolver(board);
System.out.println(solver.getMinimumMove(3));
}
static class CoinMoveSolver {
int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
char[][] board;
int[] rowCount;
int[] colCount;
int height;
int width;
int totalCoins;
public CoinMoveSolver(char[][] board) {
// Set up the board
this.board = board;
this.height = board.length;
this.width = board[0].length;
// Count how many coins we have per row,
// per column and the total no. of coins
this.rowCount = new int[height];
this.colCount = new int[width];
for (int i = 0 ; i < board.length ; i++) {
for (int j = 0 ; j < board[i].length ; j++) {
if (board[i][j] == 'o') {
this.rowCount[i]++;
this.colCount[j]++;
totalCoins++;
}
}
}
}
// Returns the number of coins if the top left
// corner of the board is at rowIdx and colIdx
private int countCoins(int rowIdx, int colIdx) {
int sumRow = 0;
for (int i = rowIdx ; i < rowIdx + height ; i++) {
if (i >= 0 && i < height)
sumRow += rowCount[i];
}
int sumCol = 0;
for (int j = colIdx ; j < colIdx + width ; j++) {
if (j >= 0 && j < width)
sumCol += colCount[j];
}
return Math.min(sumRow, sumCol);
}
public int getMinimumMove(int targetCoinCount) {
if (totalCoins < targetCoinCount)
return -1;
else if (totalCoins == targetCoinCount)
return 0;
else
return this.recursiveSolve(0, 0, -1, 0, targetCoinCount);
}
private boolean isOppositeDirection(int prevDirectionIdx, int curDirectionIdx) {
if (prevDirectionIdx < 0)
return false;
else {
int[] prevDirection = directions[prevDirectionIdx];
int[] curDirection = directions[curDirectionIdx];
return prevDirection[0] + curDirection[0] + prevDirection[1] + curDirection[1] == 0;
}
}
private int recursiveSolve(int curRowIdx, int curColIdx, int prevDirectionIdx, int moveCount, int targetCoinCount) {
int minMove = -1;
for (int i = 0 ; i < directions.length ; i++) {
if (!this.isOppositeDirection(prevDirectionIdx, i)) {
int[] direction = directions[i];
int nextRowIdx = curRowIdx + direction[0];
int nextColIdx = curColIdx + direction[1];
int coinCount = this.countCoins(nextRowIdx, nextColIdx);
// If this move reduces too many coins, abandon
if (coinCount < targetCoinCount)
continue;
// If this move can get us the exact number of
// coins we're looking for, break the loop
else if (coinCount == targetCoinCount) {
minMove = moveCount + 1;
break;
} else {
// Look for the potential answer by moving the board in 1 of the 4 directions
int potentialMin = this.recursiveSolve(nextRowIdx, nextColIdx, i, moveCount + 1, targetCoinCount);
if (potentialMin > 0 && (minMove < 0 || potentialMin < minMove))
minMove = potentialMin;
}
}
}
// If minMove is still < 0, that means
// there's no solution
if (minMove < 0)
return -1;
else
return minMove;
}
}
I've a problem with an exercise. I need to find all the solutions for a given sudoku, using fork/join parallelism. I made an algorithm but it seems it doesn't work. It stops at some point and I can't figure it out why.
Here's the code:
private static int counter;
private Cella[][] sudoku;
private int i;
private int j;
private int theCounter = 0;
public SudokuMulti(Cella[][] sudoku) {
this.sudoku = sudoku;
}
public SudokuMulti(Cella[][] sudoku, int i, int j) {
this.sudoku = sudoku;
this.i = i;
this.j = j;
}
//DELETED
// Copy the sudoku matrix
private Cella[][] createCopy() {
Cella[][] toReturn = new Cella[9][9];
for (int i = 0; i < 9; i++) {
System.arraycopy(sudoku[i], 0, toReturn[i], 0, 9);
}
return toReturn;
}
And the code for the object Cella:
public class Cella {
private int current;
public Cella() {
current = 0;
}
public Cella(int current) {
this.current = current;
}
//Getter and Setter
My idea is to give to each thread the faculty to solve its own sudoku, given the "legal values" of the candidate cell. I then collect all threads in an ArrayList and ask them to fork with the last for. Every thread should return an Integer (0 for no success, 1 for success) in order to count how many possible sudokus can be solved.
However, the algorithm only covers 1/3 of the sudoku: after a certain point, it stops filling the cells and it just returns without completing it.
Can someone suggest me where I'm doing mistake(s) ?
From what code you have posted, I cannot see any issue that explains your problem. However, you have not posted code that I can compile and execute myself (known as a Minimum Working or Verifiable Example, see Wikipedia and StackOverflow's guide on creating one), nor have you posted a stack trace or output for your application. This makes it difficult to help you solve your problem. If you can provide me with more, I am willing to continue helping you with your problem.
In the meantime, I tried to throw together a program that solves the same problem following your approach. It seems to work, though I have not thoroughly unit-tested it. Perhaps you can compare it to what you wrote and use the differences to discover the problem. You will need at least Java 7 to compile and run this code.
If this is for a homework assignment, I recommend checking with your professor or TA before looking at this listing.
public class Main {
public static void main( String[] args ) {
Sudoku puzzle = new Sudoku();
// Uncomment these lines to have a uniquely solvable Sudoku puzzle. They are commented out to prove that this code can count multiple solutions.
// puzzle.set(1, 0, 2);
// puzzle.set(2, 0, 9);
// puzzle.set(4, 0, 5);
// puzzle.set(7, 0, 4);
// puzzle.set(8, 0, 1);
// puzzle.set(3, 1, 8);
// puzzle.set(6, 1, 3);
// puzzle.set(2, 2, 3);
puzzle.set(3, 2, 7);
puzzle.set(4, 2, 4);
puzzle.set(5, 2, 9);
puzzle.set(6, 2, 6);
puzzle.set(3, 3, 4);
puzzle.set(6, 3, 2);
puzzle.set(7, 3, 1);
puzzle.set(1, 4, 6);
puzzle.set(3, 4, 3);
puzzle.set(4, 4, 7);
puzzle.set(5, 4, 1);
puzzle.set(7, 4, 8);
puzzle.set(1, 5, 4);
puzzle.set(2, 5, 1);
puzzle.set(5, 5, 6);
puzzle.set(2, 6, 5);
puzzle.set(3, 6, 9);
puzzle.set(4, 6, 2);
puzzle.set(5, 6, 8);
puzzle.set(6, 6, 7);
puzzle.set(2, 7, 4);
puzzle.set(5, 7, 7);
puzzle.set(0, 8, 3);
puzzle.set(1, 8, 7);
puzzle.set(4, 8, 6);
puzzle.set(6, 8, 5);
puzzle.set(7, 8, 2);
SudokuSolver solver = new SudokuSolver(puzzle);
long start = System.nanoTime();
int totalSolutions = solver.compute();
long end = System.nanoTime();
System.out.println(totalSolutions);
System.out.format("%f ms", (end - start) / 1e6);
}
private static class Sudoku {
private final int[][] cells;
Sudoku() {
cells = new int[9][9];
}
Sudoku( Sudoku original ) {
cells = new int[9][9];
for (int column = 0; column < 9; ++column) {
for (int row = 0; row < 9; ++row) {
set(column, row, original.get(column, row));
}
}
}
int get( int column, int row) {
return cells[column][row];
}
void set( int column, int row, int value ) {
cells[column][row] = value;
}
boolean isPlausible() {
return columnsArePlausible() && rowsArePlausible() && blocksArePlausible();
}
private boolean columnsArePlausible() {
boolean result = true;
for (int column = 0; result && column < 9; ++column) {
result = isColumnPlausible(column);
}
return result;
}
private boolean isColumnPlausible( int column ) {
boolean result = true;
boolean[] seen = new boolean[10];
for (int row = 0; result && row < 9; ++row) {
int value = get(column, row);
if (value > 0 && seen[value]) {
result = false;
} else {
seen[value] = true;
}
}
return result;
}
private boolean rowsArePlausible() {
boolean result = true;
for (int row = 0; result && row < 9; ++row) {
result = isRowPlausible(row);
}
return result;
}
private boolean isRowPlausible( int row ) {
boolean result = true;
boolean[] seen = new boolean[10];
for (int column = 0; result && column < 9; ++column) {
int value = get(column, row);
if (value > 0 && seen[value]) {
result = false;
} else {
seen[value] = true;
}
}
return result;
}
private boolean blocksArePlausible() {
boolean result = true;
for (int column = 0; result && column < 9; column += 3) {
for (int row = 0; result && row < 9; row += 3) {
result = isBlockPlausible(column, row);
}
}
return result;
}
private boolean isBlockPlausible( int column, int row ) {
boolean result = true;
boolean[] seen = new boolean[10];
for (int x = 0; result && x < 3; ++x) {
for (int y = 0; result && y < 3; ++y) {
int value = get(column + x, row + y);
if (value > 0 && seen[value]) {
result = false;
} else {
seen[value] = true;
}
}
}
return result;
}
}
private static class SudokuSolver extends RecursiveTask<Integer> {
private static final long serialVersionUID = 8759452522630056046L;
private Sudoku state;
private int column;
private int row;
SudokuSolver( Sudoku state ) {
this.state = state;
// These settings allow the search loop in compute() to increment first without asking questions about
// whether this cell has been checked yet.
column = -1;
row = 8;
}
SudokuSolver( Sudoku state, int column, int row ) {
this.column = column;
this.row = row;
this.state = state;
}
#Override
protected Integer compute() {
int viableSolutions = 0;
if (state.isPlausible()) {
int originalColumn = column;
int originalRow = row;
do {
if (row + 1 >= 9) {
++column;
row = 0;
} else {
++row;
}
} while (column < 9 && state.get(column, row) != 0);
if (column >= 9) {
viableSolutions = 1;
} else {
List<SudokuSolver> solvers = new ArrayList<>();
for (int value = 1; value <= 9; ++value) {
Sudoku copy = new Sudoku(state);
copy.set(column, row, value);
solvers.add(new SudokuSolver(copy, column, row));
}
invokeAll(solvers);
for (SudokuSolver solver : solvers) {
viableSolutions += solver.join();
}
}
}
return viableSolutions;
}
}
}
Since this code times how long it takes to count the solutions, the output can vary, but I got
354
709.848410 ms
I've found a solution. Here is the error:
// Copy the sudoku matrix
private Cella[][] createCopy() {
Cella[][] toReturn = new Cella[9][9];
for (int i = 0; i < 9; i++) {
// !!ERROR!!
System.arraycopy(sudoku[i], 0, toReturn[i], 0, 9);
}
return toReturn;
}
When I copy the array I fill it with the Cella object reference and not with a new one, so it causes data races.
The correct way to copy the matrix is:
private Cella[][] createCopy() {
Cella[][] toReturn = new Cella[9][9];
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
toReturn[i][j] = new Cella(sudoku[i][j].getCurrent());
}
}
return toReturn;
}