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

题目原文:http://coursera.cs.princeton.edu/algs4/assignments/8puzzle.html

题目要求:设计一个程序解决8 puzzle问题以及该问题的推广,例如8-puzzle是3*3,程序要能解决n*n的同类问题(2 ≤ n < 128)

典型的8 puzzle如下:

算法设计参照A*搜索算法,即使不了解A*搜索算法,题目也已经将解法解释的很具体了。

Best-first search:设计参照A* 搜索算法。定义一个 search node类,包含board,从初始状态到达当前board状态的移动权重moves,和previous search node。

  1. 插入初始的search node,其board设为初始board,moves设为0,previous search node设置为0
  2. 将初始化的search node置于MinPQ类型的优先级队列中
  3. 删除优先级队列中的min节点,再将该节点的邻居节点放入优先级队列中。
  4. 重复2和3操作直至从优先级队列中删除的min节点是目标board

A* 搜索算法的优先级判定依据是f(n) = g(n) + h(n),g(n)是从初始节点到达当前节点的代价,h(n)是当前节点到目标节点的预估代价。

在本题中search node中的moves就是g(n),而关于h(n)题目给出了两种候选:

Hamming priority function:  处于错误位置的block的个数(空白处不算block)

Manhatten priority function: 处于错误位置的block距离其各自目标位置的横向和纵向距离之和

h(n)采用这两者均可,根据题目中的图示,显然发现题目推荐采用manhatten方法。

至此优先级队列中的优先级判断依据就是当前search node的moves+manhatten value

A critical optimization: 上述Best-first search中可能会存在刚出队列的节点又被当成其邻居节点的邻居而被放回优先级队列的情况,这种情况会造成很大的性能损失。为了阻止这种情况的发生,可以在Best-first search的第3步“将该节点的邻居节点放入优先级队列”时比较下这个邻居节点的board是否与本节点的board相同。

例如此时{{8,1,3},{4,0,2},{7,6,5}}就不应该放入优先级队列中。

A second optimization: 建议在search node的构造函数中计算其manhattan值,也就是在search node的构造函数中确定其优先级。

Detecting unsolvable puzzles:如果一个board是不可解的,那么随便在该board中选择一对block互换位置,就能将其变为可解的。为此采用同时对board和其互换了一对block的twindboard进行求解,如果board先实现目标解,那么其就是可解的,相反,如果twinboard先实现目标节,那么该board就不可解。

  1 import java.util.ArrayList;
  2 /**
  3  * @author evasean www.cnblogs.com/evasean/
  4  */
  5 public class Board {
  6     private static final int BLANK = 0;
  7     private final int n;
  8     private int[][] blocks;
  9
 10     public Board(int[][] inBlocks) {
 11         // construct a board from an n-by-n array of blocks
 12         // (where blocks[i][j] = block in row i, column j)
 13         n = inBlocks.length;
 14         blocks = new int[n][n];
 15         copy(blocks, inBlocks);
 16     }
 17
 18     private void copy(int[][] toBlocks, int[][] fromBlocks) {
 19         for (int row = 0; row < n; row++)
 20             for (int col = 0; col < n; col++)
 21                 toBlocks[row][col] = fromBlocks[row][col];
 22     }
 23
 24     public int dimension() {
 25         // board dimension n
 26         return n;
 27     }
 28
 29     private int getRow(int value) {
 30         return (value - 1) / n;
 31     }
 32
 33     private int getCol(int value) {
 34         return (value - 1) % n;
 35     }
 36
 37     private int getValue(int row, int col) {
 38         return row * n + col + 1;
 39     }
 40
 41     public int hamming() {
 42         // number of blocks out of place
 43         int hamming = 0;
 44         for (int row = 0; row < n; row++)
 45             for (int col = 0; col < n; col++)
 46                 if (blocks[row][col] != BLANK && blocks[row][col] != getValue(row, col))
 47                     hamming++;
 48         return hamming;
 49     }
 50
 51     public int manhattan() {
 52         // sum of Manhattan distances between blocks and goal
 53         int manhattan = 0;
 54         for (int row = 0; row < n; row++)
 55             for (int col = 0; col < n; col++)
 56                 if (blocks[row][col] != BLANK && blocks[row][col] != getValue(row, col))
 57                     manhattan += Math.abs(getRow(blocks[row][col]) - row) + Math.abs(getCol(blocks[row][col]) - col);
 58         return manhattan;
 59     }
 60
 61     public boolean isGoal() {
 62         // is this board the goal board?
 63         for (int row = 0; row < n; row++)
 64             for (int col = 0; col < n; col++)
 65                 if (blocks[row][col] != BLANK && blocks[row][col] != getValue(row, col))
 66                     return false;
 67         return true;
 68     }
 69
 70     public Board twin() {
 71         // a board that is obtained by exchanging any pair of blocks
 72         Board twinBoard = new Board(blocks);
 73         int firRow = 0;
 74         int firCol = 0;
 75         if (blocks[firRow][firCol] == BLANK)
 76             firCol++;
 77         for (int row = 0; row < n; row++) {
 78             for (int col = 0; col < n; col++) {
 79                 if (blocks[row][col] != blocks[firRow][firCol] && blocks[row][col] != BLANK) {
 80                     twinBoard.swap(firRow, firCol, row, col);
 81                     return twinBoard;
 82                 }
 83             }
 84         }
 85         return twinBoard;
 86     }
 87
 88     private void swap(int vRow, int vCol, int wRow, int wCol) {
 89         int t = blocks[vRow][vCol];
 90         blocks[vRow][vCol] = blocks[wRow][wCol];
 91         blocks[wRow][wCol] = t;
 92     }
 93
 94     public boolean equals(Object y) {
 95         // does this board equal y?
 96         if (y == null)
 97             return false;
 98         if (y == this)
 99             return true;
100         if (y.getClass().isInstance(this)) {
101             Board yb = (Board) y;
102             if (yb.n != this.n)
103                 return false;
104             else {
105                 for (int row = 0; row < n; row++)
106                     for (int col = 0; col < n; col++)
107                         if (yb.blocks[row][col] != blocks[row][col])
108                             return false;
109                 return true;
110             }
111         } else
112             return false;
113     }
114
115     public Iterable<Board> neighbors() {
116         // all neighboring boards
117         ArrayList<Board> neighbors = new ArrayList<Board>();
118         for (int row = 0; row < n; row++) {
119             for (int col = 0; col < n; col++) {
120                 if (blocks[row][col] == BLANK) {
121                     // 空白的位置分别与上下左右的元素交换一次位置就得到一个邻居board
122                     // 与上方元素互换
123                     if (row > 0) {
124                         Board neighborT = new Board(blocks);
125                         neighborT.swap(row, col, row - 1, col);
126                         neighbors.add(neighborT);
127                     }
128                     // 与下方元素互换
129                     if (row < n - 1) {
130                         Board neighborB = new Board(blocks);
131                         neighborB.swap(row, col, row + 1, col);
132                         neighbors.add(neighborB);
133                     }
134                     // 与左边元素互换
135                     if (col > 0) {
136                         Board neighborL = new Board(blocks);
137                         neighborL.swap(row, col, row, col - 1);
138                         neighbors.add(neighborL);
139                     }
140                     // 与右边元素互换
141                     if (col < n - 1) {
142                         Board neighborR = new Board(blocks);
143                         neighborR.swap(row, col, row, col + 1);
144                         neighbors.add(neighborR);
145                     }
146                 }
147             }
148         }
149         return neighbors;
150     }
151
152     public String toString() {
153         // string representation of this board (in the output format specified
154         // below)
155         StringBuilder sb = new StringBuilder();
156         sb.append(n + "\n");
157         for (int row = 0; row < n; row++) {
158             for (int col = 0; col < n; col++) {
159                 //本来考虑到n<128时元素可能会很大,设置的是%6d,但是提交时不满足校验规则
160                 //校验规则要求必须是%2d,很奇怪的校验
161                 sb.append(String.format("%2d ", blocks[row][col]));
162             }
163             sb.append("\n");
164         }
165         return sb.toString();
166     }
167
168     public static void main(String[] args) {
169 //         unit tests (not graded)
170 //         int[][] test = { { 0, 1}, {2,3 }};
171 //         Board b = new Board(test);
172 //         System.out.println(b);
173 //         System.out.println(b.hamming());
174 //         System.out.println(b.manhattan());
175     }
176 }
  1 import edu.princeton.cs.algs4.In;
  2 import edu.princeton.cs.algs4.MinPQ;
  3 import edu.princeton.cs.algs4.Stack;
  4 import edu.princeton.cs.algs4.StdOut;
  5 /**
  6  * @author evasean www.cnblogs.com/evasean/
  7  */
  8 public class Solver {
  9
 10     private SearchNode currentNode;
 11     private SearchNode twincurrentNode;
 12     private Stack<Board> solution;
 13
 14     private class SearchNode implements Comparable<SearchNode>{
 15         public Board board;
 16         public int moves;
 17         public SearchNode preSearchNode;
 18
 19         public final int priority;
 20
 21         public SearchNode(Board inboard, SearchNode inPreSearchNode){
 22             board = inboard;
 23             preSearchNode = inPreSearchNode;
 24             if(inPreSearchNode == null) moves = 0;
 25             else moves = inPreSearchNode.moves + 1;
 26             priority = moves + board.manhattan();
 27         }
 28
 29         @Override
 30         public int compareTo(SearchNode o) {
 31             return Integer.compare(this.priority, o.priority);
 32         }
 33     }
 34
 35
 36     public Solver(Board initial) {
 37         // find a solution to the initial board (using the A* algorithm)
 38         if(initial == null)
 39             throw new IllegalArgumentException("Constructor argument Board is null!");
 40         currentNode = new SearchNode(initial,null);
 41         twincurrentNode = new SearchNode(initial.twin(),null);
 42         MinPQ<SearchNode> priorityQueue = new MinPQ<SearchNode>();
 43         MinPQ<SearchNode> twinPriorityQueue = new MinPQ<SearchNode>();
 44         priorityQueue.insert(currentNode);
 45         twinPriorityQueue.insert(twincurrentNode);
 46         while(true){
 47             currentNode = priorityQueue.delMin();
 48             if(currentNode.board.isGoal()) break;
 49             putNeighBorsIntoPQ(currentNode,priorityQueue);
 50
 51             twincurrentNode = twinPriorityQueue.delMin();
 52             if(twincurrentNode.board.isGoal()) break;
 53             putNeighBorsIntoPQ(twincurrentNode,twinPriorityQueue);
 54         }
 55     }
 56
 57     private void putNeighBorsIntoPQ(SearchNode searchNode, MinPQ<SearchNode> pq){
 58         Iterable<Board> neighbors = searchNode.board.neighbors();
 59         for(Board neighbor : neighbors){
 60             //只有在当前搜索节点的邻居们的borad不与当前节点的preSearchNode的borad相同
 61             //才将该邻居放入优先队列
 62
 63             if(searchNode.preSearchNode==null || !neighbor.equals(searchNode.preSearchNode.board))
 64                 pq.insert(new SearchNode(neighbor,searchNode));
 65         }
 66     }
 67
 68     public boolean isSolvable() {
 69         // is the initial board solvable?
 70         return currentNode.board.isGoal();
 71     }
 72
 73     public int moves() {
 74         // min number of moves to solve initial board; -1 if unsolvable
 75         if(currentNode.board.isGoal())
 76             return currentNode.moves;
 77         else
 78             return -1;
 79     }
 80
 81     public Iterable<Board> solution() {
 82         // sequence of boards in a shortest solution; null if unsolvable
 83         if(currentNode.board.isGoal()){
 84             solution = new Stack<Board>();
 85             SearchNode node = currentNode;
 86             while(node != null){
 87                 solution.push(node.board);
 88                 node = node.preSearchNode;
 89             }
 90             return solution;
 91         }else
 92             return null;
 93     }
 94
 95     public static void main(String[] args) {
 96         // solve a slider puzzle (given below)
 97         // create initial board from file
 98 //        In in = new In(args[0]);
 99         In in = new In("8puzzle/puzzle3x3-unsolvable.txt"); //本地测试之用
100         int n = in.readInt();
101         int[][] blocks = new int[n][n];
102         for (int i = 0; i < n; i++)
103             for (int j = 0; j < n; j++)
104                 blocks[i][j] = in.readInt();
105         Board initial = new Board(blocks);
106
107         // solve the puzzle
108         Solver solver = new Solver(initial);
109
110         // print solution to standard output
111         if (!solver.isSolvable())
112             StdOut.println("No solution possible");
113         else {
114             StdOut.println("Minimum number of moves = " + solver.moves());
115             for (Board board : solver.solution())
116                 StdOut.println(board);
117         }
118     }
119 }
时间: 2024-10-19 00:25:34

Coursera Algorithms Programming Assignment 4: 8 Puzzle (100分)的相关文章

Coursera Algorithms Programming Assignment 1: Percolation

题目来源http://coursera.cs.princeton.edu/algs4/assignments/percolation.html 作业分为两部分:建立模型和仿真实验. 最关键的部分就是建立模型对象.模型对象要求如下: The model.  We model a percolation system using an n-by-n grid of sites. Each site is either open or blocked. A full site is an open s

Coursera Algorithms Programming Assignment 2: Deque and Randomized Queue

这次作业与第一周作业相比,稍微简单一些.有三个编程练习:双端队列(Deque)设计.随机队列(Randomized Queue)设计,还有一个排列组合类Permutation. 一.双端队列Deque 设计要求:A double-ended queue or deque (pronounced "deck") is a generalization of a stack and a queue that supports adding and removing items from e

Programming Assignment 4: 8 Puzzle

The Problem. 求解8数码问题.用最少的移动次数能使8数码还原. Best-first search.使用A*算法来解决,我们定义一个Seach Node,它是当前搜索局面的一种状态,记录了从初始到达当前状态的移动次数和上一个状态.初始化时候,当前状态移动次数为0,上一个状态为null,将其放入优先级队列,通过不断的从优先级队列中取出Seach Node去扩展下一级的状态,直到找到目标状态.对于优先级队列中优先级的定义我们可以采用:Hamming priority function 和

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

自我总结: 1.编程的思维不够,虽然分析有哪些需要的函数,但是不能比较好的汇总整合 2.写代码能力,容易挫败感,经常有bug,很烦心,耐心不够好 题目: In this programming assignment you will implement one or more of the integer multiplication algorithms described in lecture. To get the most out of this assignment, your pro

C200 Programming Assignment

C200 Programming Assignment № 8Computer ScienceSchool of Informatics, Computing, and EngineeringMarch 30, 2019ContentsIntroduction 2Problem 1: Newton Raphson Root Finding Algorithm 4Root . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Coursera Algorithms第二周编程任务

Programming Assignment 2: Deques and Randomized Queues Write a generic data type for a deque and a randomized queue. The goal of this assignment is to implement elementary data structures using arrays and linked lists, and to introduce you to generic

Programming Assignment 1: WordNet

题目地址:http://coursera.cs.princeton.edu/algs4/assignments/wordnet.html 1. 题目阅读 WordNet定义 WordNet是指一个包含唯一根的有向无环图,图中每一组词表示同一集合,每一条边v→w表示w是v的上位词.和树不同的地方是,每一个子节点可以有许多父节点. 输入格式 同义词表 文件中每行包含一次同义名词.首先是序号:然后是词,用空格分开.若为词组,则使用下划线连接词组.最后是同义名词的注释 36,AND_circuit AN

CMPT 459.1-19. Programming Assignment

---title: "CMPT 459.1-19. Programming Assignment 1"subtitle: "FIFA 19Players"author: "Name - Student ID"output: html_notebook---### IntroductionThe data has detailed attributes for every player registered inthe latest edition

Algorithm Part I:Programming Assignment(2)

问题描述: Programming Assignment 2: Randomized Queues and Deques Write a generic data type for a deque and a randomized queue. The goal of this assignment is to implement elementary data structures using arrays and linked lists, and to introduce you to g