8 puzzle問題

    8-puzzle問題是cousera上algorithm的第四周作業。

    8-puzzle問題如上所示,用A*算法求從一個亂序的數字拼圖(initial board)到順序(goal board)數字拼圖的最短路徑,每次移動只能通過將空白塊與數字塊交換來實現。

    感覺cousera上作業的specification已經將A*算法講的很清楚了,主要是有些需要注意的地方,如果不知道的話很難得到滿分。

    這題的A*算法的大概思路爲:

 1.將initial放入一個優先級隊列中(優先級是隊列中元素與goal的曼哈頓距離以及initial移動到隊列中元素的距離);

2.若隊列不爲空,從優先級隊列中取出優先級最高的board;

3.判斷2中取出的board,是否是goal board,是的話則得到了最終的goal board,可以找出最短路徑,得到結果;否則轉4;

4.找出這個board的neighbour(即移動一次得到的所有board),放入優先級隊列中),轉2;

但這樣子就有個問題,3中從優先級隊列中取出了優先級最高的元素之後,如何反推得到移動路線和移動次數。單純用Board類無法做到,就得新建一個類searchnode,類中存放着board和board的移動次數以及board的前一個board(這樣的話通過這個就可以反推最短路徑),因此這個優先級隊列應該是searchnode的優先級隊列,而不是Board的。

又因爲不是每一個initial board都是可以移動得到goal board(有解)的,如何檢測呢,作業說明中介紹了這樣一個結論:如果一個initial board有解,那麼它的twin board(交換任意兩個相鄰非空白元素得到的board)則無解;如果initial無解,twin board就有解;

因此只要改進以下A*算法的實現思路,將initial和一個twin都放入同一個優先級隊列,最後一定可以得到一個goal,檢查goal的源頭到底是initial還是twin即可判斷initial是否有解。

作業需要實現兩個類。

Borad類的實現沒啥大問題,需要注意的是equals的實現完整性,判斷是否爲空,爲自己,類是否相同以及toString的實現要按照課程的格式。

Solver類的實現需要注意的有:

1,將一些需要多次調用的函數,提前用實例變量存儲下來,比如求的曼哈頓距離,優先級;

2.提前判斷,避免重複計算的情況,這裏主要是指一個board的prev和neighbour相同的情況(剔除走回頭路的情況)。

 

滿分的代碼如下:

Board.java:

/******************************************************************************
 *  NetID:   lark
 *  CreatedTime: 2020/2/18 17:09.
 *
 *  Description:  Board.java
 ******************************************************************************/
import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;

import java.util.Iterator;


public class Board {
    private int[][] tiles;
    private int n;
    private final int hamming;
    public Board(int[][] tiles)
    {
        n = tiles.length;
        this.tiles = new int[n][n];
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                this.tiles[i][j] = tiles[i][j];
            }
        }
        int ham = 0;
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                if (tiles[i][j] != (i * n + j + 1))
                {
                    ham++;
                }
            }
        }
        hamming = ham - 1;

    }

    // string representation of this board
    public String toString()
    {
        StringBuilder  result = new StringBuilder();
        result.append(dimension()+"\n");
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                result.append(String.format("%2d ",tiles[i][j]));
            }
            result.append("\n");
        }
        return result.toString();
    }

    // board dimension n
    public int dimension()
    {
        return n;
    }

    // number of tiles out of place
    public int hamming()
    {
        return hamming;
    }

    // sum of Manhattan distances between tiles and goal
    public int manhattan()
    {
        int res = 0;
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                if (tiles[i][j] != 0)
                {
                    int newadd = 0;
                    if (tiles[i][j] % n == 0)
                    {
                        newadd = Math.abs(tiles[i][j] / n -1 - i) + Math.abs((tiles[i][j] - 1) % n - j);
                    }
                    else
                    {
                        newadd = Math.abs(tiles[i][j] / n - i) + Math.abs((tiles[i][j] - 1) % n - j);
                    }
                    res += newadd;
                }
            }
        }
        return res;
    }

    // is this board the goal board?
    public boolean isGoal()
    {
        if (hamming == 0)
        {
            return true;
        }
        return false;
    }

    // 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 that = (Board) y;
        if (that.dimension() != this.dimension()) return false;
        if (this.toString().compareTo(that.toString()) == 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    // all neighboring boards
    public Iterable<Board> neighbors()
    {
        return new Boards();
    }

    private class Boards implements Iterable<Board>
    {
        Board[] neighours = new Board[4];
        int index = 0;
        Boards()
        {
            int col = 0,row = 0;

            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    if (tiles[i][j] == 0)
                    {
                        col = i;
                        row = j;
                    }
                }
            }
            if (col != 0)
            {
                exch(col - 1,row,col,row);
                neighours[index++] = new Board(tiles);
                exch(col - 1,row,col,row);
            }
            if ( col != (n-1))
            {
                exch(col + 1, row, col, row);
                neighours[index++] = new Board(tiles);
                exch(col + 1, row, col, row);
            }
            if (row != 0)
            {
                exch(col, row - 1, col, row);
                neighours[index++] = new Board(tiles);
                exch(col, row - 1, col, row);
            }
            if (row != (n-1))
            {
                exch(col, row + 1, col, row);
                neighours[index++] = new Board(tiles);
                exch(col, row + 1, col, row);
            }
        }
        @Override
        public Iterator<Board> iterator() {
            return new arounditerator();
        }
        private class arounditerator implements Iterator<Board>
        {
            int cur = 0;
            @Override
            public boolean hasNext()
            {
                if (cur < index)
                {
                    return true;
                }
                return false;
            }

            @Override
            public Board next() {
                return neighours[cur++];
            }
        }
    }
    // a board that is obtained by exchanging any pair of tiles
    public Board twin()
    {
        int row = 0,col = 0;
        if (tiles[row][col] == 0 || tiles[row + 1][col] == 0)
        {
            col++;
        }
        exch(row,col,row+1,col);
        Board result = new Board(tiles);
        exch(row,col,row+1,col);
        return result;
    }

    private void exch(int i,int j,int m,int n)
    {
        int temp = tiles[i][j];
        tiles[i][j] = tiles[m][n];
        tiles[m][n] = temp;
    }
    // unit testing (not graded)
    public static void main(String[] args)
    {
        In in = new In(args[0]);
        int n = in.readInt();
        int[][] tiles = new int[n][n];
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                tiles[i][j] = in.readInt();
        Board initial = new Board(tiles);
        StdOut.println(initial);
        StdOut.println(initial.dimension());
        StdOut.println("manhattan=" + initial.manhattan());
        StdOut.println("hamming=" + initial.hamming());
        StdOut.println(initial.isGoal());
    }

}

Solver.java:


/******************************************************************************
 *  NetID:   lark
 *  CreatedTime: 2020/2/18 22:57.
 *
 *  Description:  Solver.java
 ******************************************************************************/
import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.MinPQ;
import java.util.Arrays;

public class Solver {
    private Board[] result;//用來存放路徑
    private boolean issolvable = false;//是否有解
    // find a solution to the initial board (using the A* algorithm)
    public Solver(Board initial)
    {
        MinPQ<searchnode> gameTree = new MinPQ<>();//A*算法所需要的最小堆。
        if (initial == null) throw new  IllegalArgumentException();
        gameTree.insert(new searchnode(initial));
        gameTree.insert(new searchnode(initial.twin()));
        while (!gameTree.isEmpty())
        {
            searchnode min  = gameTree.delMin();
            for (Board neighour:min.board.neighbors())
            {
                if (min.moves >= 1 && neighour.equals(min.prev.board));
                else
                {
                    gameTree.insert(new searchnode(neighour,min));
                }
            }
            if (min.board.isGoal())
            {
                searchnode cur = min;
                result = new Board[min.moves + 1];
                for (int i = min.moves; i >= 0; i--)
                {
                    result[i] = cur.board;
                    cur = cur.prev;
                }
                if (result[0].equals(initial)) issolvable = true;
                break;
            }


        }
    }

    // is the initial board solvable? (see below)
    public boolean isSolvable()
    {
        boolean result = issolvable;
        return result;
    }

    // min number of moves to solve initial board
    public int moves()
    {
        if (issolvable)  return result.length - 1;
        else return -1;
    }
    // sequence of boards in a shortest solution
    public Iterable<Board> solution()
    {
        if (issolvable) return Arrays.asList(result);
        else return null;
    }

    private class searchnode implements Comparable<searchnode>
    {
        private final Board board;
        private final int moves;
        private final searchnode prev;
        private final int priority;
        private final int manhattan;
        searchnode(Board node)
        {
            board = node;
            moves = 0;
            prev = null;
            manhattan = board.manhattan();
            priority = manhattan + moves;

        }
        searchnode(Board node,searchnode pre)
        {
            board = node;
            prev = pre;
            moves = pre.moves + 1;
            manhattan = board.manhattan();
            priority = manhattan + moves;
        }

        @Override
        public int compareTo(searchnode o)
        {
            int sub = priority - o.priority;
            if (sub == 0)
            {
                return manhattan - o.manhattan;
            }
            return sub;
        }
    }
    // test client (see below)
    public static void main(String[] args) {

        // create initial board from file
        In in = new In(args[0]);
        int n = in.readInt();
        int[][] tiles = new int[n][n];
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                tiles[i][j] = in.readInt();
        Board initial = new Board(tiles);

        // 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);
        }

        solver.moves();
        solver.isSolvable();
        solver.isSolvable();
        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);
        }
    }

}

 

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