The Problem. 求解8数码问题。用最少的移动次数能使8数码还原.

Best-first search.使用A*算法来解决,我们定义一个Seach Node,它是当前搜索局面的一种状态,记录了从初始到达当前状态的移动次数和上一个状态。初始化时候,当前状态移动次数为0,上一个状态为null,将其放入优先级队列,通过不断的从优先级队列中取出Seach Node去扩展下一级的状态,直到找到目标状态。对于优先级队列中优先级的定义我们可以采用:Hamming priority function 和 Manhattan priority function,第一个表示有多少个块不在目标位置,第二个表示每一个块到他所在目标位置曼哈顿距离之和。

对于解决8数码来说,为了寻求最少步数,那么当前状态移动步数优先级需要考虑,同时选择Hamming或者Manhattan进行启发式的搜索。当目标状态出现时,我们就是使用了最少的步数。怎么证明?

A critical optimization.在搜索的过程中会遇到重复出现的状态,所以我们在进行下一个状态搜索的时候,判断不要将它相邻已经出现的状态加入到优先级队列中。

Game Tree. 搜索是一个博弈树的形式展开,每一个节点对应一个状态,树根是初始状态,在每一步中,A*算法删除优先级对联中priority最小的那个节点,然后进行处理

Detecting infeasible puzzles. 有些初始状态是无法通过移动来得到目标状态的,比如:

但是我们通过交换任意行不为空白的相邻的两个,如果按照这个初始状态来进行搜索,我们就可以得到目标状态。对于可行性的判断,可以根据初始状态和目标状态逆序数的奇偶性来进行判断,在进行移动后,应该奇偶性保持一致。但在这次Assignment中,不要求这么做,而是通过加入两个初始节点,一个是原有的,一个是进行相邻交换一次的,同时进行A*的搜索,如果原有的找到了目标解,那么就是可行,否则另一个找到了可行解,原有的就是不可行状态。

同一个初始状态到目标状态的最小移动次数是存在多解。其他还有IDA*,双向BFS等解法。

一些优化的地方,使用char[][] 比int[][]的空间更小。在进行曼哈顿距离求解的时候,我们可以用空间换时间,预存每个数字的曼哈顿距离,然后直接返回。

8数码是一个NP-Hard问题,没有有效的解存在。

完整的代码如下:

Board.java

public class Board {
private int[][] matrix; // blocks
private int N; // deimension
private int posX, posY; // 0' position // construct a board from an N-by-N array of blocks
// (where blocks[i][j] = block in row i, column j)
public Board(int[][] blocks) {
N = blocks.length;
matrix = new int[N][N];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
matrix[i][j] = blocks[i][j];
if (matrix[i][j] == 0) {
posX = i;
posY = j;
}
}
}
} // board dimension N
public int dimension() {
return N;
} // number of blocks out of place
public int hamming() {
int hammingDis = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (matrix[i][j] == 0) continue;
if (i*N+j+1 != matrix[i][j]) hammingDis++;
}
}
return hammingDis;
} // sum of Manhattan distances between blocks and goal
public int manhattan() {
int manhattanDis = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (matrix[i][j] == 0) continue;
int x, y;
if (matrix[i][j] % N == 0) {
x = matrix[i][j] / N - 1;
y = N - 1;
} else {
x = matrix[i][j] / N;
y = matrix[i][j] % N - 1;
}
manhattanDis += Math.abs(i-x) + Math.abs(j-y);
}
}
return manhattanDis;
} // is this board the goal board?
public boolean isGoal() {
if (posX != N-1 || posY != N-1) return false;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (matrix[i][j] == 0) continue;
if (i*N+j+1 != matrix[i][j]) return false;
}
}
return true;
} // a board obtained by exchanging two adjacent blocks in the same row
public Board twin() {
int x = -1, y = -1;
int[][] tmpBlock = new int[N][N];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (j < N-1 && matrix[i][j] != 0 && matrix[i][j+1] != 0) {
x = i;
y = j;
}
tmpBlock[i][j] = matrix[i][j];
}
}
if (x == -1 && y == -1) throw new IllegalArgumentException();
int t = tmpBlock[x][y];
tmpBlock[x][y] = tmpBlock[x][y+1];
tmpBlock[x][y+1] = t;
return new Board(tmpBlock);
} // 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 (this.dimension() != that.dimension()) return false;
int sz = this.dimension();
for (int i = 0; i < sz; i++) {
for (int j = 0; j < sz; j++) {
if (this.matrix[i][j] != that.matrix[i][j])
return false;
}
}
return true;
} // all neighboring boards
public Iterable<Board> neighbors() {
Queue<Board> queue = new Queue<Board>();
int[] dx = {0, 0, -1, 1};
int[] dy = {1, -1, 0, 0};
for (int i = 0; i < 4; i++) {
int x = posX + dx[i];
int y = posY + dy[i]; if (x < N && x >= 0 && y < N && y >= 0) {
int tmp = matrix[posX][posY];
matrix[posX][posY] = matrix[x][y];
matrix[x][y] = tmp;
queue.enqueue(new Board(matrix));
tmp = matrix[posX][posY];
matrix[posX][posY] = matrix[x][y];
matrix[x][y] = tmp;
}
}
return queue;
} // string representation of the board (in the output format specified below)
public String toString() {
StringBuilder s = new StringBuilder();
s.append(N + "\n");
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
s.append(String.format("%2d ", matrix[i][j]));
}
s.append("\n");
}
return s.toString();
} public static void main(String[] args) {
int[][] mat = {
{1, 2, 3},
{4, 6, 0},
{7, 8, 5}
};
//hamming
//manhattan
Board b = new Board(mat);
Board c = b.twin().twin();
StdOut.println(b.equals(c));
StdOut.print(b.toString());
for (Board it : b.neighbors()) {
StdOut.print(it.toString());
StdOut.println("hamming: " + it.hamming());
StdOut.println("manhattan: " + it.manhattan());
}
} }

Solver.java

public class Solver {

    private BoardNode targetBoardNode; // record targetBoardNode

    private class BoardNode implements Comparable<BoardNode> {
private Board item;
private BoardNode prev;
private int move;
private boolean isTwin; // compare by priority
public int compareTo(BoardNode that) {
if (that == null)
throw new NullPointerException("Input argument is null");
int thisPriority = this.move + this.item.manhattan();
int thatPriority = that.move + that.item.manhattan();
if (thisPriority < thatPriority)
return -1;
else if (thisPriority == thatPriority)
return 0;
else
return 1;
}
} // find a solution to the initial board (using the A* algorithm)
public Solver(Board initial) {
targetBoardNode = null;
// priority queue maintain the minimum elements
MinPQ<BoardNode> minpq = new MinPQ<BoardNode>();
// initial boardnode
BoardNode bn = new BoardNode();
bn.item = initial;
bn.prev = null;
bn.move = 0;
bn.isTwin = false;
minpq.insert(bn);
// initial twin boardnode
BoardNode twinbn = new BoardNode();
twinbn.item = initial.twin();
twinbn.prev = null;
twinbn.move = 0;
twinbn.isTwin = true;
minpq.insert(twinbn); while (!minpq.isEmpty()) {
BoardNode curbn = minpq.delMin();
if (curbn.item.isGoal()) {
if (curbn.isTwin) targetBoardNode = null;
else targetBoardNode = curbn;
break;
} for (Board it : curbn.item.neighbors()) {
if (curbn.prev == null || !curbn.prev.item.equals(it)) {
bn = new BoardNode();
bn.item = it;
bn.prev = curbn;
bn.move = curbn.move+1;
if (curbn.isTwin)
bn.isTwin = true;
else
bn.isTwin = false;
minpq.insert(bn);
}
}
}
} // is the initial board solvable?
public boolean isSolvable() {
return targetBoardNode != null;
} // min number of moves to solve initial board; -1 if no solution
public int moves() {
if (isSolvable())
return targetBoardNode.move;
else
return -1;
} // sequence of boards in a shortest solution; null if no solution
public Iterable<Board> solution() {
Stack<Board> stack = new Stack<Board>();
BoardNode tmpbn = targetBoardNode;
while (tmpbn != null) {
stack.push(tmpbn.item);
tmpbn = tmpbn.prev;
}
if (stack.isEmpty())
return null;
else
return stack;
} // solve a slider puzzle (given below)
public static void main(String[] args) {
// create initial board from file
In in = new In(args[0]);
int N = in.readInt();
int[][] blocks = new int[N][N];
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
blocks[i][j] = in.readInt();
Board initial = new Board(blocks); // solve the puzzle
Solver solver = new Solver(initial); // print solution to standard output
if (!solver.isSolvable())
StdOut.println("No solution possible");
else {
StdOut.println("Minimum number of moves = " + solver.moves());
for (Board board : solver.solution())
StdOut.println(board);
}
}
}

Programming Assignment 4: 8 Puzzle的更多相关文章

  1. Coursera Algorithms Programming Assignment 4: 8 Puzzle (100分)

    题目原文:http://coursera.cs.princeton.edu/algs4/assignments/8puzzle.html 题目要求:设计一个程序解决8 puzzle问题以及该问题的推广 ...

  2. 课程一(Neural Networks and Deep Learning),第三周(Shallow neural networks)—— 3.Programming Assignment : Planar data classification with a hidden layer

    Planar data classification with a hidden layer Welcome to the second programming exercise of the dee ...

  3. Algorithms: Design and Analysis, Part 1 - Programming Assignment #1

    自我总结: 1.编程的思维不够,虽然分析有哪些需要的函数,但是不能比较好的汇总整合 2.写代码能力,容易挫败感,经常有bug,很烦心,耐心不够好 题目: In this programming ass ...

  4. Algorithms : Programming Assignment 3: Pattern Recognition

    Programming Assignment 3: Pattern Recognition 1.题目重述 原题目:Programming Assignment 3: Pattern Recogniti ...

  5. Programming Assignment 2: Randomized Queues and Deques

    实现一个泛型的双端队列和随机化队列,用数组和链表的方式实现基本数据结构,主要介绍了泛型和迭代器. Dequeue. 实现一个双端队列,它是栈和队列的升级版,支持首尾两端的插入和删除.Deque的API ...

  6. 课程一(Neural Networks and Deep Learning),第二周(Basics of Neural Network programming)—— 2、编程作业常见问题与答案(Programming Assignment FAQ)

    Please note that when you are working on the programming exercise you will find comments that say &q ...

  7. Programming Assignment 5: Kd-Trees

    用2d-tree数据结构实现在2维矩形区域内的高效的range search 和 nearest neighbor search.2d-tree有许多的应用,在天体分类.计算机动画.神经网络加速.数据 ...

  8. coursera普林斯顿算法课part1里Programming Assignment 2最后的extra challenge

    先附上challenge要求: 博主最近在刷coursera普林斯顿大学算法课part1部分的作业,Programming Assignment2最后的这个extra challenge当初想了一段时 ...

  9. Programming Assignment 2: Deques and Randomized Queues

    编程作业二 作业链接:Deques and Randomized Queues & Checklist 我的代码:Deque.java & RandomizedQueue.java & ...

随机推荐

  1. [转载]: delphi中XLSReadWrite控件的使用(1)---简介

    XLSReadWrite控件简介: 一个你需要的,能在Delphi和.NET下访问Excel文件的完美解决方案. 一个经典的读写Excel的控件,对于使用Excel 开发很有帮助 官方网站: http ...

  2. App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure.-解决办法

    运行环境:Xcode Version 7.3.1 (7D1014) 使用NSURL进行数据请求数据代码: -(NSData *)requestData{ NSURL *url = [NSURL URL ...

  3. SQLServer中char、varchar、nchar、nvarchar的区别:

    (1)       定义: char:    固定长度,存储ANSI字符,不足的补英文半角空格. nchar:   固定长度,存储Unicode字符,不足的补英文半角空格 varchar:  可变长度 ...

  4. unity3d InverseTransformPoint方法

    从歪果仁的脚本里看到了这个方法,查脚本,看脚本说明也没看懂,官方的说明是,变换位置从世界坐标到自身坐标,Transform.TransformPoint相反. 试验了一下得出这个结论,如果某一个物体A ...

  5. php函数的引用返回

    <?php function &test(){ static $b = 1; $b += 2; return $b; } $a = &test(); $a =8; $c = te ...

  6. awk用法

    目前虽然有很多工具可以代替awk,但是呢我还是认为awk还是非常重要,比如有时候load数据到hive,mysql发现数据有点问题,这样可以先对比文件和库中数据是否一致,这样awk就发挥用处了,还有从 ...

  7. information_schema系列二(列,列权限,事件,存储引擎)

    这个系列的文章主要是为了能够让自己了解MySQL5.7的一些系统表,统一做一下备注和使用,也希望分享出来让大家能够有一点点的受益. 1:COLUMNS 老规矩.查一下这个表,看一下记录,由于这个是看表 ...

  8. 易语言软件加VMProtect壳的正确方法

    VMP是一款很强大的加密壳,代码虚拟化技术可以很好的保护程序不被恶意修改破J但是很多人不知道怎么给自己的程序加壳,今天给大家晋级下加壳的正确方法 相信很多新手都以为只要把软件直接拖到VMP里重新编译一 ...

  9. JS根据服务器时间倒计时

    原文链接:http://caibaojian.com/daojishi-2.html //获取服务器时间 function getSevertime(){ var xmlHttp = new XMLH ...

  10. artDialog测试

    artDialog测试 <script src="../Content/artDialog/v4/artDialog.source.js?skin=default">& ...