【问题标题】:Empty Sudoku Filler C++空数独填充 C++
【发布时间】:2016-08-09 16:48:10
【问题描述】:

我正在开发一个程序,该程序使用数独板的正确值填充一个空的 9x9 数组。我的验证方法有效,因为我已经在一个简单的迭代回溯方法中对其进行了测试。 我目前的做法是随机选择一行和一列并放置一个适当的值,我也在通过使用递归来实现回溯。这是我的求解器函数:

void solve (int board[9][9]) {

    static int counter = 0;
    int val = 0;
    int row = 0;
    int col = 0;

    if (counter == 81) {
        print (board, counter);
        exit(1);
    }

    while (1) {
        row = rand() % 9;
        col = rand() % 9;
        if (board[row][col] == 0)
            break;
    }

    ++counter;
    for (int i = 1; i < 10; i++) {
        val = i;
        if (ok (board, row, col, val)) {
            board[row][col] = val;
            solve (board);
        }
    }
    --counter;
}

现在我遇到的问题是我从来没有达到 81,我的函数在此之前终止,我假设堆栈变空并返回到 main。你能帮我理解我犯了什么错误吗?谢谢你。

【问题讨论】:

  • @user4581301 谢谢你,就像一个白痴一样,每次插入值时我都会打印出板子,并用它来查看问题所在。我现在一定要使用它。
  • This 可能会有所帮助。我做到了。
  • void solve (int board[9][9]) { 在技术上类似于solve(int **),因此每次调用都会给出指针,每个solve 在同一块板上工作,因此您必须自己清理它(就像 Nelxost 指出的那样)。在对象编程中,一种可能的设计是创建板作为求解器类的成员变量,求解器在其上运行(不是每次都用相同的指针填充堆栈)。代码风格的更多注释:for (int i = 1; i &lt; 10; i++) - 我会使用i &lt;= 9 来遵循人类逻辑:“尝试从 1 到 9 的数字”。将 i 重命名为 val 并删除 val(使用较少的堆栈)。
  • @Ped7g 感谢您对清理代码的建议,非常感谢。嗯,所以 board 作为成员变量背后的想法是,由于该类只会被实例化一次,因此对该类的成员的每次调用只会发生一次,而不是多次?

标签: c++


【解决方案1】:

当您尝试使用适当的值填充数独网格时,您最终会陷入死锁:在不违反数独规则的情况下,您尝试为其查找值的单元格无法填写的情况。举个简单的例子,考虑一个网格,其中第一行是[x, 2, 3, 4, 5, 6, 7, 8, 9],第一列是[x, 1, 8, 2, 3, 4, 5, 6, 7]。 x 不能填写,但其他值都符合规则。

在您的情况下,当发生死锁时,ok 条件不满足从 1 到 9 的任何值,并且执行将返回一层递归,到上一个单元格,其中值已经被选中。在那里,函数尝试下一个可能的值。

在一个已经有值的单元格发生死锁(即多级死锁)的情况下,执行会再次返回一层递归,但值仍然存在。由于这些“幽灵”值,这会导致后续的ok 条件在它们应该成功时失败。简单的死锁不会出现该问题,因为该值保持为 0。

for循环后,需要将board[row][col]的值设置回0。

【讨论】:

  • 嗯,这很有趣,在您介绍的情况下,您只需返回上一个已填充的框并增加其值,直到找到正确的数字,对吗?我以为既然我使用的是递归,一旦函数终止,它会回到之前的递归调用并从那里开始,所以它会自动回溯,我理解错了吗?
  • @SyedSharukChowdhury 我的错,我完全错过了这个问题。已编辑。
  • @SyedSharukChowdhury 维基百科entry on backtracking 以数独为例,我认为它提供了一些伪代码。没有。但它确实链接到sudoku solver entry。这是维基百科,因此可以对其进行编辑以包含 batsmurph 幻想,但仍然值得一看。
  • @Ped7g 嗯,这是一个有趣的想法,谢谢。我将如何在空板上进行此操作,因为第一个框为 0,然后它将逐列、逐行地进行。
  • @SyedSharukChowdhury 首先,由于您选择随机单元格的方式,选择特定订单将比您现在执行的操作更快(尽管我猜不是很多)。除此之外,您可以做的一件事是为每个单元格保留更新的可能值列表。如果列表变为空,那么您已经知道最终会陷入死锁并且需要回溯。这也使得选择一个值更快,因为您不需要验证它:单元格列表中的每个值都应该是有效的。此外,您可以搜索性能更好的替代解决方法。
猜你喜欢
  • 2017-11-16
  • 1970-01-01
  • 2020-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-17
  • 1970-01-01
  • 2020-09-11
相关资源
最近更新 更多