Programming Assignment 4: 8 Puzzle

The Problem. 求解8数码问题。用最少的移动次数能使8数码还原.

Best-first search.使用A*算法来解决,我们定义一个Seach Node,它是当前搜索局面的一种状态,记录了从初始到达当前状态的移动次数和上一个状态。初始化时候,当前状态移动次数为0,上一个状态为null,将其放入优先级队列,通过不断的从优先级队列中取出Seach Node去扩展下一级的状态,直到找到目标状态。对于优先级队列中优先级的定义我们可以采用:Hamming priority function 和 Manhattan priority function,第一个表示有多少个块不在目标位置,第二个表示每一个块到他所在目标位置曼哈顿距离之和。

对于解决8数码来说,为了寻求最少步数,那么当前状态移动步数优先级需要考虑,同时选择Hamming或者Manhattan进行启发式的搜索。当目标状态出现时,我们就是使用了最少的步数。怎么证明?

A critical optimization.在搜索的过程中会遇到重复出现的状态,所以我们在进行下一个状态搜索的时候,判断不要将它相邻已经出现的状态加入到优先级队列中。

Game Tree. 搜索是一个博弈树的形式展开,每一个节点对应一个状态,树根是初始状态,在每一步中,A*算法删除优先级对联中priority最小的那个节点,然后进行处理

Detecting infeasible puzzles. 有些初始状态是无法通过移动来得到目标状态的,比如:

但是我们通过交换任意行不为空白的相邻的两个,如果按照这个初始状态来进行搜索,我们就可以得到目标状态。对于可行性的判断,可以根据初始状态和目标状态逆序数的奇偶性来进行判断,在进行移动后,应该奇偶性保持一致。但在这次Assignment中,不要求这么做,而是通过加入两个初始节点,一个是原有的,一个是进行相邻交换一次的,同时进行A*的搜索,如果原有的找到了目标解,那么就是可行,否则另一个找到了可行解,原有的就是不可行状态。

同一个初始状态到目标状态的最小移动次数是存在多解。其他还有IDA*,双向BFS等解法。

一些优化的地方,使用char[][] 比int[][]的空间更小。在进行曼哈顿距离求解的时候,我们可以用空间换时间,预存每个数字的曼哈顿距离,然后直接返回。

8数码是一个NP-Hard问题,没有有效的解存在。

完整的代码如下:

Board.java

public class Board {
    private int[][] matrix; // blocks
    private int N; // deimension
    private int posX, posY; // 0‘ position 

    // 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;
        matrix = new int[N][N];
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                matrix[i][j] = blocks[i][j];
                if (matrix[i][j] == 0) {
                    posX = i;
                    posY = j;
                }
            }
        }
    }

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

    // number of blocks out of place
    public int hamming() {
        int hammingDis = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (matrix[i][j] == 0) continue;
                if (i*N+j+1 != matrix[i][j]) hammingDis++;
            }
        }
        return hammingDis;
    }    

    // sum of Manhattan distances between blocks and goal
    public int manhattan() {
        int manhattanDis = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (matrix[i][j] == 0) continue;
                int x, y;
                if (matrix[i][j] % N == 0) {
                    x = matrix[i][j] / N - 1;
                    y = N - 1;
                } else {
                    x = matrix[i][j] / N;
                    y = matrix[i][j] % N - 1;
                }
                manhattanDis += Math.abs(i-x) + Math.abs(j-y);
            }
        }
        return manhattanDis;
    } 

    // is this board the goal board?
    public boolean isGoal() {
        if (posX != N-1 || posY != N-1) return false;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (matrix[i][j] == 0) continue;
                if (i*N+j+1 != matrix[i][j]) return false;
            }
        }
        return true;
    }               

    // a board obtained by exchanging two adjacent blocks in the same row
    public Board twin() {
        int x = -1, y = -1;
        int[][] tmpBlock = new int[N][N];
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (j < N-1 && matrix[i][j] != 0 && matrix[i][j+1] != 0) {
                    x = i;
                    y = j;
                }
                tmpBlock[i][j] = matrix[i][j];
            }
        }
        if (x == -1 && y == -1) throw new IllegalArgumentException();
        int t = tmpBlock[x][y];
        tmpBlock[x][y] = tmpBlock[x][y+1];
        tmpBlock[x][y+1] = t;
        return new Board(tmpBlock);
    }           

    // 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 (this.dimension() != that.dimension()) return false;
        int sz = this.dimension();
        for (int i = 0; i < sz; i++) {
            for (int j = 0; j < sz; j++) {
                if (this.matrix[i][j] != that.matrix[i][j])
                    return false;
            }
        }
        return true;
    }  

    // all neighboring boards
    public Iterable<Board> neighbors() {
        Queue<Board> queue = new Queue<Board>();
        int[] dx = {0, 0, -1, 1};
        int[] dy = {1, -1, 0, 0};
        for (int i = 0; i < 4; i++) {
            int x = posX + dx[i];
            int y = posY + dy[i];

            if (x < N && x >= 0 && y < N && y >= 0) {
                int tmp = matrix[posX][posY];
                matrix[posX][posY] = matrix[x][y];
                matrix[x][y] = tmp;
                queue.enqueue(new Board(matrix));
                tmp = matrix[posX][posY];
                matrix[posX][posY] = matrix[x][y];
                matrix[x][y] = tmp;
            }
        }
        return queue;
    }

    // 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 ", matrix[i][j]));
            }
            s.append("\n");
        }
        return s.toString();
    } 

    public static void main(String[] args) {
        int[][] mat = {
            {1, 2, 3},
            {4, 6, 0},
            {7, 8, 5}
        };
        //hamming
        //manhattan
        Board b = new Board(mat);
        Board c = b.twin().twin();
        StdOut.println(b.equals(c));
        StdOut.print(b.toString());
        for (Board it : b.neighbors()) {
            StdOut.print(it.toString());
            StdOut.println("hamming: " + it.hamming());
            StdOut.println("manhattan: " + it.manhattan());
        }
    }

}

Solver.java

public class Solver {

    private BoardNode targetBoardNode; // record targetBoardNode

    private class BoardNode implements Comparable<BoardNode> {
        private Board item;
        private BoardNode prev;
        private int move;
        private boolean isTwin;

        // compare by priority
        public int compareTo(BoardNode that) {
            if (that == null)
                throw new NullPointerException("Input argument is null");
            int thisPriority = this.move + this.item.manhattan();
            int thatPriority = that.move + that.item.manhattan();
            if (thisPriority < thatPriority)
                return -1;
            else if (thisPriority == thatPriority)
                return 0;
            else
                return 1;
        }
    }

    // find a solution to the initial board (using the A* algorithm)
    public Solver(Board initial) {
        targetBoardNode = null;
        // priority queue maintain the minimum elements
        MinPQ<BoardNode> minpq = new MinPQ<BoardNode>();
        // initial boardnode
        BoardNode bn = new BoardNode();
        bn.item = initial;
        bn.prev = null;
        bn.move = 0;
        bn.isTwin = false;
        minpq.insert(bn);
        // initial twin boardnode
        BoardNode twinbn = new BoardNode();
        twinbn.item = initial.twin();
        twinbn.prev = null;
        twinbn.move = 0;
        twinbn.isTwin = true;
        minpq.insert(twinbn);

        while (!minpq.isEmpty()) {
            BoardNode curbn = minpq.delMin();
            if (curbn.item.isGoal()) {
                if (curbn.isTwin) targetBoardNode = null;
                else targetBoardNode = curbn;
                break;
            }

            for (Board it : curbn.item.neighbors()) {
                if (curbn.prev == null || !curbn.prev.item.equals(it)) {
                    bn = new BoardNode();
                    bn.item = it;
                    bn.prev = curbn;
                    bn.move = curbn.move+1;
                    if (curbn.isTwin)
                        bn.isTwin = true;
                    else
                        bn.isTwin = false;
                    minpq.insert(bn);
                }
            }
        }
    }

    // is the initial board solvable?
    public boolean isSolvable() {
        return targetBoardNode != null;
    }       

    // min number of moves to solve initial board; -1 if no solution
    public int moves() {
        if (isSolvable())
            return targetBoardNode.move;
        else
            return -1;
    }

    // sequence of boards in a shortest solution; null if no solution
    public Iterable<Board> solution() {
        Stack<Board> stack = new Stack<Board>();
        BoardNode tmpbn = targetBoardNode;
        while (tmpbn != null) {
            stack.push(tmpbn.item);
            tmpbn = tmpbn.prev;
        }
        if (stack.isEmpty())
            return null;
        else
            return stack;
    }      

    // solve a slider puzzle (given below)
    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);
        }
    }
}

Programming Assignment 4: 8 Puzzle

时间: 2024-08-28 03:13:06

Programming Assignment 4: 8 Puzzle的相关文章

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类,包

Algorithm Part I:Programming Assignment(2)

问题描述: Programming Assignment 2: Randomized Queues and Deques Write a generic data type for a deque and a randomized queue. The goal of this assignment is to implement elementary data structures using arrays and linked lists, and to introduce you to g

Programming Assignment 3 : Pattern Recognition

这周的这个问题是在给定一系列的点中寻找多点共线的模式. 计算机视觉重要的两个部分:特征检测(Feature Dectection)和模式识别(Pattern Recognition).特征检测提取出图片的重要特征,模式识别发掘出这些特征中的模式.这里探究的点共线的问题在现实生活中也有很多应用,比如统计数据分析. Problem. 从二维平面上的N个互不相同的点中,绘制出每个(最多)连接的4个或4个以上点集合的线段. Point data type. 给定的Point类型的API public c

Algorithm Part I:Programming Assignment(3)

问题描述: Programming Assignment 3: Pattern Recognition Write a program to recognize line patterns in a given set of points. Computer vision involves analyzing patterns in visual images and reconstructing the real-world objects that produced them. The pr

Programming Assignment 1: WordNet

题目地址:http://coursera.cs.princeton.edu/algs4/assignments/wordnet.html 1. 题目阅读 WordNet定义 WordNet是指一个包含唯一根的有向无环图,图中每一组词表示同一集合,每一条边v→w表示w是v的上位词.和树不同的地方是,每一个子节点可以有许多父节点. 输入格式 同义词表 文件中每行包含一次同义名词.首先是序号:然后是词,用空格分开.若为词组,则使用下划线连接词组.最后是同义名词的注释 36,AND_circuit AN

Algorithms: Design and Analysis, Part 1 - Programming Assignment #1

自我总结: 1.编程的思维不够,虽然分析有哪些需要的函数,但是不能比较好的汇总整合 2.写代码能力,容易挫败感,经常有bug,很烦心,耐心不够好 题目: In this programming assignment you will implement one or more of the integer multiplication algorithms described in lecture. To get the most out of this assignment, your pro

CMPT 459.1-19. Programming Assignment

---title: "CMPT 459.1-19. Programming Assignment 1"subtitle: "FIFA 19Players"author: "Name - Student ID"output: html_notebook---### IntroductionThe data has detailed attributes for every player registered inthe latest edition

C200 Programming Assignment

C200 Programming Assignment № 8Computer ScienceSchool of Informatics, Computing, and EngineeringMarch 30, 2019ContentsIntroduction 2Problem 1: Newton Raphson Root Finding Algorithm 4Root . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

第二周:神经网络的编程基础----------2、编程作业常见问题与答案(Programming Assignment FAQ)

Please note that when you are working on the programming exercise you will find comments that say "# GRADED FUNCTION: functionName". Do not edit that comment. The function in that code block will be graded. 1) What is a Jupyter notebook? A Jupyt