8-puzzle 普林斯顿 算法第四版
前言
这个assignment来自Princeton University的Coursera Algorithms, Part I
需要了解优先队列的实现。
友情链接:
8 puzzle assignment specification
一、分析
采用优先队列思想,具体实现可以使用提供的API
Board部分按照链接中的要求就可以比较容易的实现。
Solver部分参考了一些博文的做法。
二、代码
1.board
import java.util.ArrayList;
import java.util.Arrays;
public class Board {
private final int[][] tiles;
private final int N;
// create a board from an n-by-n array of tiles,
// where tiles[row][col] = tile at (row, col)
public Board(int[][] tiles) {
//创建(复制)一个
this.N = tiles.length;
this.tiles = new int[N][N];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
this.tiles[i][j] = tiles[i][j];
}
}
}
// string representation of this board
public String toString() {
//StringBuilder 用于修改字符串
StringBuilder s = new StringBuilder();
s.append(N).append("\n");
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
s.append(" " + tiles[i][j]);
}
s.append("\n");
}
String string = s.toString();
return string;
}
// board dimension n
public int dimension() {
return N;
}
// number of tiles out of place
public int hamming() {
int k = 0;
int dis_hamming = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
k++;
if (tiles[i][j] == 0) {
} else if (k != tiles[i][j]) {
dis_hamming++;
}
}
}
return dis_hamming;
}
// sum of Manhattan distances between tiles and goal
public int manhattan() {
int dis_manhattan = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
int tmp = tiles[i][j];
if (tmp == 0) {
} else {
int x = (tmp - 1) / N;
int y = (tmp - 1) % N;
dis_manhattan += Math.abs(x - i) + Math.abs(y - j);
}
}
}
return dis_manhattan;
}
// is this board the goal board?
public boolean isGoal() {
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
if (tiles[i][j] != 0 && tiles[i][j] != i * N + j + 1) // blocks[i][j]位置上的元素放错
return false;
return true;
}
// does this board equal y?
public boolean equals(Object y) {
if (y == this) return true;
if (y == null) return false;
if (y.getClass() != this.getClass())
return false;
Board that = (Board) y;
if (!Arrays.equals(this.tiles, that.tiles)) return false;
return true;
}
private int[][] copy() // 拷贝棋盘元素
{
int[][] newblocks = new int[N][N];
for (int i1 = 0; i1 < N; i1++)
for (int j1 = 0; j1 < N; j1++)
newblocks[i1][j1] = this.tiles[i1][j1];
return newblocks;
}
private Board swap(int i1, int j1, int i2, int j2) {
int[][] newblocks = copy();
int temp = newblocks[i1][j1];
newblocks[i1][j1] = newblocks[i2][j2];
newblocks[i2][j2] = temp;
return new Board(newblocks);
}
// all neighboring boards
public Iterable<Board> neighbors() {
ArrayList<Board> boards;
boards = new ArrayList<>();
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (tiles[i][j] == 0) {
if (i > 0) {
//head
Board upBoard = swap(i, j, i - 1, j);
boards.add(upBoard);
}
if (i < N - 1) {
//bottom
Board lowBoard = swap(i, j, i + 1, j);
boards.add(lowBoard);
}
if (j > 0) {
//left
Board leftBoard = swap(i, j, i, j - 1);
boards.add(leftBoard);
}
if (j < N - 1) {
//right
Board rightBoard = swap(i, j, i, j + 1);
boards.add(rightBoard);
}
}
}
}
return boards;
}
// a board that is obtained by exchanging any pair of tiles
public Board twin() {
int i1 = 0, j1 = 0, i2 = 1, j2 = 1;
if (tiles[i1][j1] == 0) {
i1 = 1;
j1 = 0;
}
if (tiles[i2][j2] == 0) {
i2 = 1;
j2 = 0;
}
Board newBoard = swap(i1, j1, i2, j2);
return newBoard;
}
// unit testing (not graded)
public static void main(String[] args) {
int[][] blocks = new int[3][3];
blocks[0][0] = 1;
blocks[0][1] = 2;
blocks[0][2] = 3;
blocks[1][0] = 4;
blocks[1][1] = 5;
blocks[1][2] = 6;
blocks[2][0] = 8;
blocks[2][1] = 7;
blocks[2][2] = 0;
Board board = new Board(blocks);
System.out.println(board.manhattan());
System.out.println(board.toString());
for (Board it : board.neighbors()) {
//System.out.println(it.toString());
}
System.out.println(board.twin().toString());
}
}
2.solver
代码如下(示例):
import edu.princeton.cs.algs4.MinPQ;
import edu.princeton.cs.algs4.Stack;
public class Solver {
private TreeNode currentNode;
private TreeNode currenttwinNode;
private Stack<Board> stackBoard;
// find a solution to the initial board (using the A* algorithm)
private class TreeNode implements Comparable<TreeNode> {
private final Board board;
private final TreeNode pre;
private final int moves;
private final int priority;
private TreeNode(Board b, TreeNode pre) {
this.board = b;
this.pre = pre;
if (pre != null) {
this.moves = pre.moves + 1;
this.priority = this.moves + this.board.manhattan();
} else {
//pre==null
moves = 0;
this.priority = this.moves + this.board.manhattan();
}
}
public TreeNode getPre() {
return pre;
}
public int getMoves() {
return moves;
}
public Board getBoard() {
return board;
}
public int getPriority() {
return priority;
}
public int compareTo(TreeNode a) {
if (this.priority == a.priority) {
return this.board.manhattan() - a.board.manhattan();
}
return this.priority - a.priority;
}
}
private final boolean solvable;
public boolean isSolvable() {
return solvable;
}
public int moves() {
if (isSolvable())
return currentNode.getMoves();
else
return -1;
}
public Solver(Board initial) {
if (initial == null)
throw new NullPointerException();
currentNode = new TreeNode(initial, null);
MinPQ<TreeNode> PQ = new MinPQ<>();
PQ.insert(currentNode);
currenttwinNode = new TreeNode(initial.twin(), null);
MinPQ<TreeNode> twinPQ = new MinPQ<>();
twinPQ.insert(currenttwinNode);
//boolean flag = false;
while (true) {
//origin
currentNode = PQ.delMin();
if (currentNode.getBoard().isGoal()) {
solvable = true;
break;
//found
} else {
for (Board it : currentNode.getBoard().neighbors()) {
//==null 针对第一个Node
if (currentNode.getPre() == null ||
!it.equals(currentNode.getPre().getBoard())) {
PQ.insert(new TreeNode(it, currentNode));
}
}
}
//twin tiles
currenttwinNode = twinPQ.delMin();
if (currenttwinNode.getBoard().isGoal()) {
solvable = false;
break;
//found
} else {
for (Board it : currenttwinNode.getBoard().neighbors()) {
//==null 针对第一个Node
if (currenttwinNode.getPre() == null ||
!it.equals(currenttwinNode.getPre().getBoard())) {
twinPQ.insert(new TreeNode(it, currenttwinNode));
}
}
}
}
}
// sequence of boards in a shortest solution; null if unsolvable
public Iterable<Board> solution() {
if (!isSolvable())
return null;
stackBoard = new Stack<>();
TreeNode nowNode = currentNode;
while (nowNode != null) {
stackBoard.push(nowNode.getBoard());
nowNode = nowNode.getPre();
}
return stackBoard;
}
public static void main(String[] args) // solve a slider puzzle (given below)
{
int[][] blocks = new int[3][3];
blocks[0][0] = 1;
blocks[0][1] = 2;
blocks[0][2] = 3;
blocks[1][0] = 4;
blocks[1][1] = 5;
blocks[1][2] = 6;
blocks[2][0] = 8;
blocks[2][1] = 7;
blocks[2][2] = 0;
Board board = new Board(blocks);
Solver solver = new Solver(board);
//System.out.println(solver.currentNode.getPre() == null);
//System.out.println(solver.currentNode.getPre());
if (!solver.isSolvable()) {
System.out.println("this board is can't resolve");
} else {
Iterable<Board> bIterable = solver.solution();
//System.out.println(bIterable.toString());
//System.out.println("444");
System.out.println("Minimum number of moves = " + solver.moves());
for (Board it : bIterable) {
System.out.println(it.toString());
}
}
}
}
总结
花了很多时间来搞懂到底要做什么。有一些疑问可以在链接中的答疑中知道。比如: 1.board的中的twin()是干嘛的,应为在写solver的时候要么tiles本身有解,要么它的twin()有解,因此需要两个队列来判断本事是不是有解本代码得了89/100分,还有许多可以优化的。