I am writing a sudoku board game generator. My code looks like this:
/**
* Created by szubansky on 3/9/16.
*/
public class SudokuBoard {
static int N = 9;
static int[][] grid = new int[N][N];
static void printGrid()
{
for (int row = 0; row < N; row++)
{
for (int col = 0; col < N; col++) {
System.out.printf("%5d", grid[row][col]);
}
System.out.println("\n");
}
}
private static boolean checkRow(int row, int num)
{
for( int col = 0; col < 9; col++ )
if(grid[row][col] == num)
return false;
return true;
}
private static boolean checkCol(int col, int num)
{
for( int row = 0; row < 9; row++ )
if(grid[row][col] == num)
return false;
return true;
}
private static boolean checkBox(int row, int col, int num)
{
row = (row / 3) * 3;
col = (col / 3) * 3;
for(int r = 0; r < 3; r++)
for(int c = 0; c < 3; c++)
if(grid[row+r][col+c] == num)
return false;
return true;
}
public static boolean fillBoard(int row, int col, int[][] grid)
{
if(row==9) {
row = 0;
if (++col == 9)
return true;
}
if(grid[row][col] != 0)
return fillBoard(row+1, col, grid);
for(int num=1 + (int)(Math.random() * ((9 - 1) + 1)); num<=9; num++)
{
if(checkRow(row,num) && checkCol(col,num) && checkBox(row,col,num)){
grid[row][col] = num;
if(fillBoard(row+1, col, grid))
return true;
}
}
grid[row][col] = 0;
return false;
}
static public void main(String[] args){
fillBoard(0, 0, grid);
printGrid();
}
}
the problem is with generating numbers in fillBoard backtrack algorithm
it basically puts 0 everywhere
when I change the num range in for loop to 10 it goes smoothly but my numbers need to be lower than 9
i can also change the beginning of backtrack fillBoard to row==8 and col==8 and it fills it correctly with random numbers leaving last row and last column with "0".
how to generate random numbers from 1 to 9 and fill all my grid?
Try this :
public static void main(String[] args) {
int[][] grid = new int[9][9];
randomFillGrid(grid, 1, 10);
for (int[] row : grid) {
System.out.println(Arrays.toString(row));
}
}
static void randomFillGrid(int[][] grid, int randomNumberOrigin, int randomNumberBound) {
PrimitiveIterator.OfInt iterator = ThreadLocalRandom.current()
.ints(randomNumberOrigin, randomNumberBound)
.iterator();
for (int[] row : grid) {
for (int i = 0; i < row.length; i++) {
row[i] = iterator.nextInt();
}
}
}
EDIT :
if you want to generate a sudoku grid i.e.
the same single integer may not appear twice in the same row, column or in any of the nine 3×3 subregions of the 9x9 playing board.
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
/**
* #author FaNaJ
*/
public class SudokuGenerator {
private static final int N = 9;
private static final int S_N = 3;
public static int[][] generateSudokuGrid() {
int[][] grid = new int[N][N];
int[] row = {1, 2, 3, 4, 5, 6, 7, 8, 9};
for (int y = 0; y < N; y++) {
int attempts = 0;
do {
if (++attempts > 1000000) { // Oops! I know (sometimes :) it's not a good algorithm...
return generateSudokuGrid();
}
shuffleArray(row);
} while (!isAllowed(grid, y, row));
System.arraycopy(row, 0, grid[y], 0, N);
}
return grid;
}
static boolean isAllowed(int[][] grid, int y, int[] row) {
// check columns
for (int i = 0; i < y; i++) {
for (int j = 0; j < N; j++) {
if (grid[i][j] == row[j]) {
return false;
}
}
}
// check sub grids
int startY = (y / S_N) * S_N;
for (int x = 0; x < N; x++) {
int startX = (x / S_N) * S_N;
for (int j = startX; j < startX + S_N; j++) {
if (j != x) {
for (int i = startY; i < y; i++) {
if (grid[i][j] == row[x]) {
return false;
}
}
}
}
}
return true;
}
static void shuffleArray(int[] array) {
Random random = ThreadLocalRandom.current();
for (int i = N; i > 1; i--) {
swap(array, i - 1, random.nextInt(i));
}
}
static void swap(int[] array, int i, int j) {
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
public static void main(String[] args) {
int[][] grid = generateSudokuGrid();
for (int[] row : grid) {
System.out.println(Arrays.toString(row));
}
}
}
output :
[3, 4, 6, 9, 1, 2, 7, 8, 5]
[9, 7, 2, 3, 8, 5, 4, 1, 6]
[5, 8, 1, 6, 7, 4, 3, 2, 9]
[7, 6, 3, 8, 2, 9, 1, 5, 4]
[4, 9, 5, 1, 6, 7, 2, 3, 8]
[2, 1, 8, 4, 5, 3, 6, 9, 7]
[6, 2, 4, 5, 9, 1, 8, 7, 3]
[8, 5, 7, 2, 3, 6, 9, 4, 1]
[1, 3, 9, 7, 4, 8, 5, 6, 2]
You can use Random class of java.utilpackage to generate random numbers. Below is an example that generates random numbers between 1 to 9:
Random rn = new Random();
int answer = rn.nextInt(9) + 1;
Before I start let me say that this while I'm not going tell you outright what all of your errors are, I will help you narrow your search and suggest a method to further debug your code.
Now to your problem: your array populated only with zero.
Only two lines can populate your array.
The first sets a spot in the array to have a random int value between 1 and 9 inclusive. If this random number doesn't pass a series of tests then that spot in the array is set to zero.
If one of these testers never returns true, the array will be populated with only zeros.
This leaves 4 functions that could be bugged.
-checkRow
-checkCol
-checkBox
-fillBoard
The first 2 functions are relatively simple so I can say with certainty that those work fine.
This leads only the checkBox and fillBoard functions under suspicion as causes of your bug.
At this point, this is where the debugging comes in.
Both of these functions contain loops.
One method to see if your loop is working is to compare a set of expected changes/return values to those which are actually gotten from the program.
To do this, either use a debugger
(assert statement + java -ea program.java and or other debugger methods)
Or, you could add print statements within your loop which print your gotten values for various variables/functions.
In any case, welcome to Stacks Overflow.
You mention that you are using a backtrack algorithm so I thought this would be fun to use a backtrack algorithm and try to demonstrate what it is. Below is a mash-up of your code and mine. I tried to stick to what you were doing and only add/change what I felt like I needed to.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* SudokuBoard.
*/
public class SudokuBoard {
// Dimension size of everything
static int N = 9;
// Sudoku grid
static int[][] grid = new int[N][N];
// Values that are potentially valid at each position in the sudoku grid
static int[][][] values = new int[N][N][N];
// Current index into the values
static int[][] index = new int[N][N];
/**
* Return a shuffled list of values from 1 - 9 with each value
* appearing only once.
*/
private static List<Integer> getValidValues(){
List<Integer> validValues = new ArrayList<>();
for(int i = 1; i < 10; i++){
validValues.add(i);
}
Collections.shuffle(validValues);
return validValues;
}
/**
* Populate the values array with shuffled values to choose from.
*/
private static void initValues()
{
for(int i = 0; i < values.length; i++){
for(int j = 0; j < values[i].length; j++){
List<Integer> validValues = getValidValues();
for(int k = 0; k < values[j].length; k++){
values[i][j][k] = validValues.get(k);
}
}
}
}
/**
* print the 2D sudoku grid.
*/
public static void printGrid()
{
for (int row = 0; row < N; row++)
{
for (int col = 0; col < N; col++) {
System.out.printf("%5d", grid[row][col]);
}
System.out.println("\n");
}
}
/**
* Check the row for validity.
*/
private static boolean checkRow(int row, int num)
{
for( int col = 0; col < 9; col++ )
if(grid[row][col] == num)
return false;
return true;
}
/**
* Check the col for validity.
*/
private static boolean checkCol(int col, int num)
{
for( int row = 0; row < 9; row++ )
if(grid[row][col] == num)
return false;
return true;
}
/**
* Check the box for validity.
*/
private static boolean checkBox(int row, int col,
int num, boolean testRowCol)
{
int theR = row;
int theC = col;
row = (row / 3) * 3;
col = (col / 3) * 3;
for(int r = 0; r < 3; r++) {
for (int c = 0; c < 3; c++) {
if (testRowCol) {
if (theR == row + r && theC == col + c){
continue;
}
}
if (grid[row + r][col + c] == num) {
return false;
}
}
}
return true;
}
/**
* Build the sudoku board.
*/
public static boolean fillBoard(int row, int col)
{
// if we are back at the beginning then success!
// but just for sanity we will check that its right.
if(row != 0 && col != 0){
if(row % 9 == 0 && col % 9 == 0){
return checkBoard();
}
}
// don't go out of range in the grid.
int r = row % 9;
int c = col % 9;
// get the index in the values array that we care about
int indexIntoValues = index[r][c];
// if the index is out of range then we have domain wipe out!
// lets reset the index and try to back up a step. Backtrack!
if(indexIntoValues > 8){
index[r][c] = 0;
// there are a few cases to cover
// if we are at the beginning and the index is out
// of range then failure. We should never get here.
if(row == 0 && col == 0) {
return false;
}
grid[r][c] = 0;
// if the row is at 0 then back the row up to row - 1 and
// the col - 1
if(r == 0 && c > 0) {
return fillBoard(row - 1, col - 1);
}
// if the row is greater than 0 then just back it up by 1
if(r > 0){
return fillBoard(row - 1, col);
}
}
index[r][c] += 1;
// get the value that we care about
int gridValue = values[r][c][indexIntoValues];
// is this value valid
if(checkRow(r,gridValue) && checkCol(c,gridValue) &&
checkBox(r,c,gridValue,false)){
// assign it and move on to the next one
grid[r][c] = gridValue;
return fillBoard(row+1, r == 8 ? col + 1 : col);
}
// the value is not valid so recurse and try the next value
return fillBoard(row, col);
}
/**
* This is a sanity check that the board is correct.
* Only run it after a solution is returned.
*/
private static boolean checkBoard(){
//the grid is N X N so just use N as the condition.
//check rows are ok.
for(int i = 0; i < N; i++){
for(int j = 0; j < N; j++){
//for each of the elements in the row compare against
//every other one except its self.
int toTest = grid[i][j];
//check that the digits in the elements are in the valid range.
if(toTest > 9 || toTest < 1)
return false;
for(int k = 0; k < N; k++){
//don't test me against myself
if(k == j)
continue;
//if i am equal to another in the row there is an error.
if(toTest == grid[i][k])
return false;
}
}
}
//check the cols are ok.
for(int i = 0; i < N; i++){
for(int j = 0; j < N; j++){
//flip i and j to go for cols.
int toTest = grid[j][i];
for(int k = 0; k < N; k++){
if(k == j)
continue;
if(toTest == grid[k][i])
return false;
}
}
}
//check blocks are ok
for(int i = 0; i < N; i++){
for(int j = 0; j < N; j++){
int toTest = grid[i][j];
if(!checkBox(i, j, toTest, true))
return false;
}
}
return true;
}
static public void main(String[] args){
initValues();
if(fillBoard(0, 0))
printGrid();
else
System.out.println("Something is broken");
}
}
So the main things that I added were two multidimensional arrays one is a values list, and the other is a current index into that values list. So the values array keeps a list of all of the valid values that each cell can have. I shuffled the values to give the random feel that you were looking for. We don't really care about value ordering here for any performance reasons, but we want it to be "random" in order to get a different board from run to run. Next the index array keeps track of where you are in the values array. At any point that your index in the index array goes out of bounds, that means that none of the values will work. This is the point that you need reset the index to zero and go back to the cell you came from to try a new value there. That is the backtracking step. I kept moving through the array sequential as you did but its not necessary. I think you could actually go to the variable that has the least amount of valid values, so the highest index in this case, and the search should be faster, but who cares right now, it's a small search, the depth is 81 and you win! I also included a sanity check that the board is valid.
Here is an output:
1 3 2 5 7 6 4 9 8
7 5 8 1 9 4 3 2 6
6 4 9 8 2 3 1 7 5
8 6 3 2 5 9 7 1 4
5 7 4 6 3 1 9 8 2
9 2 1 4 8 7 5 6 3
4 9 7 3 6 8 2 5 1
3 8 5 7 1 2 6 4 9
2 1 6 9 4 5 8 3 7
I hope this helps you in some way, this was a fun one.
Related
I have a given matrix called m and its dimensions are n by n and it contains whole numbers. I need to copy the numbers that appear just once to a new array called a.
I think the logic would be to have a for loop for each number in the matrix and compare it to every other number, but I don't know how to actually do that with code.
I can only use loops (no maps or such) and this is what I've come up with:
public static void Page111Ex14(int[][] m) {
int previous = 0, h = 0;
int[] a = new int[m.length*m[0].length];
for (int i = 0; i < m.length; i++) {
for (int j = 0; j < m[0].length; j++) {
previous = m[i][j];
if (m[i][j] != previous) {
a[h] = m[i][j];
h++;
}
}
}
It's probably not correct though.
Loop through it again to see if there's any repeated one. Assuming you can use labels, the answer might look a bit like that:
public static int[] getSingleInstanceArrayFromMatrix(int[][] m) {
int[] a = new int[m.length * m[0].length];
// Main loop.
for (int x = 0; x < m.length; x++) {
for (int y = 0; y < m[0].length; y++) {
// Gets the current number in the matrix.
int currentNumber = m[x][y];
// Boolean to check if the variable appears more than once.
boolean isSingle = true;
// Looping again through the array.
checkLoop:
for (int i = 0; i < m.length; i++) {
for (int j = 0; j < m[0].length; j++) {
// Assuring we are not talking about the same number in the same matrix position.
if (i != x || j != y) {
// If it is equal to our current number, we can update the variable and break.
if (m[i][j] == currentNumber) {
isSingle = false;
break checkLoop;
}
}
}
}
if (isSingle) {
a[(x * m.length) + y] = currentNumber;
}
}
}
return a;
}
Not sure if it's the most efficient, but I think it will work. It's somewhat hard to form your final array without the help of Lists or such. Since the unassigned values will default to 0, any actual zero (i.e. it's "supposed" to be there based on the matrix) will be undetected if you look up the returned array. But if there's such limitations I imagine that it's not crucially important.
This is one of those problems you can just throw a HashMap at and it just does your job for you. You traverse the 2d array, use a HashMap to store each element with its occurence, then traverse the HashMap and add all elements with occurence 1 to a list. Then convert this list to an array, which is what you're required to return.
This has O(n*n) complexity, where n is one dimension of the square matrix m.
import java.util.*;
import java.io.*;
class GetSingleOccurence
{
static int[] singleOccurence(int[][] m)
{
// work with a list so that we can append to it
List<Integer> aList = new ArrayList<Integer>();
HashMap<Integer, Integer> hm = new HashMap<>();
for (int row = 0; row < m.length; row++) {
for (int col = 0; col < m[row].length; col++) {
if (hm.containsKey(m[row][col]))
hm.put(m[row][col], 1 + hm.get(m[row][col]));
else
hm.put(m[row][col], 1);
}
}
for (Map.Entry entry : hm.entrySet())
{
if (Integer.parseInt(String.valueOf(entry.getValue())) == 1)
a.add(Integer.parseInt(String.valueOf(entry.getKey())));
}
// return a as an array
return a.toArray(new int[a.size()]);
}
public static void main(String args[])
{
// A 2D may of integers with some duplicates
int[][] m = { { 1, 2, 3, 4, 5 },
{ 6, 7, 8, 9, 10 },
{ 11, 12, 12, 14, 15 },
{ 16, 17, 18, 18, 20 },
{ 21, 22, 23, 24, 25 } };
a = singleOccurence(m);
}
}
It may be better to use a boolean array boolean[] dups to track duplicated numbers, so during the first pass this intermediate array is populated and the number of singles is counted.
Then create the resulting array of appropriate size, and if this array is not empty, in the second iteration over the dups copy the values marked as singles to the resulting array.
public static int[] getSingles(int[][] arr) {
int n = arr.length;
int m = arr[0].length;
boolean[] dups = new boolean[n * m];
int singles = 0;
for (int i = 0; i < dups.length; i++) {
if (dups[i]) continue; // skip the value known to be a duplicate
int curr = arr[i / m][i % m];
boolean dup = false;
for (int j = i + 1; j < dups.length; j++) {
if (curr == arr[j / m][j % m]) {
dup = true;
dups[j] = true;
}
}
if (dup) {
dups[i] = true;
} else {
singles++;
}
}
// debugging log
System.out.println("singles = " + singles + "; " + Arrays.toString(dups));
int[] res = new int[singles];
if (singles > 0) {
for (int i = 0, j = 0; i < dups.length; i++) {
if (!dups[i]) {
res[j++] = arr[i / m][i % m];
}
}
}
return res;
}
Test:
int[][] mat = {
{2, 2, 3, 3},
{4, 2, 0, 3},
{5, 4, 2, 1}
};
System.out.println(Arrays.toString(getSingles(mat)));
Output(including debugging log):
singles = 3; [true, true, true, true, true, true, false, true, false, true, true, false]
[0, 5, 1]
Your use of previous is merely an idea on the horizon. Remove it, and fill the one dimensional a. Finding duplicates with two nested for-loops would require n4 steps. However if you sort the array a, - order the values - which costs n² log n², you can find duplicates much faster.
Arrays.sort(a);
int previous = a[0];
for (int h = 1; h < a.length; ++h) {
if (a[h] == previous)...
previous = a[h];
...
It almost looks like this solution was already treated in class.
It doesn't look good:
previous = m[i][j];
if (m[i][j] != previous) {
a[h] = m[i][j];
h++;
}
you assigned m[i][j] to previous and then you check if if (m[i][j] != previous)?
Are there any limitations in the task as to the range from which the numbers can come from?
I am trying to add two differently sized matrices together. For example, the resultant matrix should be matOne[0][0]+matTwo[0][0]; however, I am having trouble taking into account their different sizes (if there's a missing value, it should be assumed it's a 0).
Here's my code:
int[][] go(int[][] matOne, int[][] matTwo)
{
int size= Math.max(matOne.length, matTwo.length);
int[][] matThree= new int [size][];
int c;
for (int i=0; i<size; i++) {
c= Math.max(matOne[i].length, matTwo[i].length);
for (int j = 0; j < c; j++) {
if (matOne[j].length > i) {
matThree[i][j] += matOne[i][j];
}
if (matTwo[j].length > i) {
matThree[i][j] += matTwo[i][j];
}
}
}
return matOne;
}
I see the following bugs in the code:
You never allocate the inner arrays. After you get size, you correctly create the outer array. After you get c, you forget to create the inner array for that row.
Right inside the i loop, one of matOne[i] and matTwo[i] is likely to fail eventually, whichever one is shorter.
Variable i iterates over rows, and variable j iterates over columns in a row, which means that the [i][j] in the += statements are correct, but that the matOne[j].length > i should have been matOne[i].length > j. Same for matTwo.
You return the wrong array.
Here is the fixed code, using better variable names:
static int[][] go(int[][] matOne, int[][] matTwo) {
int rows = Math.max(matOne.length, matTwo.length);
int[][] matThree = new int [rows][];
for (int r = 0; r < rows; r++) {
int cells = Math.max((matOne.length > r ? matOne[r].length : 0),
(matTwo.length > r ? matTwo[r].length : 0));
matThree[r] = new int[cells];
for (int c = 0; c < cells; c++) {
if (matOne.length > r && matOne[r].length > c) {
matThree[r][c] += matOne[r][c];
}
if (matTwo.length > r && matTwo[r].length > c) {
matThree[r][c] += matTwo[r][c];
}
}
}
return matThree;
}
Test
public static void main(String[] args) {
int[][] matOne = { { 1, 2, 3 }, { 4, 5, 6 } };
int[][] matTwo = { { 7, 8 }, { 9, 10 }, { 11, 12 } };
int[][] matThree = go(matOne, matTwo);
print(matOne);
System.out.println("+");
print(matTwo);
System.out.println("=");
print(matThree);
}
static void print(int[][] mat) {
for (int[] row : mat) {
for (int cell : row)
System.out.printf(" %2d", cell);
System.out.println();
}
}
Output
1 2 3
4 5 6
+
7 8
9 10
11 12
=
8 10 3
13 15 6
11 12
If you don't want the result to be a jagged array, i.e. you want the result to be a 2D rectangular matrix, change the code as follows:
static int[][] go(int[][] matOne, int[][] matTwo) {
int rows = Math.max(matOne.length, matTwo.length);
int cols = 0;
for (int[] row : matOne)
cols = Math.max(cols, row.length);
for (int[] row : matTwo)
cols = Math.max(cols, row.length);
int[][] matThree = new int [rows][cols];
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
if (matOne.length > r && matOne[r].length > c) {
matThree[r][c] += matOne[r][c];
}
if (matTwo.length > r && matTwo[r].length > c) {
matThree[r][c] += matTwo[r][c];
}
}
}
return matThree;
}
Output
1 2 3
4 5 6
+
7 8
9 10
11 12
=
8 10 3
13 15 6
11 12 0
Just before the inner loop; replace current calculation of 'c' and allocate a row for matThree.
c = 0;
if (i < matOne.length)
c = matOne[i].length;
if (i < matTwo.length && matTwo[i].length > c)
c = matTwo[i].length;
matThree[i] = new int[c];
The code inside the inner loop should be:
int elem1 = 0, elem2 = 0;
if (i < matOne.length && j < matOne[i].length)
elem1 = matOne[i][j];
if (i < matTwo.length && j < matTwo[i].length)
elem2 = matTwo[i][j];
matThree[i][j] = elem1 + elem2;
Firstly, it's necessary to allocate storage for the current row of matThree.
Secondly, you need to check that both row and column are within bounds for each matrix. For my taste it's clearest to explicitly extract the values, defaulting to zero, thus the variables elem1 and elem2.
The problem I am having is that I must create program that uses an object to reverse the elements of each row in a 2D Array and prints out the resulting matrix. The code I have only prints out the first 3 numbers of each row in reverse. This is my code:
class Reverse {
void matrixReverse(int[][] data) {
int row_val = data.length;
int col_val = data[0].length;
int[][] data_reverse = new int[row_val][col_val];
for(int row = row_val - 1; row >= 0; row--){
for(int col = col_val - 1; col >= 0; col--){
data_reverse[row_val - 1 - row][col_val - 1 - col] = data[row][col];
}
}
for(int row = 0; row < row_val; row++){
for(int col = 0; col < col_val; col++){
System.out.println(data_reverse[row][col]);
}
System.out.println();
}
}
}
public class ArrayReverse {
public static void main(String[] args) {
int[][] data = {
{3, 2, 5},
{1, 4, 4, 8, 13},
{9, 1, 0, 2},
{0, 2, 6, 3, -1, -8}
}; // Creates the array
Reverse reverse = new Reverse();
reverse.matrixReverse(data);
}
}
The output is:
6
2
0
0
1
9
4
4
1
5
2
3
As you can see, it's only printing the first 3 numbers of the rows but I need it to print all of them.
Your columns are variable width yet you are getting the width from the first row (which may not even exist!). You need to check the size of the array before accessing an element of it. You also have the array size parameters switched, start with row first.
To fix your specific problem, you need to set the col_val for each row inside the row loop.
void matrixReverse(int[][] data) {
int rowWidth = data.length;
int[][] reversedData = new int[rowWidth][];
for (int row = rowWidth - 1; row >= 0; row--) {
int colWidth = data[row].length;
int reverseRow = rowWidth - 1 - row;
reversedData[reverseRow] = new int[colWidth];
for (int col = colWidth - 1; col >= 0; col--) {
int reverseCol = colWidth - 1 - col;
reversedData[reverseRow][reverseCol] = data[row][col];
}
}
for (int row = 0; row < rowWidth; row++) {
int colWidth = reversedData[row].length;
for (int col = 0; col < colWidth; col++) {
System.out.println(reversedData[row][col]);
}
System.out.println();
}
}
P.S. row_val and col_val are bad names. They are widths not values and the naming convention in Java is colWidth and rowWidth not col_width or row_width.
I have a code for nth largest element in a sorted matrix (sorted row and column wise increasing order)
I had some problem doing the (findNextElement) part in the code
i.e if the row is exhausted, then go up one row and get the next element in that.
I have managed to do that, but the code looks kind of complex. (My code does work and produces the output correctly) I will post my code here
k is the Kth largest element
m, n are matrix dimensions (right now it just supports NxN matrix but can be modified to support MxN)
public int findkthLargestElement(int[][] input, int k, int m, int n) {
if (m <=1 || n <= 1 || k > m * n) {
return Integer.MIN_VALUE;
}
int i = 0;
int j = 0;
if (k < m && k < n) {
i = m - k;
j = n - k;
}
PriorityQueue<Element> maxQueue = new PriorityQueue(m, new Comparator<Element>() {
#Override
public int compare(Element a, Element b) {
return b.value - a.value;
}
});
Map<Integer, Integer> colMap = new HashMap<Integer, Integer>();
for (int row = i; row < m; row++) {
Element e = new Element(input[row][n - 1], row, n - 1);
colMap.put(row, n - 1);
maxQueue.add(e);
}
Element largest = new Element(0, 0, 0);
for (int l = 0; l < k; l++) {
largest = maxQueue.poll();
int row = largest.row;
colMap.put(row, colMap.get(row) - 1);
int col = colMap.get(row);
while (col < j && row > i) {
row = row - 1;
colMap.put(row, colMap.get(row) - 1);
col = Math.max(0, colMap.get(row));
}
Element nextLargest = new Element(input[row][Math.max(0, col)], row, Math.max(0, col));
maxQueue.add(nextLargest);
}
return largest.value;
}
I need some help in the for loop specifically, please suggest me a better way to accomplish the task.
I have my code running here
http://ideone.com/wIeZSo
Ok I found a a simple and effective way to make this work, I changed my for loop to ths
for (int l = 0; l < k; l++) {
largest = maxQueue.poll();
int row = largest.row;
colMap.put(row, colMap.get(row) - 1);
int col = colMap.get(row);
if (col < j) {
continue;
}
Element nextLargest = new Element(input[row][Math.max(0, col)], row, Math.max(0, col));
maxQueue.add(nextLargest);
}
If we are exhausted with a column then we do not add anymore items till we reach an element from some other column.
This will also work for matrix which are only sorted row wise but not column wise.
In response to the comment: Even if there are duplicate elements, I don't think that it is necessary to use sophisticated data structures like priority queues and maps, or even inner classes. I think it should be possible to simply start at the end of the array, walk to the beginning of the array, and count how often the value changed. Starting with the value "infinity" (or Integer.MAX_VALUE here), after the kth value change, one has the kth largest element.
public class KthLargestElementTest
{
public static void main (String[] args) throws java.lang.Exception
{
testDistinct();
testNonDistinct();
testAllEqual();
}
private static void testDistinct()
{
System.out.println("testDistinct");
int[][] input = new int[][]
{
{1, 2, 3, 4},
{8, 9, 10, 11},
{33, 44, 55, 66},
{99, 150, 170, 200}
};
for (int i = 1; i <= 17; i ++)
{
System.out.println(findkthLargestElement(input, i, 4, 4));
}
}
private static void testNonDistinct()
{
System.out.println("testNonDistinct");
int[][] input = new int[][]
{
{ 1, 1, 1, 4 },
{ 4, 4, 11, 11 },
{ 11, 11, 66, 66 },
{ 66, 150, 150, 150 }
};
for (int i = 1; i <= 6; i++)
{
System.out.println(findkthLargestElement(input, i, 4, 4));
}
}
private static void testAllEqual()
{
System.out.println("testAllEqual");
int[][] input = new int[][]
{
{ 4, 4, 4, 4 },
{ 4, 4, 4, 4 },
{ 4, 4, 4, 4 },
{ 4, 4, 4, 4 }
};
for (int i = 1; i <= 2; i++)
{
System.out.println(findkthLargestElement(input, i, 4, 4));
}
}
public static int findkthLargestElement(
int[][] input, int k, int m, int n)
{
int counter = 0;
int i=m*n-1;
int previousValue = Integer.MAX_VALUE;
while (i >= 0)
{
int value = input[i/n][i%n];
if (value < previousValue)
{
counter++;
}
if (counter == k)
{
return value;
}
previousValue = value;
i--;
}
if (counter == k)
{
return input[0][0];
}
System.out.println("There are no "+k+" different values!");
return Integer.MAX_VALUE;
}
}
I have an array something like the following.
int[][] myArray =
{{1, 2, 3, 0, 0, 1}
{1, 0, 4, 4, 0, 1}
{1, 2, 4, 3, 4, 0}
{2, 2, 0, 0, 2, 2}
{3, 0, 0, 3, 0, 0}
{4, 2, 3, 0, 0, 0}}
It would say that one had won because of the 1s in the three 1s in the first column. Two would not win because they are not in a "row".
I want to do some sort of win checking so that it finds three of the same number in a row, diagonal or column. Sort of like tic-tac-toe but with a larger grid. Before I used a messy set of if statements and goto statements. (It was written in Basic.) I have tried using a system where it found the directions from the last placed piece, in which there is a number of the same, but it didn't work properly. How can I do this in an easy and maintainable way?
Tried code:
private static boolean checkBoardCombinations(int[][] board, int inputX, int inputY) {
int currentPlayer = board[inputX-1][inputY-1];
boolean[][] directions = new boolean[3][3];
for(int y = 0; y >= -2; y--){
for(int x = 0; x >= -2; x--){
if(inputX+x >= 0 && inputX+x <= 7 && inputY+y >= 0 && inputY+y <= 7
&& (board[inputX+x][inputY+y] == currentPlayer)){
//System.out.printf("X: %s Y: %s", inputX+x, inputY+y);
directions[x+2][y+2] = true;
}
else{
directions[x+2][y+2] = false;
}
//System.out.printf("X: %s Y: %s B: %s,", inputX+x, inputY+y, directions[x+2][y+2]);
}
//System.out.println();
}
/*
for(int x = 0; x <= 2; x++){
for(int y = 0; y <= 2; y++){
System.out.print(directions[x][y] + " ");
}
System.out.println();
}
*/
return false;
}
Supposing the number of players are known, you can iterate over all the players one by one, and check if any player is forming a connection of required length or not.
Such code would look like following:
private int[][] grid; // Your array of size ROWS x COLUMNS
private final int ROWS = 6, COLUMNS = 6;
private final int CONSECUTIVE_CONNECTION_REQUIRED = 3;
// Returns true if given playerType is forming a connection, else false.
public boolean checkGrid(int playerType)
{
// Check downward
for (int i = 0; i <= ROWS - CONSECUTIVE_CONNECTION_REQUIRED; i++)
{
for (int j = 0; j < COLUMNS; j++)
{
int counter = 0;
for (int k = i; k < CONSECUTIVE_CONNECTION_REQUIRED + i; k++)
{
if (grid[k][j] == playerType)
counter++;
}
if (counter == CONSECUTIVE_CONNECTION_REQUIRED)
return true;
}
}
// Check across
for (int i = 0; i <= COLUMNS - CONSECUTIVE_CONNECTION_REQUIRED; i++)
{
for (int j = 0; j < ROWS; j++)
{
int counter = 0;
for (int k = i; k < CONSECUTIVE_CONNECTION_REQUIRED + i; k++)
{
if (grid[j][k] == playerType)
counter++;
}
if (counter == CONSECUTIVE_CONNECTION_REQUIRED)
return true;
}
}
// Check left to right diagonally
for (int i = 0; i <= ROWS - CONSECUTIVE_CONNECTION_REQUIRED; i++)
{
for (int j = 0; j <= COLUMNS - CONSECUTIVE_CONNECTION_REQUIRED; j++)
{
int counter = 0;
for (int k = i, m = j; k < CONSECUTIVE_CONNECTION_REQUIRED + i; k++, m++)
{
if (grid[k][m] == playerType)
counter++;
}
if (counter == CONSECUTIVE_CONNECTION_REQUIRED)
return true;
}
}
// Check right to left diagonally
for (int i = 0; i <= ROWS - CONSECUTIVE_CONNECTION_REQUIRED; i++)
{
for (int j = COLUMNS - 1; j >= COLUMNS - CONSECUTIVE_CONNECTION_REQUIRED; j--)
{
int counter = 0;
for (int k = i, m = j; k < CONSECUTIVE_CONNECTION_REQUIRED + i; k++, m--)
{
if (grid[k][m] == playerType)
counter++;
}
if (counter == CONSECUTIVE_CONNECTION_REQUIRED)
return true;
}
}
return false;
}
Where playerType is 0, 1, 2, 3 and so on...
You can use checkGrid() method like following:
for(int i = MIN_PLAYER_NUMBER; i <= MAX_PLAYER_NUMBER; i++)
{
if(checkGrid(i))
{
// Player i is forming the connection!!!
}
}
But if you don't want to iterate over your grid this many times, then drop your two dimensional array, and use a graph with adjacency list representation. Write a proper API for that which lets you make changes in your particular representation easily, and you can then find if any player is making a connection of particular length in the graph or not, in less iterations.
Although you already accepted an answer I thought to also submit you my answer for diversity :)
public static void main (String[] args)
{
int[][] myArray =
{{1, 2, 3, 0, 0, 1},
{1, 0, 4, 4, 0, 1},
{1, 2, 4, 3, 4, 0},
{2, 2, 0, 0, 2, 2},
{3, 0, 0, 3, 0, 0},
{4, 2, 3, 0, 0, 0}};
System.out.println(testForWinner(myArray));
}
/**
* Returns -1 if no winner
*/
static int testForWinner(int[][] ar) {
for(int i=0; i<ar.length; i++) {
for(int j=0; j<ar[i].length; j++) {
if(checkNext(ar, i, j, 0, 1, 1)) { //Check the element in the next column
return ar[i][j];
}
for(int k=-1; k<=1; k++) { //Check the three adjacent elements in the next row
if(checkNext(ar, i, j, 1, k, 1)) {
return ar[i][j];
}
}
}
}
return -1;
}
/**
* Step number `step` starting at `ar[i][j]` in direction `(di, dj)`.
* If we made 3 steps we have a winner
*/
static boolean checkNext(int[][] ar, int i, int j, int di, int dj, int step) {
if(step==3) {
return true;
}
if(i+di<0 || i+di>ar.length-1 || j+dj<0 || j+dj>ar[i].length-1) {
return false;
}
if(ar[i+di][j+dj]==ar[i][j]) {
return checkNext(ar, i+di, j+dj, di, dj, step+1);
}
return false;
}
See it in action: http://ideone.com/Ou2sRh