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