哪个是提供解决15个谜题的最佳算法?

时间:2014-05-13 11:58:08

标签: ios algorithm puzzle

我正在努力寻找随机生成" 15拼图的解决方案步骤"。那么告诉我哪个是最好的算法来快速解决它。提供我这样做的方法。

我正在制作一个包含4 * 4数组的节点树,并遍历尚未处理的所有节点,当我得到解决方案时,我停止迭代。

在viewcontroller中我有一些代码

- (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;}

并且在节点类中我将节点扩展为(除了父节点使用的移动)

-(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;}

目前我正在使用曼哈顿距离A *,但是没有在很长时间内获得结果,应用程序内存增加到1GB并且应用程序崩溃。

1 个答案:

答案 0 :(得分:4)

我假设您正在寻找达到this拼图目标的最短路径。您可以将A* algorithm与当前电路板和目标板之间的manhattan distance一起用作成本函数。

following code in Java实现了算法。函数Solver将NxN板的大小作为输入N,然后将相应的N * N个数作为[0,N ^ 2],给出2d网格中数字的位置。它输出所需的最小移动次数和实际移动次数。 0表示拼图中的空位。

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

所以输入

3
   7   8   5
   4   0   2
   3   6   1

算法生成输出

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

我还想提一下

  • 找到N-by-N滑块拼图的最短解决方案是NP-Hard,因此不太可能存在有效的解决方案。
  • 如果您不是在寻找最短路径解决方案,而是在输入中快速运行的任何解决方案,那么this论文描述了一种算法,可以保证最多执行N ^ 3次移动。

因此,虽然我给出的解决方案在大多数输入上运行速度很快,但在其他困难的输入上可能会失败。

另请注意,并非所有谜题都可以解决。对于无法解决的谜题,算法会打印拼图无法解决的问题。

PS。上面实现的算法遵循this programming assignment的指导原则。