【问题标题】:Java NQueens Recursion and Enumeration Logic ExplanationJava NQueens递归枚举逻辑讲解
【发布时间】:2015-12-14 09:17:32
【问题描述】:

我正在做一些递归练习,遇到了 nQueens 问题here

我不确定我是否误解了递归发生了什么,但我不明白这段代码(如下)如何打印问题的多个解决方案。

例如,如果我使用 4 Queen 解决方案运行程序,它会打印出:

* Q * * 
* * * Q 
Q * * * 
* * Q * 

* * Q * 
Q * * * 
* * * Q 
* Q * * 

我也在一步步看,还是不太明白。

这是我认为正在发生的事情:
1. isConsistent 正在检查逻辑/在当前位置放置女王是否安全
2. printQueens 正在打印电路板
3. enumerate(int[] q, int n) 递归地将皇后放在一个解决方案中
4.(我猜) enumerate(int N) 是通过多板解决方案枚举的,但它只被调用一次。所以,这不可能。所以,我的下一个想法是它只是一种调用递归枚举方法的方法。但是,这意味着一旦递归完成找到第一块板的解决方案,它应该停止。所以,我回到我原来的问题:

它如何/在哪里计算板的多个解决方案?

我知道这可能很容易,但即使使用调试器,我也无法理解正在发生的事情。如果有人能解释它从哪里开始计算板的第二个解决方案,我将不胜感激。

/******************************************************************************
 *  Compilation:  javac Queens.java
 *  Execution:    java Queens N
 *  
 *  Solve the 8 queens problem using recursion and backtracing.
 *  Prints out all solutions.
 *
 *  Limitations: works for N <= 25, but slows down considerably
 *  for larger N.
 *
 *  Remark: this program implicitly enumerates all N^N possible
 *  placements (instead of N!), but the backtracing prunes off
 *  most of them, so it's not necessarily worth the extra
 *  complication of enumerating only permutations.
 *
 *
 *  % java Queens 3
 *
 *  % java Queens 4
 *  * Q * * 
 *  * * * Q 
 *  Q * * * 
 *  * * Q * 
 *
 *  * * Q * 
 *  Q * * * 
 *  * * * Q 
 *  * Q * * 
 *
 *  % java Queens 8
 *  Q * * * * * * * 
 *  * * * * Q * * * 
 *  * * * * * * * Q 
 *  * * * * * Q * * 
 *  * * Q * * * * * 
 *  * * * * * * Q * 
 *  * Q * * * * * * 
 *  * * * Q * * * * 
 *
 *  ...
 *
 ******************************************************************************/

public class Queens {

   /***************************************************************************
    * Return true if queen placement q[n] does not conflict with
    * other queens q[0] through q[n-1]
    ***************************************************************************/
    public static boolean isConsistent(int[] q, int n) {
        for (int i = 0; i < n; i++) {
            if (q[i] == q[n])             return false;   // same column
            if ((q[i] - q[n]) == (n - i)) return false;   // same major diagonal
            if ((q[n] - q[i]) == (n - i)) return false;   // same minor diagonal
        }
        return true;
    }

   /***************************************************************************
    * Print out N-by-N placement of queens from permutation q in ASCII.
    ***************************************************************************/
    public static void printQueens(int[] q) {
        int N = q.length;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (q[i] == j) StdOut.print("Q ");
                else           StdOut.print("* ");
            }
            StdOut.println();
        }  
        StdOut.println();
    }


   /***************************************************************************
    *  Try all permutations using backtracking
    ***************************************************************************/
    public static void enumerate(int N) {
        int[] a = new int[N];
        enumerate(a, 0);
    }

    public static void enumerate(int[] q, int n) {
        int N = q.length;
        if (n == N) printQueens(q);
        else {
            for (int i = 0; i < N; i++) {
                q[n] = i;
                if (isConsistent(q, n)) enumerate(q, n+1);
            }
        }
    }  


    public static void main(String[] args) {
        int N = Integer.parseInt(args[0]);
        enumerate(N);
    }

}

【问题讨论】:

    标签: java recursion n-queens


    【解决方案1】:

    重要的部分是enumerate 方法。它保存一个整数数组,表示每行的列索引。

    例如:

    * Q * * 
    * * * Q 
    Q * * * 
    * * Q * 
    

    q 看起来像:

    q[0] = 1
    q[1] = 3
    q[2] = 0
    q[3] = 2
    

    它确实是递归的,但不仅仅是一种解决方案。它将使用回溯而不是重新计算所有内容。

    例如对于N=4,代码将等效于以下运行:

    public static void enumerate(int[] q, int n) {
        for(int a0=0;a0<4;a0++){
            q[0]=a0;
            if(isConsistent(q, 0)){
                for(int a1=0;a1<4;a1++){
                    q[1]=a1;
                    if(isConsistent(q, 1)){
                        for(int a2=0;a2<4;a2++){
                            q[2]=a2;
                            if(isConsistent(q, 2)){
                                for(int a3=0;a3<4;a3++){
                                    q[3]=a3;
                                    if(isConsistent(q, 3)){
                                        printQueens(q);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    

    它将尽可能地覆盖整个解决方案空间。这意味着它将从无法进一步解决行的那一刻开始中断,但它将继续到仍然有可能走得更远的地方。

    isConsistent 方法确实在检查当前板是否仍然有效。 (对角线/列,行隐含在递归中)

    当前板在您遇到第 N 行的那一刻打印出来。因为你有一个仍然有效的棋盘,每行都有一个皇后。

    查看this simulation 以查看算法执行的步骤。

    【讨论】:

    • 那么,你是说它计算一个解,然后回溯。并且由于它是回溯,它会计算第二个解决方案?
    • 是的,如果第一行的皇后组合是有效的。我们不会重新计算,而是继续我们原来的位置。在给出的示例中,我们将在使用 q[0]=1;q[1]=3;q[2]=0;q[3]=3; 打印解决方案后继续
    • 我会继续努力,但我想我快到了。现在,我可以看到它正在计算棋盘,当它退出递归时,它正在计算第二个棋盘(这只是从 q[0] 开始的第一个解决方案) .我还没有说服自己,但这就是我在锻炼的原因!感谢您的详尽解释。
    • 但是您知道它使用相同的数组来计算所有解决方案吗?而且您永远不会进入前 2 个索引相同或仅相差 1 个(对角线)的第 3 行?
    猜你喜欢
    • 1970-01-01
    • 2017-05-05
    • 2017-07-13
    • 1970-01-01
    • 1970-01-01
    • 2021-02-25
    • 2011-03-23
    • 1970-01-01
    • 2012-12-12
    相关资源
    最近更新 更多