javaalgorithmrecursionbacktrackingknights-tour

Knight tour problem - order of moves affect performance


I was trying to implement a solution for knight tour problem. I faced an interesting issue. I would like to know the reason for the behavior.

This is the code - it works fine.

public class OpenKnightTour {

    private int[][] chessTable;
    private int startRow;
    private int startCol;
    private int ChessBoardSize;


    private final int[][] moves = {
            {-1, -2},
            {-2, -1},
            {-2, 1},
            {-1, 2},
            {1, -2},
            {2, -1},
            {2, 1},
            {1, 2}
    };

    public OpenKnightTour(int ChessBoardSize, int startRow, int startCol){
        this.ChessBoardSize = ChessBoardSize;
        this.startRow = startRow;
        this.startCol = startCol;
        this.chessTable = new int[ChessBoardSize][ChessBoardSize];
    }

    void move(){
        //first move
        this.chessTable[this.startRow][this.startCol] = 1;

        //start with 2nd move recursively
        moveHelper(this.startRow, this.startCol, 2);

        //print results
        this.print();

    }

    private boolean moveHelper(int r, int c, int count){
        if((count-1) == (this.ChessBoardSize * this.ChessBoardSize))
            return true;

        for(int[] move: moves){
            int nextR = r + move[0];
            int nextC = c + move[1];
            if(isMoveValid(nextR, nextC)){
                chessTable[nextR][nextC] = count;
                if(moveHelper(nextR, nextC, count + 1)){
                    return true;
                }
                chessTable[nextR][nextC] = 0;
            }
        }
        return false;
    }

    private void print(){
        Arrays.stream(this.chessTable)
                .forEach(a -> System.out.println(Arrays.toString(a)));
    }

    private boolean isMoveValid(int r, int c){
        if(!(r >= 0 && r < this.ChessBoardSize))
            return false;
        if(!(c >= 0 && c < this.ChessBoardSize))
            return false;
        return chessTable[r][c]==0;
    }

}

Now we could run this, starting with knight position as (2, 2) in the matrix

    OpenKnightTour o = new OpenKnightTour(8, 2, 2);
    o.move();

When i tried to run the starting position as (0, 0) , it ran continuously without showing any results. So i thought there could not be any solution for it. But if i change below order slightly, It works fine immediately.

private final int[][] moves = {
        {-1, -2},
        {-2, -1},
        {-2, 1},
        {-1, 2},
        {1, -2},
        {1, 2}, //moved from last 
        {2, -1},
        {2, 1},
};

What is really going on? how does this position of moves could affect the performance this much? then in that case, how do we know the correct order of the moves?


Solution

  • You are doing a brute force search through a very large search space. If you're unlucky in how you do your search, you can spend a long time with no solution showing up.

    The answer is that either you are lucky in your choice of starting place/starting pattern, or else you need to be smarter about recognizing "hopelessly stuck".

    For example every 10,000 times that you backtrack, do a scan of the whole board. If you can find hole that you cannot possibly get back to, then set a flag that causes you to backtrack to the last move that you could have visited it from. Then continue. This lets you skip over a potentially big chunk of useless work and get back to finding one of the many possible tours that are to be found.