骑士之旅 - 导致无限循环,我不知道为啥

Posted

技术标签:

【中文标题】骑士之旅 - 导致无限循环,我不知道为啥【英文标题】:Knights tour - results in an infinite loop and i can't figure out why骑士之旅 - 导致无限循环,我不知道为什么 【发布时间】:2014-07-03 15:44:26 【问题描述】:

我正在尝试使用回溯来解决骑士的旅行问题。我认为我拥有的算法应该可以工作。我试过了,但我不知道为什么它不起作用。它导致无限循环。

但是,如果我注释掉回溯solutionBoard[dst.x][dst.y]=-1; 的行,它就可以工作! 我只是不明白为什么! 任何帮助,将不胜感激。

private int solutionBoard[][] = new int [8][8];

// The eight possible moves a knight can make from any given position
private static final Point[] MOVES = new Point[]  new Point(-2, -1),
        new Point(-2, 1), new Point(2, -1), new Point(2, 1),
        new Point(-1, -2), new Point(-1, 2), new Point(1, -2),
        new Point(1, 2) ;

private int count = 0;

public KnightsTour_DFS()
    // board is 0- 7
    //initialize visited
    for(int i =0; i<8;i++)
        for(int j = 0; j< 8; j++)
            solutionBoard[i][j] = -1;
        
    

    solutionBoard[0][0]=count++;
    if(findTour(0, 0))
        System.out.println("Tour found!!");
        printSolution();
    


public boolean findTour(int x, int y)
    if(x <0 || y <0 || x>7 || y > 7 )
        return false;
    
    if(count == 64)
        //we've covered all node
        return true;
    
    for(int i = 0; i < this.MOVES.length; i++)
        Point dst =  new Point(x + MOVES[i].x, y + MOVES[i].y);
        if(canMove(dst))
            solutionBoard[dst.x][dst.y]=count++;
            if(findTour(dst.x, dst.y))
                System.out.println("Solution shown on board\n");
                return true;
            
            else
                count --;
                solutionBoard[dst.x][dst.y]=-1;
            
               
    
    return false;


private void printSolution() 
    System.out.println("Solution shown on board\n");
    for (int[] rows : solutionBoard) 
        for (int r : rows) 
            System.out.printf("%2d ", r);
        
        System.out.println();
    

public boolean canMove(Point destination)
    if(destination.x<0 || destination.y<0 || destination.x>7|| destination.y>7)
        return false;
    
    if(solutionBoard[destination.x][destination.y] != -1)
        //already visited 
        return false;
    
    return true;

【问题讨论】:

你是什么意思,当你删除该行时它会起作用?它会终止,还是找到正确的解决方案?你确定它不仅非常慢吗? 它终止并找到正确的解决方案。 事实上,@tobias_k 观察到的是正确的:通过不删除您设置的值,您必然会设置重复项,在计数达到 64 之前用完空闲方块,并且找不到完全游览。 似乎可以在中间广场开始使用 5x5 板。即使采用看似正确的解决方案,7x7 也能快速完成。 8x8 有什么特别之处吗? 看看wikipedia has to say about brute force on 8x8 【参考方案1】:

您的算法似乎运行良好,对于较小的问题实例(如 5x5 或 7x7 板)产生正确的结果。这似乎是8x8 board is just too big for the brute force / 回溯方法。

不过,您可以简化 findTour 方法,使其更易于理解和调试:

public boolean findTour(int x, int y, int c) 
    solutionBoard[x][y] = c;
    if (c == size*size) 
        return true;
    
    for (Point p : MOVES) 
        Point dst =  new Point(x + p.x, y + p.y);
        if (canMove(dst) && findTour(dst.x, dst.y, c + 1)) 
            return true;
               
    
    solutionBoard[x][y] = -1;
    return false;

findTour(0, 0, 1)size = 7 的示例输出(需要使所有代码适应可变大小!)

 1 14  3 38  5 34  7 
12 39 10 33  8 37 26 
15  2 13  4 25  6 35 
40 11 32  9 36 27 44 
19 16 21 24 45 48 29 
22 41 18 31 28 43 46 
17 20 23 42 47 30 49    

更好:使用***文章中提到的其他算法之一,例如相当简单的 Warnsdorff 启发式算法:"We move the knight so that we always proceed to the square from which the knight will have the fewest onward moves." 我们可以通过对移动进行排序来实现这一点...

public Point[] sortedPoints(final int x, final int y) 
    Point[] sorted = Arrays.copyOf(MOVES, MOVES.length);
    Arrays.sort(sorted, new Comparator<Point>() 
        public int compare(Point p1, Point p2) 
            return Integer.signum(nextMoves(p1) - nextMoves(p2));
        ;
        private int nextMoves(Point p) 
            Point dst = new Point(x + p.x, y + p.y);
            if (canMove(dst)) 
                int s = 0;
                for (Point m : MOVES) 
                    Point dst2 = new Point(dst.x + m.x, dst.y + m.y);
                    if (canMove(dst2)) 
                        s++;
                    
                
                return s;
             else 
                return 999;
            
        
    );
    return sorted;

...并将后继循环更改为for (Point p : sortedPoints(x, y))。结果:

size     findTour calls without and with heuristic
5x5                     76497       25 
7x7                     8947880     49
8x8                     ???         64
20x20                   ???         400

确实,对于我尝试过的所有尺寸,findTour 方法被调用完全size^2 次,即它在第一次尝试时找到了游览,根本没有回溯。

【讨论】:

您也可以将findTours 方法更改为使用堆栈而不是递归,否则您将在大于66x66 的板上获得***Error。 (如果使用启发式方法,第一选择确实一直有效,那么您甚至不需要这样做。)

以上是关于骑士之旅 - 导致无限循环,我不知道为啥的主要内容,如果未能解决你的问题,请参考以下文章

为啥意外的无限循环会增加 CPU 使用率?

React 组件中的错误导致应用程序重新渲染,从而导致无限循环。为啥?

为啥这会导致无限循环

为啥这段代码不会导致无限循环?

有人能告诉我为啥这个方法会进入无限循环吗?

为啥在循环开始时调用 requestAnimationFrame 不会导致无限递归?