N-queens puzzle in Java with 1D array

时间:2016-02-12 20:19:55

标签: java arrays n-queens

I am working a problem that seems to be somewhat famous among beginning programmers, the 8 queens puzzle. I have seen several solutions to this problems using 2D arrays, recursion etc, but this problem is an assignment given in CS course book chapter introducing 1D arrays, so the available techniques to solve this problem are limited.

The procedure I have used, is by first creating a 1D array with the size of 64, which makes possible positions to place queens from index 0 to 63. A random position index is then generated, and a test is preformed to check if there is any queens attacking this position. If this position is not attacked by any queens, a queen is placed by setting the void showServerError() { [[TWMessageBarManager sharedInstance] showMessageWithTitle:@"Server Error" description:SERVER_ERROR type:TWMessageBarMessageTypeError duration:4.0]; } . When a queen is placed, the board[position] = true is incremented, and this process repeats until 8 queens have been placed.

If queens are placed in such a way that it is impossible to place 8, the board resets after 1 millisecond by preforming a timecheck, and retries to place the 8 queens. At the best I am able to place 7 queens, but the last remaining one is never placed. Each attempt is printed, along with queenCount for this attempt. Is it possible to use this approach, or is it a dead end?

Code example below:

queenCount

3 个答案:

答案 0 :(得分:2)

首先,8号阵列就足够了 数组 index 表示放置后插件的表示

[0, 2, 4, 6, 1, 3, 5, 7] 

意味着第一列中的女王被放置在第一行,第二列被放置在第三行,第三列被放置在第五行等等......

因此,当您放置一个新的女王时,检查您添加它的行是否已经在数组中。这样,您只需要担心对角线碰撞。

解决问题的最简单方法是递归(回溯)。如果不允许,您可以使用堆栈模拟递归。如果不允许这样做,你可以使用8个嵌套循环 - 丑陋。

您可以使用简单的技巧改进碰撞检查。它的工作原理如下 -
让我们说你的女王#0在第3行。
她对角线攻击了哪些细胞? 在第一列,它是第2行和第4行(-1+1
在第二列,它是第1行和第5行(-2+2
在第三列,它是第0行和第6行(-3+3
所以当你添加一个新的女王时,你会迭代前面的女王,用(newIndex - oldIndex) + oldRow == newRow检查一个对角线,用(newIndex - oldIndex) - oldRow == newRow检查另一个对角线

因此,考虑到所有这些,检查功能可能看起来像

boolean canAdd(List<Integer> p, int newRow) {
    if (p.contains(newRow))
        return false;
    int insertIndex = p.size();
    for (int i = 0; i < p.size(); i++) {
        if (p.get(i) + (insertIndex - i) == newRow || p.get(i) - (insertIndex - i) == newRow)
            return false;
    }
    return true;
}

虽然主递归函数看起来像

void solve(List<Integer> p, int index) {
    if (index == 8) {
        System.out.println(p);
        return;
    }

    for (int i = 0; i < 8; i++) {
        if (canAdd(p, i)) {
            p.add(i);
            solve(p, index + 1);
            p.remove(p.size() - 1);
        }
    }
}

你可以这样称呼它

solve(new ArrayList<Integer>(), 0);

答案 1 :(得分:1)

在解决这个问题几天之后,我现在有了一个解决方案,在N <= 20的合理时间内工作。就像这样。

  1. For i < N,初始化queens[i] = i。每行只能保存1个值,因此无需检查左侧或右侧的碰撞。只要数组中没有重复值,就不会发生任何列冲突。

  2. 使用该方法检查给定点的女王是否与另一个给定点的女王分享对角线。该方法检查x1和x0之间的距离是否等于y1和y0的距离。如果距离相等,则坐标(x0,y0)和(x1,y1)共享相同的对角线。

  3. 使用另一种方法调用shareDiagonal(int x0, int y0, int x1, int y1)来检查给定行的女王(例如第7行)是否与第7行上方的任何行上的女王相撞。如上所述,只有上面的行检查给定的行。原因是,例如,如果您正在检查row2是否存在任何对角线碰撞,则在检查具有更高索引值的行时,将显示与第2行下方的行的任何碰撞。如果第2行的女王与第4行的女王发生碰撞,则在检查第4行和上面的行时会显示。

  4. 第三种检查方法调用checkRowForCollision(int[] queens, int row),其中遍历每一行检查上面行的冲突。检查第1行是否与第0行上的任务发生任何冲突,检查第2行是否在第0行和第1行有任何冲突,如果第0,1和2行有任何冲突,则检查第3行等。

  5. 虽然任何一个女王之间都存在对角线碰撞,但是电路板会一直洗牌,直到它显示一个没有女王相互攻击的解决方案。

  6. 下面的代码示例:

    package ch7;
    
    public class Chapter_07_E22_EightQueens {
    
        static final int N = 8;
    
        public static void main(String[] args) {
    
            int[] queens = new int[N];
            int attempts = 0;
    
            for (int i = 0; i < N; i++) 
                queens[i] = i;
    
            while (checkBoardForCollision(queens)) {
                shuffleBoard(queens);
                attempts++;
            }
            printBoard(queens);
            System.out.println("Solution found in " + attempts + " attempts");
        }
    
        public static void printBoard(int[] queens) {
    
            for (int row = 0; row < N; row++) {
                System.out.printf("%-1c", '|');
                for (int column = 0; column < N; column++) {
                    System.out.printf("%-1c|", (queens[row] == column) ? 'Q' : ' ');
                }
                System.out.println();
            }       
        }
    
        public static boolean shareDiagonal(int x0, int y0, int x1, int y1) {
    
            int dy = Math.abs(y1 - y0);
            int dx = Math.abs(x1 - x0);
    
            return dx == dy;
        }
    
        public static boolean checkRowForCollision(int[] queens, int row) {
    
            for (int i = 0; i < row; i++) {
    
                if (shareDiagonal(i, queens[i], row, queens[row]))
                    return true;    
            }
            return false;
        }
    
        public static boolean checkBoardForCollision(int[] queens) {
    
            for (int row = 0; row < queens.length; row++)
                if (checkRowForCollision(queens, row))
                    return true;
    
            return false;
        }
    
        public static int[] shuffleBoard(int[] queens) {
    
            for (int i = queens.length - 1;  i > 0; i--) {
    
                int j = (int)(Math.random() * (i + 1));
    
                int temp = queens[i];
                queens[i] = queens[j];
                queens[j] = temp;
            }
            return queens;
        }
    }
    

答案 2 :(得分:0)

One of the problems, there may be more though, is in the HttpSession method.

checkTop

There are cases when the method doesn't find a slot (public static boolean checkTop(int position, boolean[] board) { // Checks each field above the current position while i - 8 > - 1 for (int i = position; i > (i - 8); i -= 8) { if ((i - 8) > -1) { if (board[i - 8]) return false; } } return true; } ) and board[i - 8] = true reaches the value of i. After this point, the condition of the 7 (for loop) will always be i > (i - 8) and the condition of the outermost true inside the loop (if) will always be if ( (i - 8) > -1. This causes the program to infinitely stay in the loop.

Example (false reaches -5):

i