Random generator does not work - java

i tried to implement a method to set up a board with an array of Cell object. the method then randomly place a new string "C10" over the "---" string. My class and main is below
public class Cell {
public int addSpaces;
public Cell() {
addSpaces = 0;
}
public Cell(int x) {
addSpaces = x;
}
public String toString() {
String print;
if (addSpaces == -10)
print = "C10";
else
print = "---";
return print;
}
}
import java.util.Random;
public class ChutesAndLadders {
Cell[] board = new Cell[100]; // Set array of Cell object
Random ran = new Random();
Cell s = new Cell();
public int Chut, Ladd;
public ChutesAndLadders() {
}
public ChutesAndLadders(int numChutes, int numLadders) {
Chut = numChutes;
Ladd = numLadders;
}
public void setBoard() {
for (int i = 0; i < board.length; i++)
board[i] = new Cell(); // board now has 100 Cell with toString "---"
for (int k = 1; k <= Chut; k++) {
int RanNum = ran.nextInt(board.length); // Randomly replace the
// toString
if (board[RanNum] == board[k])
this.board[RanNum] = new Cell(-10);
else
k--;
}
}
public void printBoard() { // method to print out board
int count = 0;
for (int i = 0; i < board.length; i++) {
count++;
System.out.print("|" + board[i]);
if (count == 10) {
System.out.print("|");
System.out.println();
count = 0;
}
}
}
public static void main(String[] args) {
ChutesAndLadders cl = new ChutesAndLadders(10, 10);
cl.setBoard();
cl.printBoard();
}
}
Instead of randomly placing C10 all over the board I got this output;
|---|C10|C10|C10|C10|C10|C10|C10|C10|C10|
|C10|---|---|---|---|---|---|---|---|---|
|---|---|---|---|---|---|---|---|---|---|
|---|---|---|---|---|---|---|---|---|---|
|---|---|---|---|---|---|---|---|---|---|
|---|---|---|---|---|---|---|---|---|---|
|---|---|---|---|---|---|---|---|---|---|
|---|---|---|---|---|---|---|---|---|---|
|---|---|---|---|---|---|---|---|---|---|
|---|---|---|---|---|---|---|---|---|---|
can someone tell me what i did wrong? Thank you.

I'm not sure what your intention with this was in your for-loop:
if (board[RanNum] == board[k])
But it will cause your for-loop to, for each k, generate random numbers until it generates k, and then set that cell. So for k == 1, it will always set cell 1, cell 2 for k == 2, cell 3 for k == 3, etc.
I'm guessing you want to do something more like:
for (int k = 1; k <= Chut; k++) {
int RanNum = ran.nextInt(board.length);
if (board[RanNum].addSpaces == 0) // uninitialized
this.board[RanNum] = new Cell(-10);
else
k--;
}
EDIT:
As you probably realized from the comment + other answers, the above code is not particularly readable. Something like this should be better:
int chutCount = 0;
while (chutCount < Chut)
{
int randomNum = ran.nextInt(board.length);
if (board[randomNum].addSpaces == 0) // uninitialized
{
board[randomNum] = new Cell(-10);
chutCount++;
}
}

I'm not sure exactly what you want to achieve, but if you eliminate the if else in your inner setBoard loop then you should get random placement of C10.
Change this:
for (int k = 1; k <= Chut; k++) {
int RanNum = ran.nextInt(board.length); // Randomly replace the
// toString
if (board[RanNum] == board[k])
this.board[RanNum] = new Cell(-10);
else
k--;
}
To this:
for (int k = 1; k <= Chut; k++) {
int RanNum = ran.nextInt(board.length); // Randomly replace the
// toString
this.board[RanNum] = new Cell(-10);
}

I think you meant !=.
for (int k = 1; k <= Chut; k++) {
int ranNum = (int)(Math.random()*board.length);
if (board[ranNum] != board[k])
this.board[ranNum] = new Cell(-10);
else
k--;
}

Related

Fill 2D array in JAVA

i have a file which has in it numbers that has to be inserted to 2d array but it doesn't works
here is my code
sorry for bad English
file
2,1 //starting point
3,2 //ending point
5,6 //array size which is maze
0,1,1,1,1,1 //from here till end its all gonna be inserted to 2d array
0,1,0,0,1,1 //and i somehow managed to take starting, ending point and arraysize
0,1,0,0,1,0 //all i have to do is fill the array
0,0,1,0,1,0
0,0,1,1,1,0
Complete Code
public class Mouse {
// global variables to take input from file
public static int startRow;
public static int startCol;
public static int endRow;
public static int endCol;
public static int arrayRow;
public static int arrayCol;
public static String arrayDetail;
public static void main(String[] args) throws IOException {
StringBuilder stringbuilder = new StringBuilder();
try {
BufferedReader input = new java.io.BufferedReader(new java.io.FileReader("src/input.txt"));
List<String> lines = new ArrayList<String>();
String[] stringArray = new String[lines.size()];
String line = null;
while ((line = input.readLine()) != null) {
lines = Arrays.asList(line.split("\\s*,\\s*"));
stringArray = lines.toArray(stringArray);
for (int i = 0; i < stringArray.length; i++) {
stringbuilder.append(stringArray[i]);
}
}
}
catch (FileNotFoundException e) {
System.err.println("File not found.");
}
System.out.println(stringbuilder);
//giving global variables a value that i need
startRow = Integer.parseInt(stringbuilder.substring(0, 1));
startCol = Integer.parseInt(stringbuilder.substring(1, 2));
endRow = Integer.parseInt(stringbuilder.substring(2, 3));
endCol = Integer.parseInt(stringbuilder.substring(3, 4));
arrayRow = Integer.parseInt(stringbuilder.substring(4, 5));
arrayCol = Integer.parseInt(stringbuilder.substring(5, 6));
arrayDetail = stringbuilder.substring(6);
//i cant give array values to the long variable because it's out of range
Home home = new Home();
home.print(); // print maze before releasing mouse
if (home.walk(startRow,startCol)) { //starting position
System.out.println("Ok"); //print okay if its successful
}
else {
System.out.println("No Solution"); //if unsuccessful
}
home.print(); // print maze to track how mouse went
}
}
class Home{
Home(){}
int[][] A = new int[Mouse.arrayRow][Mouse.arrayCol]; //giving array size
{
for (int i = 0; i < A.length; i++) {
for (int j = 0; j < A[i].length; j++) {
for (int k = 0; k < Mouse.arrayDetail.length(); k++) {
//and its the problem doesn't take values
A[i][j] = Integer.parseInt(Mouse.arrayDetail.substring(k, k+1));
//and it gives me bunch of 000 it means its null and not taking values right ?
}
}
}
}
// road is 1
// wall is 0
// the roads that mouse went only one time is 2
// 3 is the road that mouse went but did not get success i mean the roads mouse went 2 times
public boolean walk(int row, int col) { //method to walk
boolean result = false;
if (check(row, col)){
A[row][col] = 3;
if (row == Mouse.endRow && col == Mouse.endCol) { //ending position
result = true;
}
else {
result = walk(row+1, col); //down
if(!result)
result = walk(row, col+1); //right
if(!result)
result = walk(row-1, col); //up
if(!result)
result = walk(row, col-1); //left
}
}
if (result == true) {
A[row][col] = 2;
}
return result;
}
public boolean check(int row, int col) { //check there is a road
boolean result = false;
if (row<A.length && row >=0 && col >=0 && col < A[0].length ) {
if (A[row][col] == 1) {
result = true;
}
}
return result;
}
public void print() { //method to print maze
for (int i = 0; i < A.length; i++) {
for (int j = 0; j < A[i].length; j++) {
System.out.print(A[i][j]);
}
System.out.println();
}
}
}
output
000000
000000
000000
000000
000000
No Solution
000000
000000
000000
000000
000000
// It looks like your post is mostly code; please add some more details. so i am adding nonsense texts. It looks like your post is mostly code; please add some more details. so i am adding nonsense texts.
You can fill the 2d array A like this
int[][] A = new int[Mouse.arrayRow][Mouse.arrayCol]; //giving array size
{
for (int i = 0; i < A.length; i++) {
for (int j = 0; j < A[i].length; j++) {
A[i][j] = Mouse.arrayDetail.charAt(i * Mouse.arrayCol + j) - '0';
}
}
}

Null Pointer Exception Error in a DoubleMatrix class [duplicate]

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 7 years ago.
I'm having trouble with a java problem. I get this error:
Exception in thread "main" java.lang.NullPointerException
at DoubleMatrix.getDim1Size(DoubleMatrix.java:28)
at Program3.main(Program3.java:16)
I don't understand where it is null
public class DoubleMatrix
{
private double[][] doubMatrix;
public DoubleMatrix(int firstDim, int secondDim, double upperLimit)
{
if(firstDim > 0 && secondDim > 0 && upperLimit > 0){
firstDim = 1;
secondDim = 1;
upperLimit = 1;
}
}
public DoubleMatrix(double[][] tempArray)
{
if(tempArray != null && tempArray.length != 0){
for(int i =0; i < tempArray.length; i++) {
doubMatrix = tempArray;
}
}
else{
tempArray = new double[1][1];
}
}
public int getDim1Size(){
int firstDim1 = doubMatrix.length;
return firstDim1;
}
public int getDim2Size(){
int secondDim1 = doubMatrix[0].length;
return secondDim1;
}
private void makeDoubMatrix(int firstDim, int secondDim, double upperLimit){
double[][] randomMatrix = new double[firstDim][secondDim];
for(int row = 0; row < doubMatrix.length; row++) {
for(int column = 0; column < doubMatrix[row].length; column++){
doubMatrix[row][column] = (double)(Math.random() * 100);
}
}
}
public DoubleMatrix addMatrix(DoubleMatrix arrayObj)
{
if(doubMatrix.length == arrayObj.doubMatrix.length && doubMatrix[0].length == arrayObj.doubMatrix[0].length){
double[][] TotalTwoDimArray = new double[doubMatrix.length][doubMatrix[0].length];
for(int row = 0; row < TotalTwoDimArray.length; row++){
for(int column = 0; column < TotalTwoDimArray[row].length; column++){
TotalTwoDimArray[row][column] = doubMatrix[row][column] + arrayObj.doubMatrix[row][column];
}
}
return new DoubleMatrix(TotalTwoDimArray);
}
return new DoubleMatrix(1, 1, 1);
}
public DoubleMatrix getTransposedMatrix(){
double[][] TransMatrix = new double[doubMatrix[0].length][doubMatrix.length];
for(int row = 0; row < doubMatrix.length; row++){
for(int column = 0; column < doubMatrix[row].length; column++){
TransMatrix[row][column] = doubMatrix[column][row];
}
}
return new DoubleMatrix(TransMatrix);
}
public DoubleMatrix multiplyMatrix(DoubleMatrix obj1)
{
if(doubMatrix[0].length == obj1.doubMatrix.length){
double[][] multipliedMatrix = new double[doubMatrix.length][obj1.doubMatrix[0].length];
for(int i = 0; i < multipliedMatrix.length; i++){
for(int j = 0; j < multipliedMatrix[i].length; j++){
for(int k = 0; k < doubMatrix[0].length; k++){
multipliedMatrix[i][j] = doubMatrix[i][k] * obj1.doubMatrix[k][j] + multipliedMatrix[i][j];
}
}
}
return new DoubleMatrix(multipliedMatrix);
}
return new DoubleMatrix(1, 1, 1);
}
public void printMatrix(String titles){
System.out.println(titles);
for(int row = 0; row < doubMatrix.length; row++){
for(int column = 0; column < doubMatrix[row].length; column++){
System.out.printf("%9.1f", doubMatrix[row][column]);
}
System.out.println();
}
}
}
// main in different class
public class Program3
{
public static void main(String[] args)
{
DoubleMatrix doubMatObj1;
DoubleMatrix doubMatObj2;
DoubleMatrix doubMatObj3;
int max = 10;
int min = 3;
int firstDim = (int)(Math.random() * (max - min + 1) + min);
int secondDim = (int)(Math.random() * (max - min + 1) + min);
doubMatObj1 = new DoubleMatrix(firstDim, secondDim, 100.);
doubMatObj2 = new DoubleMatrix(doubMatObj1.getDim1Size(), doubMatObj1.getDim2Size(), 100.);
doubMatObj3 = doubMatObj1.addMatrix(doubMatObj2);
doubMatObj1.printMatrix("First Matrix Object");
doubMatObj2.printMatrix("Second Matrix Object");
doubMatObj3.printMatrix("Result of Adding Matrix Objects");
doubMatObj2.printMatrix("Result of Transposing Matrix Object");
doubMatObj1.multiplyMatrix(doubMatObj2);
doubMatObj3.printMatrix("Result of Multiplying Matrix Objects");
}
}
In java, non primitives don't get initialized by just declaring them. So if you get a NullPointerException in a line like foo.bar(), you know that foo had to be null. In your case you have doubMatrix.length, which indicates that doubMatrix has never been initialized. Looking at your code, only the second constructor ever initializes that variable, so calling the first constructor will leave doubMatrix==null to always be true.
I hope that is enough info to help you fix your problem yourself (and similar problems in the future), but I am not going to post a working code example, since fixing your code yourself will be a good exercise!
On a sidenote, in your second constructor you have:
for(int i =0; i < tempArray.length; i++) {
doubMatrix = tempArray;
}
If tempArray.length is for example 5, you would assign the same value 5 times to the same variable. I don't know what you are trying to do there, but it is certainly not what you had in mind.

IF Statement Checking (Not Working Properly)

randomEmpty() returns a random coordinate on the n x n grid that is empty (Method works). randomAdjacent() uses randomEmpty() to select an EMPTY coordinate on the map. Comparisons are then made to see if this coordinate has an VALID adjacent coordinate that is NON-EMPTY. The PROBLEM is that randomAdjacent does not always return the coordinates of space with an adjacent NON-EMPTY space. It will always return valid coordinates but not the latter. I can't spot the problem. Can someone help me identify the problem?
public int[] randomEmpty()
{
Random r = new Random();
int[] random = new int[2];
int row = r.nextInt(array.length);
int column = r.nextInt(array.length);
while(!(isEmpty(row,column)))
{
row = r.nextInt(array.length);
column = r.nextInt(array.length);
}
random[0] = row+1;
random[1] = column+1;
return random;
}
public int[] randomAdjacent()
{
int[] adjacentToX = new int[8];
int[] adjacentToY = new int[8];
int[] adjacentFrom = randomEmpty();
int count;
boolean isTrue = false;
boolean oneAdjacentNotEmpty = false;
while(!(oneAdjacentNotEmpty))
{
count = 0;
if(validIndex(adjacentFrom,1,-1))
{
adjacentToX[count] = adjacentFrom[0]+1;
adjacentToY[count] = adjacentFrom[1]-1;
count++;
}
if(validIndex(adjacentFrom,0,-1))
{
adjacentToX[count] = adjacentFrom[0];
adjacentToY[count] = adjacentFrom[1]-1;
count++;
}
if(validIndex(adjacentFrom,-1,-1))
{
adjacentToX[count] = adjacentFrom[0]-1;
adjacentToY[count] = adjacentFrom[1]-1;
count++;
}
if(validIndex(adjacentFrom,-1,0))
{
adjacentToX[count] = adjacentFrom[0]-1;
adjacentToY[count] = adjacentFrom[1];
count++;
}
if(validIndex(adjacentFrom,-1,1))
{
adjacentToX[count] = adjacentFrom[0]-1;
adjacentToY[count] = adjacentFrom[1]+1;
count++;
}
if(validIndex(adjacentFrom,0,1))
{
adjacentToX[count] = adjacentFrom[0];
adjacentToY[count] = adjacentFrom[1]+1;
count++;
}
if(validIndex(adjacentFrom,1,1))
{
adjacentToX[count] = adjacentFrom[0]+1;
adjacentToY[count] = adjacentFrom[1]+1;
count++;
}
if(validIndex(adjacentFrom,1,0))
{
adjacentToX[count] = adjacentFrom[0]+1;
adjacentToY[count] = adjacentFrom[1];
count++;
}
for(int i = 0; i < count; i++)
{
if(!(isEmpty(adjacentToX[i],adjacentToY[i])))
{
oneAdjacentNotEmpty = true;
isTrue = true;
}
}
if(isTrue)
break;
else
adjacentFrom = randomEmpty();
}
return adjacentFrom;
}
public boolean validIndex(int[] a,int i, int j)
{
try
{
Pebble aPebble = array[a[0]+i][a[1]+j];
return true;
}
catch(ArrayIndexOutOfBoundsException e)
{
return false;
}
}
public void setCell(int xPos, int yPos, Pebble aPebble)
{
array[xPos-1][yPos-1] = aPebble;
}
public Pebble getCell(int xPos, int yPos)
{
return array[xPos-1][yPos-1];
}
JUNIT Test Performed:
#Test
public void testRandomAdjacent() {
final int size = 5;
final Board board2 = new Board(size);
board2.setCell(1, 1, Pebble.O);
board2.setCell(5, 5, Pebble.O);
int[] idx = board2.randomAdjacent();
int x = idx[0];
int y = idx[1];
boolean empty = true;
for (int i = x - 1; i <= x + 1; i++) {
for (int j = y - 1; j <= y + 1; j++) {
if ((i == x && j == y) || i < 1 || j < 1 || i > size || j > size) {
continue;
}
if (board2.getCell(i, j) != Pebble.EMPTY)
empty = false;
}
}
assertFalse(empty);// NEVER gets SET TO FALSE
assertEquals(Pebble.EMPTY, board2.getCell(x, y));
}
As for the answer: I got carried away optimizing your code for readability. I'd think it's most likely
if (board2.getCell(i, j) != Pebble.EMPTY)
empty = false;
causing the problem as getCell operates in 1-based coordinates, but i, j are in 0-based.
You should think about your logic overall. The way I see it, your code might never terminate as randomEmpty() could keep returning the same field over and over again for an undetermined period of time.
I took the liberty to recode your if-if-if cascade into utility method easier to read:
public boolean hasNonEmptyNeighbor(int[] adjacentFrom) {
for(int i = -1; i <= 1; ++i) {
for(int j = -1; j <= 1; ++j) {
if(validIndex(adjacentFrom, i, j) //Still inside the board
&& // AND
!isEmpty(adjacentFrom[0]+i //not empty
,adjacentFrom[1]+j)) {
return true;
}
}
}
return false;
}
Given my previous comment about random() being not the best of choices if you need to cover the full board, your main check (give me an empty cell with a non-empty neighbor) could be rewritten like this:
public void find() {
List<Point> foundPoints = new ArrayList<Point>();
for(int i = 0; i < Board.height; ++i) { //Assumes you have stored your height
for(int j = 0; j < Board.width; ++j) { //and your width
if(isEmpty(i, j) && hasNonEmptyNeighbor(new int[]{i,j})) {
//Found one.
foundPoints.add(new Point(i, j));
}
}
}
//If you need to return a RANDOM empty field with non-empty neighbor
//you could randomize over length of foundPoints here and select from that list.
}

2D array Null Pointer Exception error

public class DoubleMatrix
{
private double[][] doubMatrix;
public DoubleMatrix(int row, int col)
{
if(row > 0 && col > 0)
{
makeDoubMatrix(row,col);
}
else
{
row = 1;
col = 1;
}
}
public DoubleMatrix(double[][] tempArray)
{
if(tempArray != null)
{
for(int i = 0; i < tempArray.length-1;i++)
{
if(tempArray[i].length == tempArray[i+1].length)
{
tempArray = doubMatrix;
}
}
}
else
{
makeDoubMatrix(1,1);
}
}
public int getDim1()
{
return doubMatrix.length;
}
public int getDim2()
{
return doubMatrix[0].length;
}
private void makeDoubMatrix(int row, int col)
{
double[][] tempArray = new double[row][col];
for (int i = 0;i < row;i++)
{
for(int j = 0;j < col;j++)
{
tempArray[i][j] = Math.random() * (100);
doubMatrix[i][j] = tempArray[i][j];
}
}
}
public DoubleMatrix addMatrix(DoubleMatrix secondMatrix)
{
//this. doubMatrix = doubMatrix;
double[][] tempArray;
if(secondMatrix.doubMatrix.length == doubMatrix.length)
if(secondMatrix.doubMatrix[0].length == doubMatrix[0].length)
{
tempArray = new double[doubMatrix.length][doubMatrix[0].length];
for(int i = 0; i< secondMatrix.doubMatrix.length;i++)
for(int j = 0; j< secondMatrix.doubMatrix[i].length;j++ )
{
tempArray[i][j] = secondMatrix.doubMatrix[i][j] + doubMatrix[i][j];// add two matrices
}//end for
return new DoubleMatrix (tempArray);
}
return new DoubleMatrix(1,1);
}
public DoubleMatrix getTransposedMatrix()
{
double[][] tempArray = new double[doubMatrix.length][doubMatrix[0].length];
for(int i = 0;i < doubMatrix.length;i++)
for(int j = 0;j < doubMatrix[i].length;j++)
{
tempArray[j][i] = doubMatrix[i][j];// transposed matrix2
}//end for
return new DoubleMatrix(tempArray);
}
public DoubleMatrix multiplyingMatrix(DoubleMatrix secondMatrix)
{
double[][] tempArray = new double[secondMatrix.doubMatrix.length][doubMatrix[0].length];
//check if dimension of matrix1 equal to dimension of matrix2
if(secondMatrix.doubMatrix[0].length == doubMatrix.length)
if(doubMatrix.length == secondMatrix.doubMatrix[0].length)
{
for (int i = 0; i <secondMatrix.doubMatrix.length; i++)
{
for(int j = 0; j < doubMatrix[0].length; j++)
{
for (int k = 0; k < doubMatrix.length; k++)
{
tempArray[i][j] = tempArray[i][j] + secondMatrix.doubMatrix[i][k]*doubMatrix[k][j]; // multiply 2 matrices
}
}
}//end for
}// end if
return new DoubleMatrix(1,1);
}
public void printMatrix(String text)
{
System.out.println(text);// output string
for(int i = 0; i< doubMatrix.length;i++)
{
for(int j = 0; j< doubMatrix[i].length;j++ ) {
System.out.printf("%9.1f", doubMatrix[i][j]);// out put value for matrices
}
System.out.println();
}
}
}
public class Program3
{
public static void main(String[] args)
{
int num1 = (int) (Math.random()*(10-3+1)+3);
int num2 = (int) (Math.random()*(10-3+1)+3);
DoubleMatrix doubMatObj1 = new DoubleMatrix(num1,num2);
DoubleMatrix doubMatObj2 = new DoubleMatrix(doubMatObj1.getDim1(),doubMatObj1.getDim2());
DoubleMatrix doubMatObj3;
doubMatObj2.getDim1();
doubMatObj3 = doubMatObj1.addMatrix(doubMatObj2);
doubMatObj1.printMatrix("First Matrix Object");
doubMatObj2.printMatrix("Second Matrix Object");
doubMatObj3.printMatrix("Result of Adding Matrix Objects");
doubMatObj2 = doubMatObj2.getTransposedMatrix();
doubMatObj2.printMatrix("Result of inverting Matrix Object");
doubMatObj3 = doubMatObj1.multiplyingMatrix(doubMatObj2);
doubMatObj3.printMatrix("Result of Multiplying Matrix Objects");
}
}
Hi, I have a NullPointerException error in the last line statement of the makeDoubMatrix method as well the call makedoubMatrix in side if statement of the first constructor.
doubMatrix seems to be null when I already initialize it. How will I be able to fix this problem ?
You want to initialize the array, i.e.:
private double[][] doubMatrix = new double[size1][size2];
where size1 and size2 are arbitrary sizes. What you probably want is:
if(row > 0 && col > 0)
{
doubMatrix = new double[row][col];
makeDoubMatrix(row,col);
}
else
{
doubMatrix = new double[1][1];
makeDoubMatrix(1,1);
}
which initializes the array doubMatrix to a size of row*col if both row and col are greather than 0, and to 1*1 otherwise, then calls makeDoubMatrix with its initialized size (you could have this method call after the if-else, using doubMatrix.size and doubMatrix[0].size, but I think it's more readable now).
Change the second constructor (which takes a 2D array) using the same reasoning.
You're not initializing doubMatrix. The only line which assigns a value to doubMatrix is this commented out one:
//this. doubMatrix = doubMatrix;
(And that wouldn't help.)
Ask yourself where you think you're initializing it - where do you think you've got something like:
doubMatrix = new double[1][2];
... or an assignment copying a value from another array:
doubMatrix = someOtherVariable;
If you haven't got any statements assigning it a value, you aren't initializing it, so it will always have the default value of null.

Optimizing N queens puzzle

I'm trying to solve the problem of positioning N queens on NxN board without row, column and diagonal conflicts. I use an algorithm with minimizing the conflicts. Firstly, on each column randomly a queen is positioned. After that, of all conflict queens randomly one is chosen and for her column are calculated the conflicts of each possible position. Then, the queen moves to the best position with min number of conflicts. It works, but it runs extremely slow. My goal is to make it run fast for 10000 queens. Would you, please, suggest me some improvements or maybe notice some mistakes in my logic?
Here is my code:
public class Queen {
int column;
int row;
int d1;
int d2;
public Queen(int column, int row, int d1, int d2) {
super();
this.column = column;
this.row = row;
this.d1 = d1;
this.d2 = d2;
}
#Override
public String toString() {
return "Queen [column=" + column + ", row=" + row + ", d1=" + d1
+ ", d2=" + d2 + "]";
}
#Override
public boolean equals(Object obj) {
return ((Queen)obj).column == this.column && ((Queen)obj).row == this.row;
}
}
And:
import java.util.HashSet;
import java.util.Random;
public class SolveQueens {
public static boolean printBoard = false;
public static int N = 100;
public static int maxSteps = 2000000;
public static int[] queens = new int[N];
public static Random random = new Random();
public static HashSet<Queen> q = new HashSet<Queen>();
public static HashSet rowConfl[] = new HashSet[N];
public static HashSet d1Confl[] = new HashSet[2*N - 1];
public static HashSet d2Confl[] = new HashSet[2*N - 1];
public static void init () {
int r;
rowConfl = new HashSet[N];
d1Confl = new HashSet[2*N - 1];
d2Confl = new HashSet[2*N - 1];
for (int i = 0; i < N; i++) {
r = random.nextInt(N);
queens[i] = r;
Queen k = new Queen(i, r, i + r, N - 1 + i - r);
q.add(k);
if (rowConfl[k.row] == null) {
rowConfl[k.row] = new HashSet<Queen>();
}
if (d1Confl[k.d1] == null) {
d1Confl[k.d1] = new HashSet<Queen>();
}
if (d2Confl[k.d2] == null) {
d2Confl[k.d2] = new HashSet<Queen>();
}
((HashSet<Queen>)rowConfl[k.row]).add(k);
((HashSet<Queen>)d1Confl[k.d1]).add(k);
((HashSet<Queen>)d2Confl[k.d2]).add(k);
}
}
public static void print () {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
System.out.print(queens[i] == j ? "♕ " : "◻◻◻ ");
}
System.out.println();
}
System.out.println();
}
public static boolean checkItLinear() {
Queen r = choseConflictQueen();
if (r == null) {
return true;
}
Queen newQ = findNewBestPosition(r);
q.remove(r);
q.add(newQ);
rowConfl[r.row].remove(r);
d1Confl[r.d1].remove(r);
d2Confl[r.d2].remove(r);
if (rowConfl[newQ.row] == null) {
rowConfl[newQ.row] = new HashSet<Queen>();
}
if (d1Confl[newQ.d1] == null) {
d1Confl[newQ.d1] = new HashSet<Queen>();
}
if (d2Confl[newQ.d2] == null) {
d2Confl[newQ.d2] = new HashSet<Queen>();
}
((HashSet<Queen>)rowConfl[newQ.row]).add(newQ);
((HashSet<Queen>)d1Confl[newQ.d1]).add(newQ);
((HashSet<Queen>)d2Confl[newQ.d2]).add(newQ);
queens[r.column] = newQ.row;
return false;
}
public static Queen choseConflictQueen () {
HashSet<Queen> conflictSet = new HashSet<Queen>();
boolean hasConflicts = false;
for (int i = 0; i < 2*N - 1; i++) {
if (i < N && rowConfl[i] != null) {
hasConflicts = hasConflicts || rowConfl[i].size() > 1;
conflictSet.addAll(rowConfl[i]);
}
if (d1Confl[i] != null) {
hasConflicts = hasConflicts || d1Confl[i].size() > 1;
conflictSet.addAll(d1Confl[i]);
}
if (d2Confl[i] != null) {
hasConflicts = hasConflicts || d2Confl[i].size() > 1;
conflictSet.addAll(d2Confl[i]);
}
}
if (hasConflicts) {
int c = random.nextInt(conflictSet.size());
return (Queen) conflictSet.toArray()[c];
}
return null;
}
public static Queen findNewBestPosition(Queen old) {
int[] row = new int[N];
int min = Integer.MAX_VALUE;
int minInd = old.row;
for (int i = 0; i < N; i++) {
if (rowConfl[i] != null) {
row[i] = rowConfl[i].size();
}
if (d1Confl[old.column + i] != null) {
row[i] += d1Confl[old.column + i].size();
}
if (d2Confl[N - 1 + old.column - i] != null) {
row[i] += d2Confl[N - 1 + old.column - i].size();
}
if (i == old.row) {
row[i] = row[i] - 3;
}
if (row[i] <= min && i != minInd) {
min = row[i];
minInd = i;
}
}
return new Queen(old.column, minInd, old.column + minInd, N - 1 + old.column - minInd);
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
init();
int steps = 0;
while(!checkItLinear()) {
if (++steps > maxSteps) {
init();
steps = 0;
}
}
long endTime = System.currentTimeMillis();
System.out.println("Done for " + (endTime - startTime) + "ms\n");
if(printBoard){
print();
}
}
}
Edit:
Here is my a-little-bit-optimized solution with removing some unused objects and putting the queens on diagonal positions when initializing.
import java.util.Random;
import java.util.Vector;
public class SolveQueens {
public static boolean PRINT_BOARD = true;
public static int N = 10;
public static int MAX_STEPS = 5000;
public static int[] queens = new int[N];
public static Random random = new Random();
public static int[] rowConfl = new int[N];
public static int[] d1Confl = new int[2*N - 1];
public static int[] d2Confl = new int[2*N - 1];
public static Vector<Integer> conflicts = new Vector<Integer>();
public static void init () {
random = new Random();
for (int i = 0; i < N; i++) {
queens[i] = i;
}
}
public static int getD1Pos (int col, int row) {
return col + row;
}
public static int getD2Pos (int col, int row) {
return N - 1 + col - row;
}
public static void print () {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
System.out.print(queens[i] == j ? "Q " : "* ");
}
System.out.println();
}
System.out.println();
}
public static boolean hasConflicts() {
generateConflicts();
if (conflicts.isEmpty()) {
return false;
}
int r = random.nextInt(conflicts.size());
int conflQueenCol = conflicts.get(r);
int currentRow = queens[conflQueenCol];
int bestRow = currentRow;
int minConfl = getConflicts(conflQueenCol, queens[conflQueenCol]) - 3;
int tempConflCount;
for (int i = 0; i < N ; i++) {
tempConflCount = getConflicts(conflQueenCol, i);
if (i != currentRow && tempConflCount <= minConfl) {
minConfl = tempConflCount;
bestRow = i;
}
}
queens[conflQueenCol] = bestRow;
return true;
}
public static void generateConflicts () {
conflicts = new Vector<Integer>();
rowConfl = new int[N];
d1Confl = new int[2*N - 1];
d2Confl = new int[2*N - 1];
for (int i = 0; i < N; i++) {
int r = queens[i];
rowConfl[r]++;
d1Confl[getD1Pos(i, r)]++;
d2Confl[getD2Pos(i, r)]++;
}
for (int i = 0; i < N; i++) {
int conflictsCount = getConflicts(i, queens[i]) - 3;
if (conflictsCount > 0) {
conflicts.add(i);
}
}
}
public static int getConflicts(int col, int row) {
return rowConfl[row] + d1Confl[getD1Pos(col, row)] + d2Confl[getD2Pos(col, row)];
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
init();
int steps = 0;
while(hasConflicts()) {
if (++steps > MAX_STEPS) {
init();
steps = 0;
}
}
long endTime = System.currentTimeMillis();
System.out.println("Done for " + (endTime - startTime) + "ms\n");
if(PRINT_BOARD){
print();
}
}
}
Comments would have been helpful :)
Rather than recreating your conflict set and your "worst conflict" queen everything, could you create it once, and then just update the changed rows/columns?
EDIT 0:
I tried playing around with your code a bit. Since the code is randomized, it's hard to find out if a change is good or not, since you might start with a good initial state or a crappy one. I tried making 10 runs with 10 queens, and got wildly different answers, but results are below.
I psuedo-profiled to see which statements were being executed the most, and it turns out the inner loop statements in chooseConflictQueen are executed the most. I tried inserting a break to pull the first conflict queen if found, but it didn't seem to help much.
Grouping only runs that took more than a second:
I realize I only have 10 runs, which is not really enough to be statistically valid, but hey.
So adding breaks didn't seem to help. I think a constructive solution will likely be faster, but randomness will again make it harder to check.
Your approach is good : Local search algorithm with minimum-conflicts constraint. I would suggest try improving your initial state. Instead of randomly placing all queens, 1 per column, try to place them so that you minimize the number of conflicts. An example would be to try placing you next queen based on the position of the previous one ... or maybe position of previous two ... Then you local search will have less problematic columns to deal with.
If you randomly select, you could be selecting the same state as a previous state. Theoretically, you might never find a solution even if there is one.
I think you woud be better to iterate normally through the states.
Also, are you sure boards other than 8x8 are solvable?
By inspection, 2x2 is not, 3x3 is not, 4x4 is not.

Categories

Resources