Which is the best algorithm to provide moves to solve 15 puzzle? [closed]

别来无恙 提交于 2019-12-01 08:28:11

问题


i am working to find the solution steps for a randomly generated "15 puzzle". So tell me which is the best algorithm to use to solve it fast. Provide me approach to do so.

I am making a tree of nodes containing 4*4 array and traversing through all the node which are not yet processed and when i get the solution i stop the iteration.

In viewcontroller i have some code as

- (IBAction)getSolution:(id)sender {
while (!appDelegate.isResultFound) {
    TreeNode *node=[self nodeWithLowestCostAndUnproceessedInRootNode];
    [node expandNodeToChilds];
    //break;
}
NSLog(@"Result Found");
if([appDelegate.result isEqualToString:@""])
    NSLog(@"No move required");
else
    NSLog(@"%@",appDelegate.result);

}

-(TreeNode*)nodeWithLowestCostAndUnproceessedInRootNode{
TreeNode *node1;
int lowestCost=200;
for (TreeNode *node in appDelegate.treeNodes) {
    if([node myHeuristicsFunction]<lowestCost&&node.isProcessed==NO){
        node1=node;
        lowestCost=[node.cost intValue];
    }
}
return node1;}

and in node class i am expanding the node as (except the move used by the parent)

-(void)expandNodeToChilds{
[self checkMovesForEmptyPlace];

if(top.x>=0){
    [self addPuzzleBoxToTreeBySwapingPoint:top withMove:@"Bottom"];
}
if(right.y<=3){
    [self addPuzzleBoxToTreeBySwapingPoint:right withMove:@"Left"];
}
if(bottom.x<=3){
    [self addPuzzleBoxToTreeBySwapingPoint:bottom withMove:@"Top"];
}
if(left.y>=0){
    [self addPuzzleBoxToTreeBySwapingPoint:left withMove:@"Right"];
}
self.isProcessed=true;}

Currently i am using manhattan distance with A*, but not getting the result in significant time, app memory increases to 1GB and app crashes.


回答1:


I am assuming that you are looking for the shortest way to reach the goal for this puzzle. You can use A* algorithm with the manhattan distance between the current board and the goal board as the cost function.

The following code in Java implements the algorithm. The function Solver takes as input N, the size of the NxN board and then the corresponding N*N numbers ranging from [0,N^2] giving the locations of the numbers in the 2d grid. It outputs the minimum number of moves that are required and the actual moves. 0 indicates the empty position in the puzzle.

import java.io.InputStreamReader;
import java.util.*;

class Solver{
    private int N ; 
    private int minMoves ;
    public static int[] correctRow;
    public static int[] correctCol;

    private class Node implements Comparable<Node>{
        private Board board ; 
        private int moves ; 
        private Node prevNode ; 
        public Node(Board board,int moves,Node prev){
            this.board = board ; 
            this.moves = moves ; 
            this.prevNode = prev ; 
        }
        public int compareTo(Node that){
            int thisPriority = this.moves+this.board.manhattan() ; 
            int thatPriority = that.moves+that.board.manhattan() ; 
            if(thisPriority<thatPriority){
                return -1 ; 
            }else if(thisPriority>thatPriority){
                return 1 ; 
            }else{
                return 0 ; 
            }
        }
    }

    private Node lastNode ; 
    private boolean solvable ; 

    public Solver(Board initial){
        N = initial.dimension() ; 
        PriorityQueue<Node> pq = new PriorityQueue<Node>() ; 
        PriorityQueue<Node> pq2 = new PriorityQueue<Node>() ; 
        pq.add(new Node(initial,0,null)) ; 
        pq2.add(new Node(initial.twin(),0,null)) ; 
        while(true){
            Node removed = pq.poll();
            Node removed2 = pq2.poll();
            if(removed.board.isGoal()){
                minMoves = removed.moves ; 
                lastNode = removed ; 
                solvable = true ; 
                break ; 
            }
            if(removed2.board.isGoal()){
                minMoves = -1 ; 
                solvable = false ; 
                break ; 
            }

            Iterable<Board> neighbors = removed.board.neighbors() ; 
            Iterable<Board> neighbors2 = removed2.board.neighbors() ; 
            for(Board board : neighbors){
                if(removed.prevNode != null && removed.prevNode.board.equals(board) ){
                    continue ; 
                }
                pq.add(new Node(board,removed.moves+1,removed)) ; 
            }
            for(Board board : neighbors2){
                if(removed2.prevNode != null && removed2.prevNode.board.equals(board) ){
                    continue ; 
                }
                pq2.add(new Node(board,removed2.moves+1,removed2)) ; 
            }
        }
    }

    public boolean isSolvable(){
        return solvable ; 
    }

    public int moves(){
        return minMoves ; 
    }

    public Iterable<Board> solution(){
        if(!isSolvable()){
            return null ;
        }
        Stack<Board> stack = new Stack<Board>() ; 
        Node node = lastNode ; 
        while(true){
            if(node == null) break ; 
            Board board = node.board ; 
            node = node.prevNode ; 
            stack.push(board) ; 
        }
        return stack ; 
    }

    static void initCorrectRowsCols(int N){
        correctRow = new int[N*N] ; 
        int z = 0 ; 
        for(int i = 0 ; i < N ; i++ ){
            for(int j = 0 ; j < N ; j++ ){
                correctRow[z++] = i ; 
            }
        }
        z = 0 ; 
        correctCol = new int[N*N] ; 
        for(int i = 0 ; i < N ; i++ ){
            for(int j = 0 ; j < N ; j++ ){
                correctCol[z++] = j ; 
            }
        }
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        // create initial board from file
        Scanner in = new Scanner(new InputStreamReader(System.in));
        int N = in.nextInt();
        initCorrectRowsCols(N);
        int[][] blocks = new int[N][N];
        for (int i = 0; i < N; i++)
            for (int j = 0; j < N; j++)
            blocks[i][j] = in.nextInt();

        Board initial = new Board(blocks);

        // solve the puzzle
        Solver solver = new Solver(initial);

        long end = System.currentTimeMillis();
        System.out.println("time taken " + (end-start) + " milli seconds");

        // print solution to standard output
        if (!solver.isSolvable())
            System.out.println("No solution possible");
        else {
            System.out.println("Minimum number of moves = " + solver.moves());
            Stack<Board> stack = new Stack<Board>();
            for (Board board : solver.solution())
                stack.push(board);
            while(!stack.isEmpty()){
                System.out.println(stack.pop());
            }
        }
    }
}

class Board{
    private int[][] array ; 
    private int N ;
    int emptyRow;
    int emptyCol;
    boolean reached;
    int manhattan = 0;

    public Board(int[][] blocks){
        N = blocks.length ; 
        array = new int[N][N] ;
        reached = true;
        for(int i = 0 ; i < N ; i++ ){
            for(int j = 0 ; j < N ; j++ ) {
                array[i][j] = blocks[i][j] ;
                if(array[i][j] == 0){
                    emptyRow = i;
                    emptyCol = j;
                }
                if(array[i][j] != N*i + j+1){
                    if(!(i==N-1 && j==N-1)){
                        reached = false;
                    }
                }
                int num = array[i][j] ; 
                if(num==0){
                    continue ; 
                }
                int indManhattan = Math.abs(Solver.correctRow[num-1] - i) 
                        + Math.abs(Solver.correctCol[num-1]-j) ; 
                manhattan += indManhattan ;
            }
        }
    }

    public int dimension(){
        return N ; 
    }

    public int hamming(){
        int outOfPlace = 0 ; 
        for(int i = 0 ; i < N ; i++ ) {
            for(int j = 0 ; j < N ; j++ ){
                if(i==N-1 && j==N-1) {
                    break ; 
                }
                if(array[i][j] != i*N+j+1){
                    outOfPlace++ ; 
                }
            }
        }
        return outOfPlace ; 
    }

    public int manhattan(){
        return manhattan ; 
    }

    public boolean isGoal(){
        return reached ; 
    }

    public Board twin(){
        int[][] newArray = new int[N][N] ; 
        for(int i = 0 ; i < N ; i++ ){
            for(int j = 0 ; j < N ; j++ ){
                newArray[i][j] = array[i][j] ; 
            }
        }
        for(int i = 0 ; i < 2 ; i++ ) {
            if(newArray[i][0]==0 || newArray[i][5]==0){
                continue ; 
            }
                int temp = newArray[i][0] ; 
                newArray[i][0] = newArray[i][6] ; 
                newArray[i][7] = temp ; 
                break ; 

        }
        return new Board(newArray) ; 
    }

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

    public Iterable<Board> neighbors(){
        Queue<Board> q = new ArrayDeque<Board>() ; 
        int firstIndex0 = 0 ; 
        int secondIndex0 = 0 ; 
        for(int i = 0 ; i < N ; i++ ){
            for(int j = 0 ; j < N ; j++ ) {
                if(array[i][j] == 0){
                    firstIndex0 = i ; 
                    secondIndex0 = j ; 
                    break ; 
                }
            }
        }
        if(secondIndex0-1>-1){
            int[][] newArr = getCopy() ; 
            exch(newArr,firstIndex0,secondIndex0,firstIndex0,secondIndex0-1) ; 
            q.add(new Board(newArr)) ; 
        }
        if(secondIndex0+1<N){
            int[][] newArr = getCopy() ; 
            exch(newArr,firstIndex0,secondIndex0,firstIndex0,secondIndex0+1) ; 
            q.add(new Board(newArr)) ; 
        }
        if(firstIndex0-1>-1){
            int[][] newArr = getCopy() ; 
            exch(newArr,firstIndex0,secondIndex0,firstIndex0-1,secondIndex0) ;     
            q.add(new Board(newArr)) ; 
        }
        if(firstIndex0+1<N){
            int[][] newArr = getCopy() ; 
            exch(newArr,firstIndex0,secondIndex0,firstIndex0+1,secondIndex0) ; 
            q.add(new Board(newArr)) ; 
        }
        return q ; 
    }

    private int[][] getCopy(){
        int[][] copy = new int[N][N] ; 
        for(int i = 0 ; i < N ; i++ ) {
            for(int j = 0 ; j < N ; j++ ){
                copy[i][j] = array[i][j] ; 
            }
        }
        return copy ; 
    }

    private void exch(int[][] arr, int firstIndex,int secIndex,int firstIndex2,int secIndex2){
        int temp = arr[firstIndex][secIndex] ; 
        arr[firstIndex][secIndex] = arr[firstIndex2][secIndex2] ;  
        arr[firstIndex2][secIndex2] = temp ; 
    }

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

So for the input

3
   7   8   5
   4   0   2
   3   6   1

The algorithm generates the output

Minimum number of moves = 28
3
   7   8   5
   4   0   2
   3   6   1

3
   7   0   5
   4   8   2
   3   6   1

3
   7   5   0
   4   8   2
   3   6   1

3
   7   5   2
   4   8   0
   3   6   1

3
   7   5   2
   4   0   8
   3   6   1

3
   7   5   2
   4   6   8
   3   0   1

3
   7   5   2
   4   6   8
   3   1   0

3
   7   5   2
   4   6   0
   3   1   8

3
   7   5   2
   4   0   6
   3   1   8

3
   7   5   2
   0   4   6
   3   1   8

3
   0   5   2
   7   4   6
   3   1   8

3
   5   0   2
   7   4   6
   3   1   8

3
   5   4   2
   7   0   6
   3   1   8

3
   5   4   2
   7   1   6
   3   0   8

3
   5   4   2
   7   1   6
   0   3   8

3
   5   4   2
   0   1   6
   7   3   8

3
   5   4   2
   1   0   6
   7   3   8

3
   5   0   2
   1   4   6
   7   3   8

3
   0   5   2
   1   4   6
   7   3   8

3
   1   5   2
   0   4   6
   7   3   8

3
   1   5   2
   4   0   6
   7   3   8

3
   1   5   2
   4   3   6
   7   0   8

3
   1   5   2
   4   3   6
   7   8   0

3
   1   5   2
   4   3   0
   7   8   6

3
   1   5   2
   4   0   3
   7   8   6

3
   1   0   2
   4   5   3
   7   8   6

3
   1   2   0
   4   5   3
   7   8   6

3
   1   2   3
   4   5   0
   7   8   6

3
   1   2   3
   4   5   6
   7   8   0

I would also like to mention that

  • Finding a shortest solution to an N-by-N slider puzzle is NP-Hard, so it's unlikely that an efficient solution exists.
  • If you are not looking for a shortest path solution but any solution that runs fast in the input then this paper describes an algorithm that guarantees to perform at most N^3 moves.

Thus although the solution I have given runs fast on most of the inputs, it may fail on other difficult inputs.

Also note that not all puzzles are solvable. For the puzzles that cannot be solved, the algorithm prints that the puzzle cannot be solved.

PS. The algorithm implemented above follows the guidelines of this programming assignment.



来源:https://stackoverflow.com/questions/23630620/which-is-the-best-algorithm-to-provide-moves-to-solve-15-puzzle

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!