From cbf7bcbbbf8ba18d88a5c7af34e11c361102dc07 Mon Sep 17 00:00:00 2001 From: iaforek Date: Sun, 3 Dec 2017 19:48:43 +0000 Subject: [PATCH] BAEL-1298 Dancing Links Algorithm. Smaller changes to Backtracking --- .../sudoku/BacktrackingAlgorithm.java | 32 +++-- .../algorithms/sudoku/ColumnNode.java | 33 +++++ .../algorithms/sudoku/DancingLinks.java | 134 ++++++++++++++++++ .../sudoku/DancingLinksAlgorithm.java | 102 +++++++++++++ .../algorithms/sudoku/DancingNode.java | 50 +++++++ 5 files changed, 336 insertions(+), 15 deletions(-) create mode 100644 algorithms/src/main/java/com/baeldung/algorithms/sudoku/ColumnNode.java create mode 100644 algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java create mode 100644 algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java create mode 100644 algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingNode.java diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java index 6d41caf97e..effdcd9c04 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java @@ -2,23 +2,25 @@ package com.baeldung.algorithms.sudoku; public class BacktrackingAlgorithm { - public static void main(String[] args) { - - char[][] board = { - { '8', '.', '.', '.', '.', '.', '.', '.', '.' }, - { '.', '.', '3', '6', '.', '.', '.', '.', '.' }, - { '.', '7', '.', '.', '9', '.', '2', '.', '.' }, + static char[][] board = { + { '8', '.', '.', '.', '.', '.', '.', '.', '.' }, + { '.', '.', '3', '6', '.', '.', '.', '.', '.' }, + { '.', '7', '.', '.', '9', '.', '2', '.', '.' }, { '.', '5', '.', '.', '.', '7', '.', '.', '.' }, - { '.', '.', '.', '.', '4', '5', '7', '.', '.' }, - { '.', '.', '.', '1', '.', '.', '.', '3', '.' }, - { '.', '.', '1', '.', '.', '.', '.', '6', '8' }, + { '.', '.', '.', '.', '4', '5', '7', '.', '.' }, + { '.', '.', '.', '1', '.', '.', '.', '3', '.' }, + { '.', '.', '1', '.', '.', '.', '.', '6', '8' }, { '.', '.', '8', '5', '.', '.', '.', '1', '.' }, - { '.', '9', '.', '.', '.', '.', '4', '.', '.' } + { '.', '9', '.', '.', '.', '.', '4', '.', '.' } }; - + + public static void main(String[] args) { BacktrackingAlgorithm solver = new BacktrackingAlgorithm(); solver.solve(board); + solver.printBoard(); + } + public void printBoard() { for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { System.out.print(board[i][j] + " "); @@ -26,7 +28,7 @@ public class BacktrackingAlgorithm { System.out.println(); } } - + public boolean solve(char[][] board) { for (int r = 0; r < board.length; r++) { for (int c = 0; c < board[0].length; c++) { @@ -45,7 +47,7 @@ public class BacktrackingAlgorithm { } return true; } - + public boolean isValid(char[][] board, int r, int c) { boolean[] row = new boolean[9]; for (int i = 0; i < 9; i++) { @@ -68,7 +70,7 @@ public class BacktrackingAlgorithm { } } } - + boolean[] grid = new boolean[9]; for (int i = (r / 3) * 3; i < (r / 3) * 3 + 3; i++) { for (int j = (c / 3) * 3; j < (c / 3) * 3 + 3; j++) { @@ -79,7 +81,7 @@ public class BacktrackingAlgorithm { return false; } } - } + } } return true; } diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/ColumnNode.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/ColumnNode.java new file mode 100644 index 0000000000..48538344b6 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/ColumnNode.java @@ -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(); + } +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java new file mode 100644 index 0000000000..a30f8ecab5 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java @@ -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 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 columnNodes = new ArrayList(); + + 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(); + search(0); + } + + public void handleSolution(List answer) { + int[][] result = parseBoard(answer); + printSolution(result); + } + + int size = 9; + + private int[][] parseBoard(List 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(); + } +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java new file mode 100644 index 0000000000..5cff88c644 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java @@ -0,0 +1,102 @@ +package com.baeldung.algorithms.sudoku; + +import java.util.*; + +public class DancingLinksAlgorithm { + private static int S = 9; + private static int SIDE = 3; + + static char[][] board = { + { '8', '.', '.', '.', '.', '.', '.', '.', '.' }, + { '.', '.', '3', '6', '.', '.', '.', '.', '.' }, + { '.', '7', '.', '.', '9', '.', '2', '.', '.' }, + { '.', '5', '.', '.', '.', '7', '.', '.', '.' }, + { '.', '.', '.', '.', '4', '5', '7', '.', '.' }, + { '.', '.', '.', '1', '.', '.', '.', '3', '.' }, + { '.', '.', '1', '.', '.', '.', '.', '6', '8' }, + { '.', '.', '8', '5', '.', '.', '.', '1', '.' }, + { '.', '9', '.', '.', '.', '.', '4', '.', '.' } + }; + + public static void main(String[] args) { + + DancingLinksAlgorithm solver = new DancingLinksAlgorithm(); + solver.solve(board); + } + + public boolean solve(char[][] 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) * S * S + (col - 1) * S + (num - 1); + } + + private boolean[][] createExactCoverBoard() { + boolean[][] R = new boolean[9 * 9 * 9][9 * 9 * 4]; + + int hBase = 0; + + for (int r = 1; r <= S; r++) { + for (int c = 1; c <= S; c++, hBase++) { + for (int n = 1; n <= S; n++) { + int index = getIndex(r, c, n); + R[index][hBase] = true; + } + } + } + + for (int r = 1; r <= S; r++) { + for (int n = 1; n <= S; n++, hBase++) { + for (int c1 = 1; c1 <= S; c1++) { + int index = getIndex(r, c1, n); + R[index][hBase] = true; + } + } + } + + for (int c = 1; c <= S; c++) { + for (int n = 1; n <= S; n++, hBase++) { + for (int r1 = 1; r1 <= S; r1++) { + int index = getIndex(r1, c, n); + R[index][hBase] = true; + } + } + } + + for (int br = 1; br <= S; br += SIDE) { + for (int bc = 1; bc <= S; bc += SIDE) { + for (int n = 1; n <= S; n++, hBase++) { + for (int rDelta = 0; rDelta < SIDE; rDelta++) { + for (int cDelta = 0; cDelta < SIDE; cDelta++) { + int index = getIndex(br + rDelta, bc + cDelta, n); + R[index][hBase] = true; + } + } + } + } + } + return R; + } + + private boolean[][] initializeExactCoverBoard(char[][] sudoku) { + boolean[][] R = createExactCoverBoard(); + for (int i = 1; i <= S; i++) { + for (int j = 1; j <= S; j++) { + char n = sudoku[i - 1][j - 1]; + if (n != '.') { + for (int num = 1; num <= S; num++) { + if ((char) ('0' + num) != n) { + Arrays.fill(R[getIndex(i, j, num)], false); + } + } + } + } + } + return R; + } +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingNode.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingNode.java new file mode 100644 index 0000000000..13dc3f2b57 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingNode.java @@ -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; + } +} \ No newline at end of file