简体   繁体   English

有人可以帮助我使用这个骑士的旅行代码吗?

[英]Can someone help me with this knight's tour code?

The code seems fine to me, but the output is too short and the answer is no when it should be yes (starting at a,0, the knight should be able to tour the entire board) 代码对我来说似乎很好,但是输出太短而答案是肯定的,因为它应该是肯定的(从0开始,骑士应该能够巡视整个棋盘)

By the way, the reason my positionsVisted array is [9][9] is because I wanted the values to be 1-8, to match the output. 顺便说一句,我的positionsVisted数组是[9] [9]的原因是因为我希望值为1-8,以匹配输出。

public class KnightChessRiddle {
static // given a chess board and a 0,0 starting point, can a knight pass through
// all the the squares without passing
// a square twice

int[][] positionsVisited = new int[9][9];
static int positionX = 1;
static int positionY = 1;
boolean stop = false;
static boolean continUe = false;
static int moveCounter = -1;

public static void main(String[] args) {
    if (recursive(1, 1, 0)) {
        System.out.println("yes");
    }
    else System.out.println("no");
}

public static boolean recursive(int x, int y, int moveType){


    if (x>8||x<=0||y>8||y<=0) return false;
    if (positionsVisited[x][y]==1) {
        return false;
    }
    positionX = x;
    positionY = y;
    positionsVisited[positionX][positionY]++;

    char c;
    c='a';
    switch (positionX) {
    case 1:
        c='a';
        break;
    case 2:
        c='b';
        break;
    case 3:
        c='c';
        break;
    case 4:
        c='d';
        break;
    case 5:
        c='e';
        break;
    case 6:
        c='f';
        break;
    case 7:
        c='g';
        break;
    case 8:
        c='h';
        break;

    default: 
        break;
    }
    moveCounter++;
    System.out.println("doing move "+moveType+" move count: "+moveCounter);
    System.out.println("Knight is in "+ c +","+positionY);

    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    if (recursive(positionX+2, positionY+1, 1)) {
        return true;
    }
    else if (recursive(positionX+1, positionY+2, 2) ) {
        return true;
    }
    else if (recursive(positionX+2, positionY-1, 3) ) {
        return true;
    }
    else if (recursive(positionX+1, positionY-2, 4)) {
        return true;
    }
    else if (recursive(positionX-2, positionY+1, 5)) {
        return true;
    }
    else if (recursive(positionX-1, positionY+2, 6)) {
        return true;
    }
    else if (recursive(positionX-2, positionY-1, 7)) {
        return true;
    }
    else if (recursive(positionX-1, positionY-2, 8)) {
        return true;
    }
    else return false;
}

This is the output of the program: 这是该程序的输出:

doing move 0 move count: 0
Knight is in a,1
doing move 1 move count: 1
Knight is in c,2
doing move 1 move count: 2
Knight is in e,3
doing move 1 move count: 3
Knight is in g,4
doing move 2 move count: 4
Knight is in h,6
doing move 5 move count: 5
Knight is in f,7
doing move 1 move count: 6
Knight is in h,8
doing move 8 move count: 7
Knight is in g,6
doing move 4 move count: 8
Knight is in h,4
doing move 5 move count: 9
Knight is in f,5
doing move 2 move count: 10
Knight is in g,7
doing move 4 move count: 11
Knight is in h,5
doing move 5 move count: 12
Knight is in f,6
doing move 1 move count: 13
Knight is in h,7
doing move 5 move count: 14
Knight is in f,8
doing move 7 move count: 15
Knight is in d,7
doing move 4 move count: 16
Knight is in e,5
doing move 4 move count: 17
Knight is in f,3
doing move 2 move count: 18
Knight is in g,5
doing move 4 move count: 19
Knight is in h,3
doing move 5 move count: 20
Knight is in f,4
doing move 4 move count: 21
Knight is in g,2
doing move 7 move count: 22
Knight is in e,1
doing move 6 move count: 23
Knight is in d,3
doing move 3 move count: 24
Knight is in f,2
doing move 3 move count: 25
Knight is in h,1
doing move 6 move count: 26
Knight is in g,3
doing move 5 move count: 27
Knight is in e,4
doing move 5 move count: 28
Knight is in c,5
doing move 1 move count: 29
Knight is in e,6
doing move 5 move count: 30
Knight is in c,7
doing move 1 move count: 31
Knight is in e,8
doing move 8 move count: 32
Knight is in d,6
doing move 5 move count: 33
Knight is in b,7
doing move 1 move count: 34
Knight is in d,8
doing move 8 move count: 35
Knight is in c,6
doing move 1 move count: 36
Knight is in e,7
doing move 1 move count: 37
Knight is in g,8
no

The algorithm you have implemented is the following: 您实施的算法如下:

Order the possible knight's moves from a square as follows: 从正方形命令可能的骑士移动如下:

On each move, choose the lowest numbered move that is still allowed. 在每次移动中,选择仍然允许的编号最小的移动。

(A) If you cover all the squares, return true . (A)如果覆盖所有方块,则返回true

(B) If you can no longer make a move, return false . (B)如果你不能再采取行动,则返回false

There are two problems with your code. 您的代码有两个问题。 The first, which has been pointed out already, is that you miss out the check (A). 第一个已经指出的是你错过了支票(A)。 The second, more serious, problem is that this algorithm does not work. 第二个更严重的问题是这个算法不起作用。 In fact, you end up with the following: 事实上,您最终会得到以下结果:

In this picture, the black knights represent the start and end squares, while the white knights are all the other squares covered. 在这张照片中,黑色骑士代表开始和结束方块,而白色骑士则是所有其他方块。 If you follow your algorithm, you eventually end up in a square where you can't get to any other squares that haven't been covered already. 如果你遵循你的算法,你最终会进入一个方形,你无法到达任何其他尚未覆盖的方格。 That doesn't mean that you can't do a knight's tour on a chessboard, just that your algorithm doesn't work. 这并不意味着你不能在棋盘上进行骑士之旅,只是你的算法不起作用。


If that really is the algorithm you want to implement, then there is no reason to use recursion, as a for loop will work just as well. 如果这确实是你想要实现的算法,那么就没有理由使用递归,因为for循环也可以正常工作。 Your function recursive returns true when there exists a valid knight's move from the square we are currently on. 当有效骑士从我们当前所在的方格移动时,你的函数recursive返回true。 There are two reasons that this is not actually a recursive algorithm: 有两个原因,这实际上不是一个递归算法:

  1. The recursive function is not idempotent - it has a side effect, which is to fill in a square of the positionsVisited array. recursive函数不是幂等的 - 它有副作用,即填充positionsVisited数组的一个正方形。
  2. The recursive function calls on a global variable - positionsVisited (I say 'global variable', rather than 'private field', because what you have written is essentially procedural code). recursive函数调用全局变量 - positionsVisited (我说'全局变量',而不是'私有字段',因为你所写的内容基本上是程序代码)。

Instead, the function recursive should tell you a much more general piece of information: given a particular square on the chess board, and a particular set of squares which we are not allowed to visit, is there a knight's tour of the remaining squares? 相反, recursive函数应该告诉你一个更为一般的信息:给定棋盘上的特定方块,以及我们不允许访问的特定方块组,是否有骑士游览剩余的方块? (Of course, calling that function with the square a1 and an empty array of visited positions will tell you whether there is a knight's tour at all.) The function can also return a string which will tell you what the knight's tour is. (当然,用方形a1调用该函数和一个空的访问位置数组将告诉你是否有骑士的游览。)该函数还可以返回一个字符串,告诉你骑士的游览是什么。

The structure of the recursive function should then be something like the following: 递归函数的结构应该类似于以下内容:

private boolean isKnightsTour(Square currentSquare,
                              int[9][9] visitedSquares,
                              KnightsTour tour)
{
  // Append the current square to the array of visited squares.
  int[9][9] newVisitedSquares = visitedSquares;
  newVisitedSquares[currentSquare.getX()][currentSquare.getY()] = 1;

  // If we have visited all the squares, there is a knight's tour.  
  // Add some code here to check for that.
  if (allSquaresVisited()) {
    tour = new KnightsTour(currentSquare);
    return true;
  }

  // Test all squares a knight's move away.  If you get a knight's tour, 
  // append the current square to the start and return that.  
  KnightsTour newTour;
  if (isKnightsTour(currentSquare.doMove1(), newVisitedSquares, newTour) {
    newTour.appendStart(currentSquare);
    tour = newTour;
    return true;
  }

  // Repeat for the other knight's moves.

  else {
    tour = null;
    return false;
  }
}

Or, in words: 或者,用文字:

Recursively check all the squares a knight's move away from the current square, passing in both the new square and a new array of visited squares formed by adding the current square. 递归检查骑士从当前方块移开的所有方块,传递新方块和通过添加当前方块形成的新访问方块阵列。 If there is a knight's tour from one of those squares, append the current square to the beginning of it to get a knight's tour from the current square. 如果从其中一个方格中有一个骑士之旅,则将当前方块附加到其开头,以从当前方块获得骑士之旅。

Rather than what you've written which is: 而不是你写的是:

Recursively build up a knight's tour by starting at a square and (fairly arbitrarily) choosing a legal knight's move at each step. 通过从一个正方形开始并(相当任意地)选择合法骑士在每一步的移动来递归地建立一个骑士之旅。 If we get to a position where we can't do any more knight's moves, return false . 如果我们到达一个我们不能再做骑士的动作的位置,则返回false

Can you see why the first (admittedly more complicated) recursive algorithm works and yours doesn't? 你能看出为什么第一个(不可否认的是更复杂的)递归算法起作用而你的算法不起作用?

Except problem with "all is visited" check I see at least one more problem, which is in class fields. 除了“全部被访问”检查的问题,我看到至少还有一个问题,即在类字段中。 When algorithm passes one branch of recursion, it marks some squares as visited, and since this information is class field, when it fails current branch and starts another, it sees all invalid information from previous attempts. 当算法通过递归的一个分支时,它将一些正方形标记为已访问,并且由于此信息是类字段,当它失败当前分支并启动另一个时,它会看到先前尝试的所有无效信息。

What if you try to pass positionsVisited , positionX and positionY as method arguments and remove it from class fields, so each method call will have it's own actual copy? 如果您尝试将positionsVisitedpositionXpositionY作为方法参数传递并将其从类字段中删除,那么每个方法调用都将拥有它自己的实际副本,该怎么办?


Final version 最终版本

public class KnightChessRiddle {

    private final static Map<Integer, Character> letters = new HashMap<>();

    static {
        letters.put(0, 'a');
        letters.put(1, 'b');
        letters.put(2, 'c');
        letters.put(3, 'd');
        letters.put(4, 'e');
        letters.put(5, 'f');
    }

    public static void main(String[] args) {
        if (recursive(0, 0, 0, new boolean[6][6], 1, "")) {
            System.out.println("yes");
        } else {
            System.out.println("no");
        }
    }

    private static boolean allVisited(boolean[][] positionsVisited) {
        for (int i = 0; i < positionsVisited.length; i++) {
            for (int j = 0; j < positionsVisited.length; j++) {
                if (!positionsVisited[i][j]) {
                    return false;
                }
            }
        }
        return true;
    }

    private static boolean recursive(int positionX, int positionY, int moveType,
            boolean[][] positionsVisited, int moveCounter, String currentMoves) {

        // checks
        if (allVisited(positionsVisited)) {
            System.out.println(currentMoves);
            return true;
        }

        if (positionX > 5 || positionX < 0 || positionY > 5 || positionY < 0) {
            return false;
        }

        if (positionsVisited[positionX][positionY]) {
            return false;
        }

        // make move
        positionsVisited[positionX][positionY] = true;

        char c = letters.get(positionX);
        currentMoves += "" + c + (positionY + 1) + " (move type: " + (moveType + 1) + ")\r\n";

        if (recursive(positionX + 2, positionY + 1, 1, cloneArray(positionsVisited), moveCounter + 1, currentMoves)) {
            return true;
        } else if (recursive(positionX + 1, positionY + 2, 2, cloneArray(positionsVisited), moveCounter + 1, currentMoves)) {
            return true;
        } else if (recursive(positionX + 2, positionY - 1, 3, cloneArray(positionsVisited), moveCounter + 1, currentMoves)) {
            return true;
        } else if (recursive(positionX + 1, positionY - 2, 4, cloneArray(positionsVisited), moveCounter + 1, currentMoves)) {
            return true;
        } else if (recursive(positionX - 2, positionY + 1, 5, cloneArray(positionsVisited), moveCounter + 1, currentMoves)) {
            return true;
        } else if (recursive(positionX - 1, positionY + 2, 6, cloneArray(positionsVisited), moveCounter + 1, currentMoves)) {
            return true;
        } else if (recursive(positionX - 2, positionY - 1, 7, cloneArray(positionsVisited), moveCounter + 1, currentMoves)) {
            return true;
        } else if (recursive(positionX - 1, positionY - 2, 8, cloneArray(positionsVisited), moveCounter + 1, currentMoves)) {
            return true;
        } else {
            return false;
        }

    }

    private static boolean[][] cloneArray(boolean[][] src) {
        boolean[][] newPositions = new boolean[src.length][src.length];
        for (int i = 0; i < src.length; i++) {
            System.arraycopy(src[i], 0, newPositions[i], 0, src[0].length);
        }
        return newPositions;
    } 
}

Here is the working variation with 6x6 board. 这是6x6板的工作变化。 With 8x8 board calculations take too much time on my machine. 使用8x8电路板计算会占用我的机器上太多时间。 Maybe it can be faster if you randomize move selection. 如果你随机选择移动选择可能会更快。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM