【问题标题】:When is recursive backtracking appropriate?什么时候递归回溯合适?
【发布时间】:2017-07-03 22:08:16
【问题描述】:

我正在为一个班级制作 SudokuSolver,但我在使用 solve 方法时遇到了问题。我目前的解决方案使用递归回溯(我认为)。

作业要求

int solve() -- 尝试使用上述策略解决难题。返回解决方案的数量。

(上述策略)

在为点分配数字时,切勿分配在那个时刻与点的行、列或正方形发生冲突的数字。我们预先谨慎地将合法数字分配给一个点,而不是分配任何数字 1..9 并稍后在递归中找到问题。假设初始网格都是合法的,之后只进行合法的点分配。

伪代码思路

我可以迭代地遵循这个小输入。例如,假设我必须解决未解决的单元格 #1 和单元格 #2。 #1 有可能性 {1, 3 },#2 有可能性 {2, 3}。那么我会

set 1 to 1
    set 2 to 2
        hasConflicts? 0 : 1
    set 2 to 3
        hasConflicts? 0 : 1
set 1 to 3
    set 2 to 2
        hasConflicts? 0 : 1
    set 2 to 3
        hasConflicts? 0 : 1

实际代码

public int solve() {
    long startTime = System.currentTimeMillis();
    int result = 0;

    if (!hasConflicts()) {
        Queue<VariableCell> unsolved = getUnsolved();
        reduceUnsolvedPossibilities(unsolved);  // Gets the possibilities down from all of 1-9

        if (!hasConflicts()) {
            result = solveRec(unsolved);
        }
    }

    mElapsedTime = System.currentTimeMillis() - startTime;
    return result;
}

protected int solveRec(Queue<VariableCell> unsolved) {
    if (unsolved.isEmpty()) {
        return (hasConflicts()) ? 0 : 1;
    }

    int result = 0;
    VariableCell cell = unsolved.remove();
    Iterator<String> possibilityIt = cell.getPossibilities().iterator();

    while (possibilityIt.hasNext()) {
        cell.setSymbol(possibilityIt.next());

        if (hasConflicts()) {
            possibilityIt.remove();
        } else {
            ++result;
        }
    }

    return result + solveRec(unsolved);
}

测试结果

testSolveSingleSolution
    expected 1, actual 1

testSolveSolved
    expected 1, actual 1

testSolveUnsolvable
    expected 0, actual 0

testSolveMultiSolutions
    expected 2, actual 7  // MAJOR PROBLEM!

递归回溯的一些很好的解释

问题

我之前做过递归回溯,我已经查看了上面的所有链接以及更多内容,但我仍然遇到问题。我认为问题在于我在思考如何解决这个问题。 (请参阅伪代码理念。)使用递归回溯进行穷举搜索是否合适?回溯是正确的,但执行是错误的吗?有没有比递归回溯更好的算法?

【问题讨论】:

  • 如果您可以修剪搜索,回溯是一种合理的方法。
  • @nikhil 是否需要在递归期间进行修剪?
  • 它是不可分割的一部分,但它唯一能做的就是帮助提高性能(在最坏的情况下,你基本上必须搜索整棵树才能找到正确的解决方案;与你不这样做的对抗游戏相反不一定需要“最佳”可能的移动,只需要在给定时间段内可以找到的最佳移动)。只允许有效的移动已经是一种修剪。无论如何,如果你得到错误的结果,那不是原因(除非你有错误)。 Possible algorithms to solve it.
  • 续。用exact cover 解决它很有趣,而且你学到的东西比暴力递归解决方案更多,所以你可能想研究一下——它还可以立即解决任何类型的 9x9 难题(也就是说,即使是最坏的数独也可以解决秒用写得很好的蛮力方法)。
  • @Eva Nice 链接 - 虽然集合论解释如果非常简洁和准确,则具有优势,但我可以理解为什么有些人更喜欢更广泛的描述。写那篇文章的人很好地解释了这个问题。很高兴听到您有一个可行的解决方案,同时学到了一个非常有用的新概念。

标签: java recursion backtracking


【解决方案1】:

这看起来像是一个实现问题。检查要增加结果的块。您真的要为该单元格的每个有效值递增吗?如果有多个有效值,则覆盖旧的有效值?

【讨论】:

  • 我明白你关于增加值的意思。它增加了太多,只有在找到有效的解决方案后才应该这样做。我不确定您所说的不覆盖旧值是什么意思。在继续下一个值之前,我正在尝试检查具有该值的有效解决方案。我想我也可能做错了。
  • 仔细查看 while (possibilityIt.hasNext()) 循环,特别注意设置符号的位置与进行递归 solveRec 调用的位置相比。
猜你喜欢
  • 2013-01-29
  • 1970-01-01
  • 2021-12-28
  • 1970-01-01
  • 2011-09-13
  • 2021-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多