【问题标题】:Sudoku recursive backtracking, unrecursing too early数独递归回溯,过早取消递归
【发布时间】:2012-10-24 07:51:45
【问题描述】:

所以我正在用 C++ 编写一个数独求解器,但遇到了一个小问题。下面是我的解决板代码。它适用于拼图的前 3 行,但在到达第 4 行末尾时不再递归。查看 gdb 上的代码,它到达第 4 行的末尾,回溯到第 6 列,尝试然后取消递归到末尾。

关于代码的其他一些注释是保持数独板从 1,1 而不是 0,0 开始的矩阵。因此,最初调用solveBoard 时,参数为(1, 1, 0)。我还附加了 setCell 和 checkConflicts 函数,以便更深入地了解那里。我有三个向量 rowConf、colConf 和 squConf 来存储已经放置在相应行、列或正方形中的值。我已经在这里待了几个小时,无法让它越过第三排。非常感谢任何帮助。谢谢!

编辑:添加 clearCell()

bool board::solveBoard(int i, int j, int count)
{

    if (j > 9)
    {
        j = 1;
        i++;

        printBoard();
        if (isSolved())
        {
            printBoard();
            cout <<"The Board has been solved!" <<endl
                 <<" The number of recursive calls was: " <<count <<endl;
            return true;
        }
     }

     if (isBlank(i, j))
     {
         for (int n = 1; n < 10; n++)
         {
             if (setCell(i, j, (char)n + '0'))
             {
                 if (solveBoard(i, j + 1, count + 1))
                 {
                     return true;
                 }
              }
          }
      }
      else
      {
          return (solveBoard(i, j + 1, count + 1));
      }

      clearCell(i, j);
      return false;
}

bool board::setCell(int i, int j, char val)
{
    int intVal;

    intVal = atoi(&val);

    if (i >= 1 && i <= BoardSize && j >= 1 && j <= BoardSize &&
        intVal >= 1 && intVal <= BoardSize)
    {
        if (!(checkConflicts(intVal, i, j, squareNumber(i, j))))
        {
        return false;
        }

        value[i][j] = intVal;

        // Set flags of the conflicts
        rowConf[i][intVal] = true;
        colConf[j][intVal] = true;
        squConf[squareNumber(i, j)][intVal] = true;

        return true;
    }
    else
    {
        throw rangeError("bad value in setCell");
    }
}

bool board::checkConflicts(int val, int i, int j, int k)
{
    if (i < 1 && i > BoardSize && j < 1 && j > BoardSize &&
        k < 1 && k > BoardSize && val < 1 && val > BoardSize)
    {
        throw rangeError("bad value in checkConflicts()");
    }

    if (rowConf[i][val] || colConf[j][val] || squConf[k][val])
    {
        return false;
    }
    else
    {
        return true;
    }
}

Initial Board:
 -----------------------------
| 3       |    8    |          -----------------------------
|         | 7       |       5  -----------------------------
| 1       |         |          ----------------------------- 
 -----------------------------
|         |         | 3  6     -----------------------------
|       2 |       4 |          -----------------------------
|    7    |         |          -----------------------------
 -----------------------------
|         |    6    | 1  3     -----------------------------
|    4  5 | 2       |          -----------------------------
|         |         | 8        -----------------------------
 -----------------------------

Final Output:
 -----------------------------
| 3  2  4 | 1  8  5 | 6  7  9  -----------------------------
| 6  8  9 | 7  2  3 | 4  1  5  -----------------------------
| 1  5  7 | 4  9  6 | 2  8  3  -----------------------------
 -----------------------------
|         |         | 3  6     -----------------------------
|       2 |       4 |          -----------------------------
|    7    |         |          -----------------------------
 -----------------------------
|         |    6    | 1  3     -----------------------------
|    4  5 | 2       |          -----------------------------
|         |         | 8        -----------------------------
 -----------------------------

void board::clearCell(int i, int j)
{
    int intVal;

    if (i >= 1 && i <= BoardSize && j >= 1 && j <= BoardSize)
    {
        if (value[i][j] != -1)
        {
            intVal = value[i][j];
            rowConf[i][intVal] = false;
            colConf[j][intVal] = false;
            squConf[squareNumber(i, j)][intVal] = false;
            value[i][j] = -1;
         }
    }
    else
    {
        throw rangeError("bad value in setCell");
    }
}

【问题讨论】:

  • 输出板子解决了吗?这将是递归结束的一种可能性。
  • 另外,请提供clearCell函数。
  • 我添加了透明单元格文档。它永远不会被宣布解决。发生的事情是,由于某种原因,由于没有可用的移动可放置,因此它不会递归回到开始尝试新方法,但不是移动到第一个空白单元格的 for 循环的下一次迭代,而是希望一个递归和结束。
  • 是的,它最终会回到 i = 1 和 j = 2 时。当它尝试放置一个值以尝试新的跟踪时,该行的 rowConf 表示除了 9 之外的所有值那里。所以它放置 9,移动到 1,3 并且不能再放置选择,因为代码认为没有有效的选择。由于某种原因,它没有将该行的 rowConf 设置回 false。
  • 好吧,我的 clearCell 功能肯定是有问题的。它没有正确删除我的冲突向量中的标志。

标签: c++ recursion sudoku backtracking


【解决方案1】:

你的问题很可能在这里:

if (isBlank(i, j))
 {
     for (int n = 1; n < 10; n++)
     {
         if (setCell(i, j, (char)n + '0'))
         {
             if (solveBoard(i, j + 1, count + 1))
             {
                 return true;
             }
          }
      }
  }

不知何故,它正在通过这部分,这就是为什么它最终没有通过else,但由于它之前没有返回,所以它卡住了。

这需要更多的调试,但这里有一个可以带来解决方案的想法:

if (isBlank(i, j))
{
    for (int n = 1; n < 10; n++)
    {
        if (setCell(i, j, (char)n + '0'))
        {
            if (solveBoard(i, j + 1, count + 1))
            {
                return true;
            } else {
                echo 'Looks like it ended on the farthest-level..';
          } 
      } else {
          echo 'Looks like it ended on the second-farthest level.';
      }
  }

【讨论】:

  • 为什么会有多个else 子句?此外,您的else 基本上说:"If it didn't work the first time, let's try that once more"。那是行不通的。
  • @phant0m,谢谢,编辑。对于每个没有运行的“如果”,它只是另一个。无论如何,我的观点是,看起来这是它退出 OP 代码的地方,OP 应该找到一种方法来跟踪它。
【解决方案2】:

atoi 函数需要一个 string 作为参数,即以字符 '\0' 结尾的字符数组,ASCII NUL。您将参数作为指向字符的指针(相当于一些字符数组),但不保证它是以零结尾的。请将intVal = atoi(&amp;val);替换为intVal = (int)val - '0';

您的checkConflicts 应该在第一个if 中使用|| 运算符而不是&amp;&amp;

这些可能不是错误的原因,但肯定需要更正。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多