Aritalab:Lecture/Programming/Java/AlgorithmX
From Metabolomics.JP
< Aritalab:Lecture | Programming | Java(Difference between revisions)
| Line 1: | Line 1: | ||
| − | + | D KnuthのアルゴリズムXのJava実装です。Sudoku用です。 | |
| + | |||
| + | <pre> | ||
| + | import java.io.BufferedReader; | ||
| + | import java.io.FileReader; | ||
| + | import java.util.Iterator; | ||
| + | import java.util.Stack; | ||
| + | |||
| + | public class AlgorithmX { | ||
| + | class Cell { | ||
| + | Cell up, down, left, right; | ||
| + | Header top; | ||
| + | Object value = null; | ||
| + | |||
| + | Cell() { | ||
| + | } | ||
| + | |||
| + | Cell(Object v) { | ||
| + | value = v; | ||
| + | left = right = this; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | class Header extends Cell { | ||
| + | String label = null; | ||
| + | int size = 0; | ||
| + | |||
| + | Header(String l) { | ||
| + | label = l; | ||
| + | top = this; | ||
| + | up = down = left = right = this; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | /** | ||
| + | * @author Masanori Arita | ||
| + | */ | ||
| + | class ExactCoverMatrix { | ||
| + | Header head = new Header(null); | ||
| + | |||
| + | Stack<Cell> stack = new Stack<Cell>(); | ||
| + | |||
| + | int colSize; | ||
| + | |||
| + | /** | ||
| + | * Matrix for Exact Cover (行が集合の数、列が要素の数)における列成分を定義する。 | ||
| + | * | ||
| + | * @param labels | ||
| + | * label for columns | ||
| + | */ | ||
| + | void initializeHeaders(String[] labels) { | ||
| + | colSize = labels.length; | ||
| + | for (int i = 0; i < colSize; i++) { | ||
| + | Header h = new Header(labels[i]); | ||
| + | h.right = head; | ||
| + | h.left = head.left; | ||
| + | head.left.right = h; | ||
| + | head.left = h; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | /** | ||
| + | * 文字配列を受け取り、該当する列にセルを作成する。 受け取る配列はinitializeHeadersと同じ順序とする。 | ||
| + | * | ||
| + | * @param labels | ||
| + | * 文字配列 | ||
| + | * @param values | ||
| + | * 相当するセルの値 | ||
| + | */ | ||
| + | void addRow(String[] labels, Object[] values) { | ||
| + | Cell first = null, last = null; | ||
| + | Header h = (Header) head.right; | ||
| + | for (int i = 0; i < labels.length; i++) { | ||
| + | String l = labels[i]; | ||
| + | while (!h.label.equals(l)) { | ||
| + | h = (Header) h.right; | ||
| + | if (h == head) { | ||
| + | System.err.println("No row of name: " + l); | ||
| + | i++; | ||
| + | break; | ||
| + | } | ||
| + | } | ||
| + | // セルを挿入 | ||
| + | Cell tmp = new Cell(values[i]); | ||
| + | tmp.top = h; | ||
| + | h.size++; | ||
| + | // 横のリンク | ||
| + | if (first == null) | ||
| + | first = last = tmp; | ||
| + | else { | ||
| + | last.right = tmp; | ||
| + | tmp.left = last; | ||
| + | last = tmp; | ||
| + | } | ||
| + | // 縦のリンク | ||
| + | Cell above = h.up; | ||
| + | above.down = h.up = tmp; | ||
| + | tmp.up = above; | ||
| + | tmp.down = h; | ||
| + | } | ||
| + | // 横のリンクを完成させる | ||
| + | last.right = first; | ||
| + | first.left = last; | ||
| + | } | ||
| + | |||
| + | /** | ||
| + | * Brute forceで探索する部分 | ||
| + | * | ||
| + | * @param k | ||
| + | * 探索の深さ。このプログラムでは使用せず。 | ||
| + | */ | ||
| + | void search(int k) { | ||
| + | if (head.right == head) { | ||
| + | printSudokuSolution(); | ||
| + | return; | ||
| + | } | ||
| + | Header c = chooseColumn(); | ||
| + | coverColumn(c); | ||
| + | for (Cell r = c.down; r != c; r = r.down) { | ||
| + | stack.push(r); | ||
| + | for (Cell j = r.right; j != r; j = j.right) | ||
| + | coverColumn(j.top); | ||
| + | search(k + 1); | ||
| + | stack.pop(); | ||
| + | for (Cell j = r.left; j != r; j = j.left) | ||
| + | uncoverColumn(j.top); | ||
| + | } | ||
| + | uncoverColumn(c.top); | ||
| + | } | ||
| + | |||
| + | void coverColumn(Header c) { | ||
| + | colSize--; | ||
| + | c.right.left = c.left; | ||
| + | c.left.right = c.right; | ||
| + | for (Cell i = c.down; i != c; i = i.down) | ||
| + | for (Cell j = i.right; j != i; j = j.right) { | ||
| + | j.down.up = j.up; | ||
| + | j.up.down = j.down; | ||
| + | j.top.size--; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | void uncoverColumn(Header c) { | ||
| + | colSize++; | ||
| + | for (Cell i = c.up; i != c; i = i.up) | ||
| + | for (Cell j = i.left; j != i; j = j.left) { | ||
| + | j.top.size++; | ||
| + | j.down.up = j; | ||
| + | j.up.down = j; | ||
| + | } | ||
| + | c.right.left = c; | ||
| + | c.left.right = c; | ||
| + | } | ||
| + | |||
| + | /** | ||
| + | * マトリクス中で一番要素数の少ない列を返す | ||
| + | * | ||
| + | * @return | ||
| + | */ | ||
| + | Header chooseColumn() { | ||
| + | Header maxCol = (Header) head.right; | ||
| + | Header h = maxCol; | ||
| + | for (int i = 1; i < colSize; i++) { | ||
| + | h = (Header) h.right; | ||
| + | if (h.size < maxCol.size) | ||
| + | maxCol = h; | ||
| + | } | ||
| + | return maxCol; | ||
| + | } | ||
| + | |||
| + | void printSolution() { | ||
| + | for (Iterator<Cell> i = stack.iterator(); i.hasNext();) { | ||
| + | Cell c = i.next(); | ||
| + | System.out.print(c.top.label + " "); | ||
| + | for (Cell j = c.right; j != c; j = j.right) | ||
| + | System.out.print(j.top.label + " "); | ||
| + | System.out.println(); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | void printSudokuSolution() { | ||
| + | int[][] answer = new int[9][9]; | ||
| + | for (Iterator<Cell> I = stack.iterator(); I.hasNext();) { | ||
| + | Cell c = I.next(); | ||
| + | while (!c.top.label.startsWith("S")) | ||
| + | c = c.right; | ||
| + | int i = c.top.label.charAt(1) - '0' - 1; | ||
| + | int j = c.top.label.charAt(2) - '0' - 1; | ||
| + | int n = c.right.top.label.charAt(2) - '0'; | ||
| + | answer[i][j] = n; | ||
| + | } | ||
| + | for (int i = 0; i < 9; i++) { | ||
| + | for (int j = 0; j < 9; j++) | ||
| + | System.out.print(answer[i][j] + " "); | ||
| + | System.out.println(); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | } | ||
| + | |||
| + | /* | ||
| + | * 数独読み込みルーチン | ||
| + | */ | ||
| + | public int[][] readSudoku(String file) { | ||
| + | // スペース区切りの9x9数を読み込んで9x9の二次元配列を返す | ||
| + | int[][] sudoku = new int[9][9]; | ||
| + | try { | ||
| + | BufferedReader br = new BufferedReader(new FileReader(file)); | ||
| + | String line; | ||
| + | int row = 0; | ||
| + | while (((line = br.readLine()) != null) && row < 9) { | ||
| + | if (line.startsWith(";")) | ||
| + | continue; | ||
| + | String[] numbers = line.split(" "); | ||
| + | for (int i = 0; i < 9; i++) | ||
| + | sudoku[row][i] = Integer.parseInt(numbers[i]); | ||
| + | row++; | ||
| + | } | ||
| + | } catch (Exception e) { | ||
| + | e.printStackTrace(); | ||
| + | } | ||
| + | return sudoku; | ||
| + | } | ||
| + | |||
| + | void solveSudoku(int[][] sudoku) { | ||
| + | String[] label = new String[] { "S", "C", "R", "B" }; | ||
| + | String[] S = new String[9 * 9 * 4]; | ||
| + | for (int i = 0; i < 4; i++) | ||
| + | for (int j = 0; j < 9; j++) | ||
| + | for (int k = 0; k < 9; k++) | ||
| + | S[i * 9 * 9 + j * 9 + k] = (label[i] + (j + 1)) + (k + 1); | ||
| + | ExactCoverMatrix mat = new ExactCoverMatrix(); | ||
| + | mat.initializeHeaders(S); | ||
| + | for (int i = 0; i < 9; i++) | ||
| + | for (int j = 0; j < 9; j++) { | ||
| + | if (sudoku[i][j] == 0) { | ||
| + | for (int n = 1; n <= 9; n++) { | ||
| + | mat.addRow(new String[] { ("S" + (i + 1) + (j + 1)), | ||
| + | ("C" + (j + 1) + n), ("R" + (i + 1) + n), | ||
| + | ("B" + ((i / 3) * 3 + (j / 3) + 1) + n) }, | ||
| + | new Object[] { null, null, null, null }); | ||
| + | } | ||
| + | } else { | ||
| + | int n = sudoku[i][j]; | ||
| + | mat.addRow(new String[] { ("S" + (i + 1) + (j + 1)), | ||
| + | ("C" + (j + 1) + n), ("R" + (i + 1) + n), | ||
| + | ("B" + ((i / 3) * 3 + (j / 3) + 1) + n) }, | ||
| + | new Object[] { null, null, null, null }); | ||
| + | } | ||
| + | } | ||
| + | mat.search(0); | ||
| + | } | ||
| + | |||
| + | static public void main(String[] args) { | ||
| + | AlgorithmX dl = new AlgorithmX(); | ||
| + | int[][] sudoku = dl.readSudoku("c:/home/sudokuSample.txt"); | ||
| + | dl.solveSudoku(sudoku); | ||
| + | } | ||
| + | } | ||
| + | </pre> | ||
Latest revision as of 14:49, 22 August 2017
D KnuthのアルゴリズムXのJava実装です。Sudoku用です。
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.Iterator;
import java.util.Stack;
public class AlgorithmX {
class Cell {
Cell up, down, left, right;
Header top;
Object value = null;
Cell() {
}
Cell(Object v) {
value = v;
left = right = this;
}
}
class Header extends Cell {
String label = null;
int size = 0;
Header(String l) {
label = l;
top = this;
up = down = left = right = this;
}
}
/**
* @author Masanori Arita
*/
class ExactCoverMatrix {
Header head = new Header(null);
Stack<Cell> stack = new Stack<Cell>();
int colSize;
/**
* Matrix for Exact Cover (行が集合の数、列が要素の数)における列成分を定義する。
*
* @param labels
* label for columns
*/
void initializeHeaders(String[] labels) {
colSize = labels.length;
for (int i = 0; i < colSize; i) {
String l = labels[i];
while (!h.label.equals(l)) {
h = (Header) h.right;
if (h == head) {
System.err.println("No row of name: " + l);
i;
// 横のリンク
if (first == null)
first = last = tmp;
else {
last.right = tmp;
tmp.left = last;
last = tmp;
}
// 縦のリンク
Cell above = h.up;
above.down = h.up = tmp;
tmp.up = above;
tmp.down = h;
}
// 横のリンクを完成させる
last.right = first;
first.left = last;
}
/**
* Brute forceで探索する部分
*
* @param k
* 探索の深さ。このプログラムでは使用せず。
*/
void search(int k) {
if (head.right == head) {
printSudokuSolution();
return;
}
Header c = chooseColumn();
coverColumn(c);
for (Cell r = c.down; r != c; r = r.down) {
stack.push(r);
for (Cell j = r.right; j != r; j = j.right)
coverColumn(j.top);
search(k + 1);
stack.pop();
for (Cell j = r.left; j != r; j = j.left)
uncoverColumn(j.top);
}
uncoverColumn(c.top);
}
void coverColumn(Header c) {
colSize--;
c.right.left = c.left;
c.left.right = c.right;
for (Cell i = c.down; i != c; i = i.down)
for (Cell j = i.right; j != i; j = j.right) {
j.down.up = j.up;
j.up.down = j.down;
j.top.size--;
}
}
void uncoverColumn(Header c) {
colSize;
j.down.up = j;
j.up.down = j;
}
c.right.left = c;
c.left.right = c;
}
/**
* マトリクス中で一番要素数の少ない列を返す
*
* @return
*/
Header chooseColumn() {
Header maxCol = (Header) head.right;
Header h = maxCol;
for (int i = 1; i < colSize; i++) {
h = (Header) h.right;
if (h.size < maxCol.size)
maxCol = h;
}
return maxCol;
}
void printSolution() {
for (Iterator<Cell> i = stack.iterator(); i.hasNext();) {
Cell c = i.next();
System.out.print(c.top.label + " ");
for (Cell j = c.right; j != c; j = j.right)
System.out.print(j.top.label + " ");
System.out.println();
}
}
void printSudokuSolution() {
int[][] answer = new int[9][9];
for (Iterator<Cell> I = stack.iterator(); I.hasNext();) {
Cell c = I.next();
while (!c.top.label.startsWith("S"))
c = c.right;
int i = c.top.label.charAt(1) - '0' - 1;
int j = c.top.label.charAt(2) - '0' - 1;
int n = c.right.top.label.charAt(2) - '0';
answer[i][j] = n;
}
for (int i = 0; i < 9; i)
System.out.print(answer[i][j] + " ");
System.out.println();
}
}
}
/*
* 数独読み込みルーチン
*/
public int[][] readSudoku(String file) {
// スペース区切りの9x9数を読み込んで9x9の二次元配列を返す
int[][] sudoku = new int[9][9];
try {
BufferedReader br = new BufferedReader(new FileReader(file));
String line;
int row = 0;
while (((line = br.readLine()) != null) && row < 9) {
if (line.startsWith(";"))
continue;
String[] numbers = line.split(" ");
for (int i = 0; i < 9; i;
}
} catch (Exception e) {
e.printStackTrace();
}
return sudoku;
}
void solveSudoku(int[][] sudoku) {
String[] label = new String[] { "S", "C", "R", "B" };
String[] S = new String[9 * 9 * 4];
for (int i = 0; i < 4; i)
for (int k = 0; k < 9; k++)
S[i * 9 * 9 + j * 9 + k] = (label[i] + (j + 1)) + (k + 1);
ExactCoverMatrix mat = new ExactCoverMatrix();
mat.initializeHeaders(S);
for (int i = 0; i < 9; i) {
if (sudoku[i][j] == 0) {
for (int n = 1; n <= 9; n++) {
mat.addRow(new String[] { ("S" + (i + 1) + (j + 1)),
("C" + (j + 1) + n), ("R" + (i + 1) + n),
("B" + ((i / 3) * 3 + (j / 3) + 1) + n) },
new Object[] { null, null, null, null });
}
} else {
int n = sudoku[i][j];
mat.addRow(new String[] { ("S" + (i + 1) + (j + 1)),
("C" + (j + 1) + n), ("R" + (i + 1) + n),
("B" + ((i / 3) * 3 + (j / 3) + 1) + n) },
new Object[] { null, null, null, null });
}
}
mat.search(0);
}
static public void main(String[] args) {
AlgorithmX dl = new AlgorithmX();
int[][] sudoku = dl.readSudoku("c:/home/sudokuSample.txt");
dl.solveSudoku(sudoku);
}
}