Coursera普林斯頓大學演算法Week4:8 Puzzle 解迷
阿新 • • 發佈:2019-01-09
本次任務難點在於構建合理的內部變數,尤其是Solver類中的變數,然後根據提示一步步解出來就行了。
Solver類中的變數如下:
private SearchNode currentNode;
private SearchNode currentTwinNode;
private Stack<Board> stackBoard;
其中stackBoard利用堆疊儲存處理的結果集。該結果集是根據最後的目標結果依次往前尋找其前驅節點的,所以利用堆疊正好可以將將結果集的順序倒過來。
SearchNode為尋找節點類。該內部類包含如下元素:
private final Board board; // 當前棋盤情況 private final int moves; // 當前棋盤移動步數 private SearchNode preSearcchNode = null; // 當前棋盤前驅 private final int priority; // 當前狀態優先順序
currentNode為原始面板當前尋找節點,currentTwinNode為交換面板當前尋找節點。根據數學可證,若原始面板無解則交換面板 一定有解。其交換方法如下:
public Board twin() // 通過交換任何一對塊而獲得的板。 { int i1 = 0, j1 = 0, i2 = 1, j2 = 1; if (blocks[i1][j1] == BLANK) { i1 = 1; j1 = 0; } if (blocks[i2][j2] == BLANK) { i2 = 1; j2 = 0; } Board newBoard = swap(i1, j1, i2, j2); return newBoard; }
其中swap是交換陣列中a[i1][j1]和a[i2][j2]的值,該交換是全域的,所以應該先將構造一個交換陣列,在將原陣列中的值拷貝過來,然後在進行交換操作。
另外,本文中的程式碼在儲存中還未達標,需要改進。
import java.util.ArrayList; public class Board { private final int [][]blocks; private static final int BLANK = 0; private final int N; public Board(int[][] blocks) // 建立n*n的棋盤 { if (blocks == null) throw new java.lang.IllegalArgumentException("the blocks is null"); N = blocks.length; this.blocks = new int[N][N]; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { this.blocks[i][j] = blocks[i][j]; } } } public int dimension() // 棋盤維度n { return N; // 返回列數 } private int getIndex(int i, int j) // 二維變一維座標 { return i * N + j + 1; } private int getRow(int value) // 在目標結果中,值所對應的行數 { return (value - 1) / N; } private int getCol(int value) // 在目標結果中,值所對應的列數 { return (value -1) % N; } public int hamming() // hamming優先順序=錯的位置個數+已移動次數 { int wrongNum = 0; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) if (blocks[i][j] != BLANK && blocks[i][j] != getIndex(i, j)) // blocks[i][j]位置上的元素放錯 wrongNum++; return wrongNum; } public int manhattan() // manhattan優先順序=距離目標距離+已移動次數 { int wrongNum = 0; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) if (blocks[i][j] != BLANK && blocks[i][j] != getIndex(i, j)) // blocks[i][j]位置上的元素放錯 { int righti = getRow(blocks[i][j]); // blocks[i][j]位置上的元素正確的i座標 int rightj = getCol(blocks[i][j]); // blocks[i][j]位置上的元素正確的j座標 wrongNum = wrongNum + Math.abs(righti - i) + Math.abs(rightj -j); } } return wrongNum; } public boolean isGoal() // 棋盤是否為目標位置 { for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) if (blocks[i][j] != BLANK && blocks[i][j] != getIndex(i, j)) // blocks[i][j]位置上的元素放錯 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.blocks[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); } public Board twin() // 通過交換任何一對塊而獲得的板。 { int i1 = 0, j1 = 0, i2 = 1, j2 = 1; if (blocks[i1][j1] == BLANK) { i1 = 1; j1 = 0; } if (blocks[i2][j2] == BLANK) { i2 = 1; j2 = 0; } Board newBoard = swap(i1, j1, i2, j2); return newBoard; } public boolean equals(Object y) // 判斷兩個棋盤是否相等 { if (y == null) return false; if (y == this) return true; if (y.getClass().isInstance(this)) // y和this屬於同一型別 { Board boardY = (Board) y; if (boardY.N != this.N) return false; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) if (this.blocks[i][j] != boardY.blocks[i][j]) return false; } else // y和this不屬於同一型別 { return false; } return true; } public Iterable<Board> neighbors() // 所有的鄰居棋盤 { ArrayList<Board> boards = new ArrayList<>(); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (blocks[i][j] == BLANK) // 找到空格位置 { // 上 if (i > 0) { Board upBoard = swap(i, j, i-1, j); boards.add(upBoard); } // 下 if (i < N-1) { Board lowBoard = swap(i, j, i+1, j); boards.add(lowBoard); } // 左 if (j > 0) { Board leftBoard = swap(i, j, i, j-1); boards.add(leftBoard); } // 右 if (j < N-1) { Board rightBoard = swap(i, j, i, j+1); boards.add(rightBoard); } } } } return boards; } public String toString() // 此板的字串表示形式(以下面指定的輸出格式) { StringBuilder sBuilder = new StringBuilder(); sBuilder.append(N + "\n"); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { sBuilder.append(blocks[i][j] +"\t"); } sBuilder.append("\n"); } String string = sBuilder.toString(); return string; } public static void main(String[] args) // unit tests (not graded) { int [][]blocks = new int[3][3]; blocks[0][0] = 8; blocks[0][1] = 1; blocks[0][2] = 3; blocks[1][0] = 4; blocks[1][1] = 0; blocks[1][2] = 2; blocks[2][0] = 7; blocks[2][1] = 6; blocks[2][2] = 5; 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()); } }
import edu.princeton.cs.algs4.MinPQ;
import edu.princeton.cs.algs4.Stack;
public class Solver {
private SearchNode currentNode;
private SearchNode currentTwinNode;
private Stack<Board> stackBoard;
private class SearchNode implements Comparable<SearchNode>
{
private final Board board; // 當前棋盤情況
private final int moves; // 當前棋盤移動步數
private SearchNode preSearcchNode = null; // 當前棋盤前身
private final int priority; // 當前狀態優先順序
public SearchNode(Board board, SearchNode pNode)
{
this.board = board;
this.preSearcchNode = pNode;
if (preSearcchNode == null)
moves = 0;
else
moves = preSearcchNode.getMoves() + 1;
priority = board.manhattan() + getMoves();
}
public int compareTo(SearchNode otherNode)
{
return Integer.compare(this.getPriority(), otherNode.getPriority());
}
public int getPriority() {
return priority;
}
public Board getBoard()
{
return board;
}
public int getMoves()
{
return moves;
}
public SearchNode getPreNode()
{
return preSearcchNode;
}
}
public Solver(Board initial) // 找到初始板的解決方案(使用A*演算法)
{
if (initial == null)
throw new java.lang.IllegalArgumentException("The intial Board is null");
currentNode = new SearchNode(initial, null); // 初始
MinPQ<SearchNode> minInitialPQ = new MinPQ<>();
minInitialPQ.insert(currentNode);
currentTwinNode = new SearchNode(initial.twin(), null); // 交換兩個位置後的
MinPQ<SearchNode> minTwinNode = new MinPQ<>();
minTwinNode.insert(currentTwinNode);
boolean flag = false;
while (flag != true)
{
// 對原始棋盤進行操作
currentNode = minInitialPQ.delMin(); // 取樹中優先順序最小的值
if (currentNode.getBoard().isGoal()) // 有解
flag = true;
else
putNeighborsBoardToPQ(currentNode, minInitialPQ); // 將鄰居步驟放入樹中
// 對調整順序的棋盤進行操作
currentTwinNode = minTwinNode.delMin(); // 取樹中優先順序最小的樹
if (currentTwinNode.getBoard().isGoal())
flag = true;
else
putNeighborsBoardToPQ(currentTwinNode, minTwinNode); // 將鄰居步驟放入樹中
}
}
// 將鄰居情況插入到樹中
private void putNeighborsBoardToPQ(SearchNode currentNode, MinPQ<SearchNode> pMinPQ)
{
Iterable<Board> boards = currentNode.getBoard().neighbors(); // 所有鄰居情況
for (Board neighborsBoard : boards) {
if (currentNode.getPreNode() == null ||
neighborsBoard.equals(currentNode.getPreNode().getBoard()) != true) // 防止回退現象
pMinPQ.insert(new SearchNode(neighborsBoard, currentNode)); // 將鄰居情況插入樹中
}
}
public boolean isSolvable() // 初始板是可解的嗎?
{
return currentNode.getBoard().isGoal();
}
public int moves() // 求解初始板的最小移動數;如果不可解,則為-1
{
if (isSolvable())
return currentNode.getMoves();
else
return -1;
}
public Iterable<Board> solution() // 最短解中的板序列;若不可解則為空
{
if (isSolvable() != true)
return null;
stackBoard = new Stack<>();
SearchNode nowNode = currentNode;
while (nowNode != null) {
stackBoard.push(nowNode.getBoard());
nowNode = nowNode.getPreNode();
}
return stackBoard;
}
public static void main(String[] args) // solve a slider puzzle (given below)
{
int [][]blocks = new int[3][3];
blocks[0][0] = 8;
blocks[0][1] = 1;
blocks[0][2] = 3;
blocks[1][0] = 4;
blocks[1][1] = 0;
blocks[1][2] = 2;
blocks[2][0] = 7;
blocks[2][1] = 6;
blocks[2][2] = 5;
Board board = new Board(blocks);
Solver solver = new Solver(board);
System.out.println(solver.currentNode.getPreNode() == null);
System.out.println(solver.currentNode.getPreNode());
if (solver.isSolvable() != false) {
System.out.println("this board is can't resolve");
}
Iterable<Board> bIterable = solver.solution();
System.out.println(bIterable.toString());
System.out.println("444");
for (Board it : bIterable) {
System.out.println(it.toString());
}
}
}