普林斯頓算法課第四周作業

8 Puzzle

作業地址:http://coursera.cs.princeton.edu/algs4/assignments/8puzzle.html

----------------------------------------------------------------------------------------------------------
第四周作業 8 Puzzle
8 puzzle 是一個經典的拼圖問題
將九宮格內亂序的8個數字按順序整理好,一次只能移動一個數字到空位當中。


作業中提供的思路是Best-first search(最好優先搜索)
首先定義一個Search Node,記錄初始到達當前狀態的移動次數和上一個狀態。
通過不斷從優先級隊列中取出Search Node 去尋找下一級的狀態,並在其中找到最符合優先級的結果。
而要確定優先級就要使用以下兩個方法:
Hamming priority function:表示不在目標位置的數目
Manhattan priority function:所有方塊距離目標位置之和。

然後是需要注意的幾個問題:
1.A critical optimization


防止重複狀態的出現,需要進行判斷


2.Game Tree  將狀態以搜索樹的方式儲存,每一個節點對應一個狀態。在每一步中,用A*算法
刪除優先級隊列中權值最小的那個節點。


3.Detecting infeasible puzzles 
有一些初始狀態是不可能得到目的狀態的。題目中給我們的方法是,將同行的兩個數進行交換,
如果其中一個得到目標解,那麼另外一個則不可能。


最後有一些方法可能使用不當,很多都超時了
代碼如下:
Board.java
import java.util.Arrays;
import java.util.Comparator;

public class Board {
	private final int[][] blocks;
	private final int N;
	
	// 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;
    	this.blocks = new int[N][];
    	for (int i=0; i<N; i++){
    		this.blocks[i] = Arrays.copyOf(blocks[i], N);
    	}
    	
    }
    
    // board dimension N
    public int dimension(){
    	return this.N;
    }
    
    // number of blocks out of place
    public int hamming(){
    	int i_hamming = 0;
    	for (int i=0; i<N; i++){
    		for (int j=0; j<N && i+j < 2*N - 2; j++){
    			if (blocks[i][j] != i*N + j + 1){
        			i_hamming++;
    			}
    		}
    	}
    	return i_hamming;
    }
    
    // sum of Manhattan distances between blocks and goal
    public int manhattan(){
    	int i_manhattan = 0;
    	for (int i=0; i<N; i++){
    		for (int j=0; j<N; j++){
    			if (blocks[i][j] != 0){
    				i_manhattan += Math.abs((blocks[i][j] - 1) % N - j) + Math.abs((blocks[i][j] - 1) / N - i);
    			}
    		}
    	}
    	return i_manhattan;
    }
    
    // is this board the goal board?
    public boolean isGoal(){
    	return this.hamming() == 0;
    }
    
    // a board obtained by exchanging two adjacent blocks in the same row
    public Board twin(){
    	int[][] twinBoard = new int[N][N];
		for (int i=0; i<N; i++){
			for (int j=0; j<N; j++){
				twinBoard[i][j] = blocks[i][j];
			}
		}
    	if (blocks[0][0] != 0 && blocks[0][1] != 0){
    		int temp = twinBoard[0][0];
    		twinBoard[0][0] = twinBoard[0][1];
    		twinBoard[0][1] = temp;
    	}else{
    		int temp = twinBoard[1][0];
    		twinBoard[1][0] = twinBoard[1][1];
    		twinBoard[1][1] = temp;
    	}
    	return new Board(twinBoard);
    }
    
    // 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 thatBoard = (Board)y;
    	if (this.N != thatBoard.N){
    		return false;
    	}
    	
    	int[][] arr_thatBoard = thatBoard.blocks;
    	for (int i=0; i<N; i++){
    		for (int j=0; j<N; j++){
    			if (blocks[i][j] != arr_thatBoard[i][j]){
    				return false;
    			}
    		}
    	}
    	return true;
    }
    
    // all neighboring boards
    public Iterable<Board> neighbors(){
    	int blank_i = N;
    	int blank_j = N;
    	for (int i=0; i<N; i++){
    		for (int j=0; j<N; j++){
    			if (blocks[i][j] == 0){
    				//this is where the blank is
    				blank_i = i;
    				blank_j = j;
    			}
    		}
    	}
    	MinPQ<Board> q = new MinPQ<Board>(new Comparator<Board>() {
            public int compare(Board o1, Board o2) {
                if (o1.manhattan() < o2.manhattan()) return -1;
               else if (o1.manhattan() == o2.manhattan()) return 0;
               else return 1;
            }
        });
    	if (blank_j - 1 >= 0){
    		int[][] arr_temp = getCopy();
    		arr_temp[blank_i][blank_j] = arr_temp[blank_i][blank_j - 1];
    		arr_temp[blank_i][blank_j - 1] = 0;
    		q.insert(new Board(arr_temp));
//    		arr_temp = blocks.clone();
    	}
    	if (blank_j + 1 < N){
    		int[][] arr_temp = getCopy();
    		arr_temp[blank_i][blank_j] = arr_temp[blank_i][blank_j + 1];
    		arr_temp[blank_i][blank_j + 1] = 0;
    		q.insert(new Board(arr_temp));
//    		arr_temp = blocks.clone();
    	}
    	if (blank_i - 1 >= 0){
    		int[][] arr_temp = getCopy();
    		arr_temp[blank_i][blank_j] = arr_temp[blank_i - 1][blank_j];
    		arr_temp[blank_i - 1][blank_j] = 0;
    		q.insert(new Board(arr_temp));
//    		arr_temp = blocks.clone();
    	}
    	if (blank_i + 1 < N){
    		int[][] arr_temp = getCopy();
    		arr_temp[blank_i][blank_j] = arr_temp[blank_i + 1][blank_j];
    		arr_temp[blank_i + 1][blank_j] = 0;
    		q.insert(new Board(arr_temp));
//    		arr_temp = blocks.clone();
    	}
    	return q;
    }
    
    // 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 ", blocks[i][j]));
            }
            s.append("\n");
        }
        return s.toString();
    }
    
    private int[][] getCopy(){
    	int[][] result = new int[N][];
    	for (int i=0; i<N; i++){
    		result[i] = Arrays.copyOf(blocks[i], N);
    	}
    	return result;
    }
    
    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);
        StdOut.println(initial);
        StdOut.println(initial.hamming());
    }
}


Solver.java
import java.util.Comparator;

public class Solver {
	private boolean isSolve = false;
	private int move = -1;

	private class SearchNode implements Comparable<SearchNode>{
		private final Board board;
		private final int move;
		private final int priority;
		private final SearchNode parent;
		private final boolean isTwin;
		
		public SearchNode(Board board, int move, SearchNode parent, boolean isTwin){
			this.board = board;
			this.move = move;
			this.priority = board.manhattan() + move;
			this.parent = parent;
			this.isTwin = isTwin;
		}

		@Override
		public int compareTo(SearchNode that) {
			if (this.board.equals(that.board)) return 0;
			if (this.priority < that.priority) return -1;
			else return 1;
		}
	}
	
	private MinPQ<SearchNode> minPQ = new MinPQ<SearchNode>(new Comparator<SearchNode>() {
        public int compare(SearchNode o1, SearchNode o2) {
            if (o1.priority < o2.priority) return -1;
           else if (o1.priority == o2.priority) return 0;
           else return 1;
        }
    });
	
	private Stack<Board> solutionQueue = new Stack<Board>();
	
	// find a solution to the initial board (using the A* algorithm)
    public Solver(Board initial){
    	Board initialTwin = initial.twin();
    	SearchNode initSearchNode = new SearchNode(initial, 0, null, false);
    	SearchNode initSearchNodeTwin = new SearchNode(initialTwin, 0, null, true);
    	minPQ.insert(initSearchNode);
    	minPQ.insert(initSearchNodeTwin);
    	solve();
    }
    
    private void solve(){
    	while(true){
    		//solve for original
        	SearchNode searchNode = minPQ.delMin();
        	if (searchNode.board.isGoal()){
        		if (searchNode.isTwin){
        			this.isSolve = false;
        			this.move = -1;
        		} else {
        			this.isSolve = true;
            		this.move = searchNode.move;
            		this.solutionQueue.push(searchNode.board);
            		while(searchNode.parent != null){
            			searchNode = searchNode.parent;
            			this.solutionQueue.push(searchNode.board);
            		}
        		}
        		break;
        	}else{
        		for (Board neiborBoard: searchNode.board.neighbors()){
        			SearchNode neiborNode = new SearchNode(neiborBoard, searchNode.move+1, searchNode, searchNode.isTwin);
        			if (searchNode.parent == null){
        				minPQ.insert(neiborNode);
        			} else if (!searchNode.parent.board.equals(neiborNode.board)){
        				minPQ.insert(neiborNode);
        			}
        		}
        	}
    	}
    }
    
    // is the initial board solvable?
    public boolean isSolvable(){
    	return this.isSolve;
    }
    
    // min number of moves to solve initial board; -1 if no solution
    public int moves(){
    	return this.move;
    }
    
    // sequence of boards in a shortest solution; null if no solution
    public Iterable<Board> solution(){
    	if (this.isSolve){
    		return this.solutionQueue;
    	}else{
    		return null;
    	}
    	
    }
    
    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);
        }
    }
}







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章