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:
iaforek 2018-01-30 18:13:53 +00:00 committed by Grzegorz Piwowarek
parent 32e309ae4d
commit f6cfff3f9d
5 changed files with 430 additions and 0 deletions

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}