1. 程式人生 > >Coursera普林斯頓大學演算法Week4:8 Puzzle 解迷

Coursera普林斯頓大學演算法Week4:8 Puzzle 解迷

本次任務難點在於構建合理的內部變數,尤其是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());
    	}
   
    }
}