BAEL-1298 - How to create a Sudoku solver (#3197)
* Code for Dependency Injection Article. * Added Java based configuration. Downloaded formatter.xml and reformatted all changed files. Manually changed tab into 4 spaces in XML configuration files. * BAEL-434 - Spring Roo project files generated by Spring Roo. No formatting applied. Added POM, java and resources folders. * Moved project from roo to spring-roo folder. * BAEL-838 Initial code showing how to remove last char - helper class and tests. * BAEL-838 Corrected Helper class and associated empty string test case. Added StringUtils.substing tests. * BAEL-838 Refromatted code using formatter.xml. Added Assert.assertEquals import. Renamed test to follow convention. Reordered tests. * BAEL-838 - Added regex method and updated tests. * BAEL-838 Added new line examples. * BAEL-838 Renamed RemoveLastChar class to StringHelper and added Java8 examples. Refactord code. * BAEL-838 Changed method names * BAEL-838 Tiny change to keep code consistant. Return null or empty. * BAEL-838 Removed unresolved conflict. * BAEL-821 New class that shows different rounding techniques. Updated POM. * BAEL-821 - Added unit test for different round methods. * BAEL-821 Changed test method name to follow the convention * BAEL-821 Added more test and updated round methods. * BAEL-837 - initial commit. A few examples of adding doubles. * BAEL-837 - Couple of smaller changes * BAEL-837 - Added jUnit test. * BAEL-579 Updated Spring Cloud Version I was getting error: java.lang.NoSuchMethodError: org.springframework.cloud.config.environment.Environment After version update, all is okay. * BAEL-579 Added actuator to Cloud Config Client. * BAEL-579 Enabled cloud bus and updated dependencies. * BAEL-579 Config Client using Spring Cloud Bus. * BAEL-579 Recreated Basic Config Server. * BAEL-579 Recreated Config Client. * BAEL-579 Removed test Git URL. * BAEL-579 Added Actuator to Config Client * BAEL-579 Added Spring Cloud Bus to Client. * BAEL-579 Server changes for Spring Cloud Bus Added dependencies and removed git.clone-on-start as this was causing server to throw errors after git properties change. * BAEL-579 Removed Git URL. * Revert "BAEL-579 Updated Spring Cloud Version" This reverts commit f775bf91e53a1ecfb9b70596688d7c8202bf495f. * Revert "BAEL-579 Config Client using Spring Cloud Bus." This reverts commit 1d96bc5761994a33af9a7a9aa5ab68604a5b44dc. * Revert "BAEL-579 Enabled cloud bus and updated dependencies." This reverts commit 7845da922d89d53506dd0fff387ea13694c50bc1. * Revert "BAEL-579 Added actuator to Cloud Config Client." This reverts commit 076657a26a57e0aa676989a4d97966a3b9d53e1c. * BAEL-579 Added missing dependency versions. * BAEL-579 Added missing dependency versions. * Updated gitignore * BAEL-1065 Simple performance check StringBuffer vs StringBuilder. * BAEL-1065 Added JMH benchmarks * BAEL-1298 Sudoku - Backtracking Algorithm * BAEL-1298 Sudoku - Backtracking Algorithm * BAEL-1298 Dancing Links Algorithm. Smaller changes to Backtracking * BAEL-1298 Resolve conflict - use most up-to-date POM * Updated code - mostly with CONSTANTS. Extracted methods. * Removed pointless Java8 code. Renamed constant
This commit is contained in:
parent
32e309ae4d
commit
f6cfff3f9d
@ -0,0 +1,103 @@
|
||||
package com.baeldung.algorithms.sudoku;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class BacktrackingAlgorithm {
|
||||
|
||||
private static int BOARD_SIZE = 9;
|
||||
private static int SUBSECTION_SIZE = 3;
|
||||
private static int BOARD_START_INDEX = 0;
|
||||
|
||||
private static int NO_VALUE = 0;
|
||||
private static int MIN_VALUE = 1;
|
||||
private static int MAX_VALUE = 9;
|
||||
|
||||
public static int[][] board = {
|
||||
{ 8, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 3, 6, 0, 0, 0, 0, 0 },
|
||||
{ 0, 7, 0, 0, 9, 0, 2, 0, 0 },
|
||||
{ 0, 5, 0, 0, 0, 7, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 4, 5, 7, 0, 0 },
|
||||
{ 0, 0, 0, 1, 0, 0, 0, 3, 0 },
|
||||
{ 0, 0, 1, 0, 0, 0, 0, 6, 8 },
|
||||
{ 0, 0, 8, 5, 0, 0, 0, 1, 0 },
|
||||
{ 0, 9, 0, 0, 0, 0, 4, 0, 0 }
|
||||
};
|
||||
|
||||
public static void main(String[] args) {
|
||||
BacktrackingAlgorithm solver = new BacktrackingAlgorithm();
|
||||
solver.solve(board);
|
||||
solver.printBoard();
|
||||
}
|
||||
|
||||
public void printBoard() {
|
||||
for (int row = BOARD_START_INDEX; row < BOARD_SIZE; row++) {
|
||||
for (int column = BOARD_START_INDEX; column < BOARD_SIZE; column++) {
|
||||
System.out.print(board[row][column] + " ");
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean solve(int[][] board) {
|
||||
for (int r = BOARD_START_INDEX; r < BOARD_SIZE; r++) {
|
||||
for (int c = BOARD_START_INDEX; c < BOARD_SIZE; c++) {
|
||||
if (board[r][c] == NO_VALUE) {
|
||||
for (int k = MIN_VALUE; k <= MAX_VALUE; k++) {
|
||||
board[r][c] = k;
|
||||
if (isValid(board, r, c) && solve(board)) {
|
||||
return true;
|
||||
} else {
|
||||
board[r][c] = NO_VALUE;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isValid(int[][] board, int r, int c) {
|
||||
return (rowConstraint(board, r) &&
|
||||
columnConstraint(board, c) &&
|
||||
subsectionConstraint(board, r, c));
|
||||
}
|
||||
|
||||
private boolean subsectionConstraint(int[][] board, int r, int c) {
|
||||
boolean[] constraint = new boolean[BOARD_SIZE];
|
||||
for (int i = (r / SUBSECTION_SIZE) * SUBSECTION_SIZE; i < (r / SUBSECTION_SIZE) * SUBSECTION_SIZE + SUBSECTION_SIZE; i++) {
|
||||
for (int j = (c / SUBSECTION_SIZE) * SUBSECTION_SIZE; j < (c / SUBSECTION_SIZE) * SUBSECTION_SIZE + SUBSECTION_SIZE; j++) {
|
||||
if (!checkConstraint(board, i, constraint, j)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean columnConstraint(int[][] board, int c) {
|
||||
boolean[] constraint = new boolean[BOARD_SIZE];
|
||||
for (int i = BOARD_START_INDEX; i < BOARD_SIZE; i++) {
|
||||
if (!checkConstraint(board, i, constraint, c)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean rowConstraint(int[][] board, int r) {
|
||||
boolean[] constraint = new boolean[BOARD_SIZE];
|
||||
for (int i = BOARD_START_INDEX; i < BOARD_SIZE; i++) {
|
||||
if (!checkConstraint(board, r, constraint, i)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean checkConstraint(int[][] board, int r, boolean[] constraint, int c) {
|
||||
if (board[r][c] >= MIN_VALUE && board[r][c] <= MAX_VALUE) {
|
||||
if (constraint[board[r][c] - 1] == false) {
|
||||
constraint[board[r][c] - 1] = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package com.baeldung.algorithms.sudoku;
|
||||
|
||||
class ColumnNode extends DancingNode {
|
||||
int size;
|
||||
String name;
|
||||
|
||||
public ColumnNode(String n) {
|
||||
super();
|
||||
size = 0;
|
||||
name = n;
|
||||
C = this;
|
||||
}
|
||||
|
||||
void cover() {
|
||||
unlinkLR();
|
||||
for (DancingNode i = this.D; i != this; i = i.D) {
|
||||
for (DancingNode j = i.R; j != i; j = j.R) {
|
||||
j.unlinkUD();
|
||||
j.C.size--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void uncover() {
|
||||
for (DancingNode i = this.U; i != this; i = i.U) {
|
||||
for (DancingNode j = i.L; j != i; j = j.L) {
|
||||
j.C.size++;
|
||||
j.relinkUD();
|
||||
}
|
||||
}
|
||||
relinkLR();
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
package com.baeldung.algorithms.sudoku;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class DancingLinks {
|
||||
|
||||
private ColumnNode header;
|
||||
private List<DancingNode> answer;
|
||||
|
||||
private void search(int k) {
|
||||
if (header.R == header) {
|
||||
handleSolution(answer);
|
||||
} else {
|
||||
ColumnNode c = selectColumnNodeHeuristic();
|
||||
c.cover();
|
||||
|
||||
for (DancingNode r = c.D; r != c; r = r.D) {
|
||||
answer.add(r);
|
||||
|
||||
for (DancingNode j = r.R; j != r; j = j.R) {
|
||||
j.C.cover();
|
||||
}
|
||||
|
||||
search(k + 1);
|
||||
|
||||
r = answer.remove(answer.size() - 1);
|
||||
c = r.C;
|
||||
|
||||
for (DancingNode j = r.L; j != r; j = j.L) {
|
||||
j.C.uncover();
|
||||
}
|
||||
}
|
||||
c.uncover();
|
||||
}
|
||||
}
|
||||
|
||||
private ColumnNode selectColumnNodeHeuristic() {
|
||||
int min = Integer.MAX_VALUE;
|
||||
ColumnNode ret = null;
|
||||
for (ColumnNode c = (ColumnNode) header.R; c != header; c = (ColumnNode) c.R) {
|
||||
if (c.size < min) {
|
||||
min = c.size;
|
||||
ret = c;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private ColumnNode makeDLXBoard(boolean[][] grid) {
|
||||
final int COLS = grid[0].length;
|
||||
final int ROWS = grid.length;
|
||||
|
||||
ColumnNode headerNode = new ColumnNode("header");
|
||||
ArrayList<ColumnNode> columnNodes = new ArrayList<ColumnNode>();
|
||||
|
||||
for (int i = 0; i < COLS; i++) {
|
||||
ColumnNode n = new ColumnNode(Integer.toString(i));
|
||||
columnNodes.add(n);
|
||||
headerNode = (ColumnNode) headerNode.hookRight(n);
|
||||
}
|
||||
headerNode = headerNode.R.C;
|
||||
|
||||
for (int i = 0; i < ROWS; i++) {
|
||||
DancingNode prev = null;
|
||||
for (int j = 0; j < COLS; j++) {
|
||||
if (grid[i][j] == true) {
|
||||
ColumnNode col = columnNodes.get(j);
|
||||
DancingNode newNode = new DancingNode(col);
|
||||
if (prev == null)
|
||||
prev = newNode;
|
||||
col.U.hookDown(newNode);
|
||||
prev = prev.hookRight(newNode);
|
||||
col.size++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
headerNode.size = COLS;
|
||||
|
||||
return headerNode;
|
||||
}
|
||||
|
||||
public DancingLinks(boolean[][] cover) {
|
||||
header = makeDLXBoard(cover);
|
||||
}
|
||||
|
||||
public void runSolver() {
|
||||
answer = new LinkedList<DancingNode>();
|
||||
search(0);
|
||||
}
|
||||
|
||||
public void handleSolution(List<DancingNode> answer) {
|
||||
int[][] result = parseBoard(answer);
|
||||
printSolution(result);
|
||||
}
|
||||
|
||||
int size = 9;
|
||||
|
||||
private int[][] parseBoard(List<DancingNode> answer) {
|
||||
int[][] result = new int[size][size];
|
||||
for (DancingNode n : answer) {
|
||||
DancingNode rcNode = n;
|
||||
int min = Integer.parseInt(rcNode.C.name);
|
||||
for (DancingNode tmp = n.R; tmp != n; tmp = tmp.R) {
|
||||
int val = Integer.parseInt(tmp.C.name);
|
||||
if (val < min) {
|
||||
min = val;
|
||||
rcNode = tmp;
|
||||
}
|
||||
}
|
||||
int ans1 = Integer.parseInt(rcNode.C.name);
|
||||
int ans2 = Integer.parseInt(rcNode.R.C.name);
|
||||
int r = ans1 / size;
|
||||
int c = ans1 % size;
|
||||
int num = (ans2 % size) + 1;
|
||||
result[r][c] = num;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void printSolution(int[][] result) {
|
||||
int N = result.length;
|
||||
for (int i = 0; i < N; i++) {
|
||||
String ret = "";
|
||||
for (int j = 0; j < N; j++) {
|
||||
ret += result[i][j] + " ";
|
||||
}
|
||||
System.out.println(ret);
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
package com.baeldung.algorithms.sudoku;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class DancingLinksAlgorithm {
|
||||
private static int BOARD_SIZE = 9;
|
||||
private static int SUBSECTION_SIZE = 3;
|
||||
private static int NO_VALUE = 0;
|
||||
private static int CONSTRAINTS = 4;
|
||||
private static int MIN_VALUE = 1;
|
||||
private static int MAX_VALUE = 9;
|
||||
private static int COVER_START_INDEX = 1;
|
||||
|
||||
public static int[][] board = {
|
||||
{ 8, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 3, 6, 0, 0, 0, 0, 0 },
|
||||
{ 0, 7, 0, 0, 9, 0, 2, 0, 0 },
|
||||
{ 0, 5, 0, 0, 0, 7, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 4, 5, 7, 0, 0 },
|
||||
{ 0, 0, 0, 1, 0, 0, 0, 3, 0 },
|
||||
{ 0, 0, 1, 0, 0, 0, 0, 6, 8 },
|
||||
{ 0, 0, 8, 5, 0, 0, 0, 1, 0 },
|
||||
{ 0, 9, 0, 0, 0, 0, 4, 0, 0 }
|
||||
};
|
||||
|
||||
public static void main(String[] args) {
|
||||
DancingLinksAlgorithm solver = new DancingLinksAlgorithm();
|
||||
solver.solve(board);
|
||||
}
|
||||
|
||||
public boolean solve(int[][] board) {
|
||||
boolean[][] cover = initializeExactCoverBoard(board);
|
||||
DancingLinks dlx = new DancingLinks(cover);
|
||||
dlx.runSolver();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private int getIndex(int row, int col, int num) {
|
||||
return (row - 1) * BOARD_SIZE * BOARD_SIZE + (col - 1) * BOARD_SIZE + (num - 1);
|
||||
}
|
||||
|
||||
private boolean[][] createExactCoverBoard() {
|
||||
boolean[][] R = new boolean[BOARD_SIZE * BOARD_SIZE * MAX_VALUE][BOARD_SIZE * BOARD_SIZE * CONSTRAINTS];
|
||||
|
||||
int hBase = 0;
|
||||
|
||||
// Cell constraint.
|
||||
for (int r = COVER_START_INDEX; r <= BOARD_SIZE; r++) {
|
||||
for (int c = COVER_START_INDEX; c <= BOARD_SIZE; c++, hBase++) {
|
||||
for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++) {
|
||||
int index = getIndex(r, c, n);
|
||||
R[index][hBase] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Row constrain.
|
||||
for (int r = COVER_START_INDEX; r <= BOARD_SIZE; r++) {
|
||||
for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++, hBase++) {
|
||||
for (int c1 = COVER_START_INDEX; c1 <= BOARD_SIZE; c1++) {
|
||||
int index = getIndex(r, c1, n);
|
||||
R[index][hBase] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Column constraint.
|
||||
for (int c = COVER_START_INDEX; c <= BOARD_SIZE; c++) {
|
||||
for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++, hBase++) {
|
||||
for (int r1 = COVER_START_INDEX; r1 <= BOARD_SIZE; r1++) {
|
||||
int index = getIndex(r1, c, n);
|
||||
R[index][hBase] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Subsection constraint
|
||||
for (int br = COVER_START_INDEX; br <= BOARD_SIZE; br += SUBSECTION_SIZE) {
|
||||
for (int bc = COVER_START_INDEX; bc <= BOARD_SIZE; bc += SUBSECTION_SIZE) {
|
||||
for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++, hBase++) {
|
||||
for (int rDelta = 0; rDelta < SUBSECTION_SIZE; rDelta++) {
|
||||
for (int cDelta = 0; cDelta < SUBSECTION_SIZE; cDelta++) {
|
||||
int index = getIndex(br + rDelta, bc + cDelta, n);
|
||||
R[index][hBase] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return R;
|
||||
}
|
||||
|
||||
private boolean[][] initializeExactCoverBoard(int[][] board) {
|
||||
boolean[][] R = createExactCoverBoard();
|
||||
for (int i = COVER_START_INDEX; i <= BOARD_SIZE; i++) {
|
||||
for (int j = COVER_START_INDEX; j <= BOARD_SIZE; j++) {
|
||||
int n = board[i - 1][j - 1];
|
||||
if (n != NO_VALUE) {
|
||||
for (int num = MIN_VALUE; num <= MAX_VALUE; num++) {
|
||||
if (num != n) {
|
||||
Arrays.fill(R[getIndex(i, j, num)], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return R;
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package com.baeldung.algorithms.sudoku;
|
||||
|
||||
class DancingNode {
|
||||
DancingNode L, R, U, D;
|
||||
ColumnNode C;
|
||||
|
||||
DancingNode hookDown(DancingNode n1) {
|
||||
assert (this.C == n1.C);
|
||||
n1.D = this.D;
|
||||
n1.D.U = n1;
|
||||
n1.U = this;
|
||||
this.D = n1;
|
||||
return n1;
|
||||
}
|
||||
|
||||
DancingNode hookRight(DancingNode n1) {
|
||||
n1.R = this.R;
|
||||
n1.R.L = n1;
|
||||
n1.L = this;
|
||||
this.R = n1;
|
||||
return n1;
|
||||
}
|
||||
|
||||
void unlinkLR() {
|
||||
this.L.R = this.R;
|
||||
this.R.L = this.L;
|
||||
}
|
||||
|
||||
void relinkLR() {
|
||||
this.L.R = this.R.L = this;
|
||||
}
|
||||
|
||||
void unlinkUD() {
|
||||
this.U.D = this.D;
|
||||
this.D.U = this.U;
|
||||
}
|
||||
|
||||
void relinkUD() {
|
||||
this.U.D = this.D.U = this;
|
||||
}
|
||||
|
||||
public DancingNode() {
|
||||
L = R = U = D = this;
|
||||
}
|
||||
|
||||
public DancingNode(ColumnNode c) {
|
||||
this();
|
||||
C = c;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user