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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM