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. Python学习——基础篇

    1.python的安装     python下载地址:https://www.python.org/downloads/     安装完成后,运行cmd.exe,输入python     如果出现“p ...

  2. Flex数据交互之WebService

    In this article you will learn how to call webservices hosted on asp.net applications from flex. Fir ...

  3. 不让Win7休眠的设置

    1:原因 目前有一个项目是采用微服务的架构搭建的,其中一个微服务的数据库是在一位同事的电脑上(Win7系统),一旦这台电脑休眠,对应的数据库服务就访问不了,所以,需要保持此台电脑永不休眠. 2:傻瓜式 ...

  4. org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'PUT' not supported

    1:先上控制台报错信息 org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'PUT' not ...

  5. 前端项目构建error

    Refusing to install webpack as a dependency of itself 原因:package.json中,"name": "webpa ...

  6. 2016-08-16: copy-and-swap

    #include <algorithm> // std::copy #include <cstddef> // std::size_t #include <stdio.h ...

  7. 前端开发框架Bootstrap和KnockoutJS

    江湖中那场异常惨烈的厮杀,如今都快被人遗忘了.当年,所有的武林同道为了同一个敌人都拼尽了全力,为数不多的幸存者心灰意冷,隐姓埋名,远赴他乡,他们将唯一的希望寄托给时间.少年子弟江湖老,红颜少女的鬓边也 ...

  8. [转载]:Fortran字符串的故事

    一. Fortran 字符串与 C 字符串的区别  Fortran的字符串处理能力其实很弱,关于字符串的语法还很落后.它与 C 字符串最大的区别就是:Fortran字符串是固定长度的,没有 \0 结束 ...

  9. mysql错误用法insert into where

    mysql中给表中插入数据,一般使用insert into. 但是在插入数据时,有时会根据条件来插入数据,比如insert into t_person(num,name) values(1,'lily ...

  10. Mybatis按SQL查询字段的顺序返回查询结果

    在SpringMVC+Mybatis的开发过程中,可以通过指定resultType="hashmap"来获得查询结果,但其输出是没有顺序的.如果要按照SQL查询字段的顺序返回查询结 ...