【发布时间】:2019-09-20 03:51:37
【问题描述】:
我想使用最大 N = 8 的递归解决 N x N 板中的 N Rooks 问题.我的代码适用于 N = 2, 3, 4, 5, 6,7。但是当 N = 8 它给出了很多可能的结果,从 1 0 0 0 0 0 0 0 的第一行开始然后给出 stackoverflow 错误,然后从 0 1 0 0 0 0 0 0 的第一行开始检查其他可能的结果。
我知道像 斐波那契数列、阶乘等一般递归,我可以追踪它们。然后我遇到了一种新的递归形式,称为回溯递归。然后我开始学习这种递归形式背后的逻辑,并阅读了一些伪代码算法。实际上,这种形式的递归在我看来比普通递归更难构建。
public class NRooks {
/**
* In this code r = which row, c = which column.
* lastY method just returns column c of last placed rook in
* a given row r in order to remove it.
* row.length, col.length, board.length have no special meaning. They all
* equal to the dimension of board N.
* main() method always initiates first row(r = 0). Therefore in main()
* method r remains 0 and c changes as you can see in putRook(0, i).
* So solve() method always begins from second row(r = 1).
*/
private static int found = 0;
private static int[][] board;
private static int[] row;
private static int[] col;
public static void putRook(int r, int c) {
board[r][c] = 1;
row[r] = 1;
col[c] = 1;
}
public static void removeRook(int r, int c) {
board[r][c] = 0;
row[r] = 0;
col[c] = 0;
}
public static boolean isValid(int r, int c) {
if (row[r] == 0 && col[c] == 0) return true;
return false;
}
public static void showBoard() {
for (int r = 0; r < board.length; r++) {
for (int c = 0; c < board.length; c++) {
System.out.print(board[r][c] + " ");
}
System.out.println();
}
System.out.println();
}
public static int lastY(int r) {
for (int j = 0; j < board.length; j++) {
if (board[r][j] == 1) return j;
}
return -1;
}
public static boolean solve(int r, int c) {
int last;
if (r == 0) return false;
if (r == col.length) {
found++;
/**
* When I dont include below printline statement my code
* works fine until N = 7 then gives SO error.
* But When I include this print statement in order
* to print number of results my code works fine until
* N = 6 then gives SO error
*/
//System.out.println("Found: " + found);
showBoard();
r--;
last = lastY(r);
removeRook(r, last);
c = last + 1;
}
for (int j = c; j < row.length; j++) {
if (isValid(r, j)) {
putRook(r, j);
return solve(r + 1, 0);
}
}
last = lastY(r - 1);
removeRook(r - 1, last);
return solve(r - 1, last + 1);
}
public static void main(String[] args) {
int n = Integer.parseInt(args[0]);
board = new int[n][n];
row = new int[n];
col = new int[n];
for (int i = 0; i < row.length; i++) {
boolean finished; // not important
putRook(0, i);
finished = solve(1, 0);
if (finished) System.out.println("============"); // ignore this too
}
}
}
Stackoverflow 指向包含对 solve() 方法的递归调用的行。
注意:我只知道 C 之类的 java 语法和基本数据抽象。我用我的这个级别的 Java 编写了这段代码。
我想自己解决这个问题和 N 个皇后问题。
因为这些问题有很多解决方案,无论是数学上还是算法上。而且我现在对高级 Java 数据抽象 东西不感兴趣。
我只想要一些关于我的代码 sn-p 的建议,例如
- 您的回溯算法效率不高。 (很直接)
- 您需要使用一些 Java 数据抽象的东西来有效地解决这个问题。
- 您需要使用另一种形式的递归,例如 尾递归(我也听说过。)
- ....
【问题讨论】:
-
暂时离开“运行代码”,拿一张纸和一支笔,然后看看你的
r在运行这个函数时会发生什么。 非常具体r == col.length时会发生什么。r可以承担什么价值?这对那个终端条件意味着什么?它真的正确吗? -
我怀疑你的逻辑有缺陷。
N=8不应该太大而导致溢出,因为如果实施正确,您应该只递归 8 次调用。阅读ericlippert.com/2014/03/05/how-to-debug-small-programs 了解调试代码以查找错误的提示。 -
您的
for循环在我看来很可疑。递归解决方案不需要任何 for 循环。我也同意 @Mike'Pomax'Kamermans 的观点,即您应该远离代码并手动解决问题。 -
问题:这段代码的目的是什么?您想找到问题的一个解决方案吗?还是您想找到所有解决方案?
-
代码完成后,我很乐意试一试。正如@Code-Apprentice 已经指出的那样,没有任何理由比 8 次调用更深:到那时,您会击中棋盘的另一边。通过手动和主要调试工具跟踪调用序列,跟踪输出。还要记录您的程序,以便我们了解您认为您在每个区块中所做的事情。
标签: java algorithm recursion backtracking