简体   繁体   English

C ++中的递归回溯

[英]Recursive Backtracking in c++

I am trying to write a program that will use backtracking to create a Sudoku solver. 我正在尝试编写一个程序,该程序将使用回溯来创建Sudoku求解器。 I have been able to create a black Sudoku grid and I can check to see if a move is a valid move. 我已经能够创建一个黑色的Sudoku网格,并且可以检查某个移动是否有效。 My program works fine until there are more than one choice of numbers for a square. 我的程序运行良好,直到一个正方形有多个数字选择为止。

Problem: Will you look at my Solve method and see how I could modify it to backtrack, change the answer and move forward again. 问题:您能否看一下我的求解方法,看看如何将其修改为后退,更改答案并再次前进。 I gave the names of all of my other methods above and every one of those work. 我在上面给出了我所有其他方法的名称,以及所有这些方法的名称。

Example input: 输入示例:

int board[ROWS][COLS] = {
    { 6, 0, 3, 0, 2, 0, 0, 9, 0 },
    { 0, 0, 0, 0, 5, 0, 0, 8, 0 },
    { 0, 2, 0, 4, 0, 7, 0, 0, 1 },
    { 0, 0, 6, 0, 1, 4, 3, 0, 0 },
    { 0, 0, 0, 0, 8, 0, 0, 5, 6 },
    { 0, 4, 0, 6, 0, 3, 2, 0, 0 },
    { 8, 0, 0, 2, 0, 0, 0, 0, 7 },
    { 0, 1, 0, 0, 7, 5, 8, 0, 0 },
    { 0, 3, 0, 0, 0, 6, 1, 0, 5 }
};

bool sudokuBoard::emptyCell(int i, int j);

bool sudokuBoard::isValidCol(int i, int j, int number);

bool sudokuBoard::isValidRow(int i, int j, int number);

bool sudokuBoard::isValidSquare(int i, int j, int number);

bool sudokuBoard::validMove(int i, int j, int number);

void sudokuBoard::solvePuzzle(int row, int col) {

    for (int i = 1; i < 10; i++) {
        if (validMove(row, col, i)) {
            board[row][col] = i;
            showBoard();
        } 
    }
    if (row < 8 && col < 8) {
        if (col < 8) {
            solvePuzzle(row, col + 1);
        }
        else {
            col = 0;
            solvePuzzle(row + 1, col);
        }
    }
}

Example current output: 电流输出示例:

  6  5  3|  1  2  8|  4  9  0|
  0  0  0|  0  5  0|  0  8  0|
  0  2  0|  4  0  7|  0  0  1|
--------------------------------
  0  0  6|  0  1  4|  3  0  0|
  0  0  0|  0  8  0|  0  5  6|
  0  4  0|  6  0  3|  2  0  0|
--------------------------------
  8  0  0|  2  0  0|  0  0  7|
  0  1  0|  0  7  5|  8  0  0|
  0  3  0|  0  0  6|  1  0  5|

my program stops at the last 0 of the first row since there is no solution unless that previous 4 changes to a 7, the program terminates. 我的程序在第一行的最后一个0处停止,因为没有解决方案,除非前4个变为7,所以程序终止。

Backtracking can be hard to wrap your mind around the first time so we will take this step by step starting with some pseudocode of about what you have now : 回溯可能很难让您全神贯注,因此我们将逐步介绍有关您现在所拥有的伪代码:

    while(puzzlenotsolved)
   {
    foreach row   
      {
         findEmptySquare
         {
         findValidMove(1-9)
         }
      }
   }

This of course gets stuck once no valid move can be found for a square because of a previously chosen value. 一旦由于先前选择的值而无法为正方形找到有效移动,这当然会陷入困境。

To counter this we need to return false when we run out of valid moves in a square, we also need to unassign our guess to make the square empty again. 为了解决这个问题,我们需要在正方形中的有效移动用尽时返回false ,还需要取消分配猜测值以使正方形再次为空。 We then need to resume looping in the previous square where we left off. 然后,我们需要在我们停下的上一个方块中继续循环播放。

So our find valid move function (Solve puzzle in your case) could look something like this : 因此,我们找到的有效移动功能(在您的情况下,解决难题)可能看起来像这样:

bool findValidMove
{
  if(noEmptySquare) {return true;}  //important bit

  findEmptySquare()
  for (1-9) 
       { if (isvalidMove )
            {
                assignMoveToSquare
            }
            if (findValidMove) {return true}  //important bit

         unassignMoveFromSquare   
       }
    return false; //no values valid in this square, a previous square has a wrong value
}

Now this is considered a brute force approach, and can be optimized, but for your question lets get backtracking working and you can worry about speed optimizations later if you wish. 现在,这被认为是蛮力方法,并且可以进行优化,但是对于您的问题,让回溯工作正常进行,您以后可以担心速度优化的问题。

Note the two places I commented as important bits, the first is a signifier that there are no empty squares left. 注意我评论过的两个重要的地方,第一个是表示没有空方的符号。 Since your program only assigns valid moves the puzzle should be complete and correct here, so the program returns true. 由于您的程序仅分配有效动作,因此此处的拼图应完整且正确,因此程序返回true。 This is the base case, In general recursive functions need a base case. 这是基本情况,通常递归函数需要一个基本情况。

The second important bit is where the function recursively calls itself. 第二个重要的位是函数递归调用自身的位置。 Take note that it is still within the loop, so when a call returns false, it will resume looping in a previous call. 请注意,它仍在循环中,因此当调用返回false时,它将在上一个调用中恢复循环。 Each call stacks onto the other like in this example except our example returns back into a loop. 除了本示例返回到循环外,每个调用都像本示例中一样堆叠到另一个上。

Notice that the cell does not get unassigned until after the recursive function returns, this allows you to not worry about adding 1 to your rows and columns as you mentioned in your comment. 请注意,直到递归函数返回后,单元格才会被分配,这使您不必担心在注释中提到的行和列加1。 All you have to do is have a reliable findEmptySquare method and recursion takes care of the rest. 您所要做的就是拥有一个可靠的findEmptySquare方法,然后递归处理其余的工作。

Your showBoard(); 您的showBoard(); method will be invaluable for debugging, i'd say put it right after assignMoveToSquare 方法对于调试将是无价的,我会说在assignMoveToSquare之后将其正确assignMoveToSquare

Hopefully this helps, you are really close so I think it will. 希望这会有所帮助,您真的很亲近,我认为会的。 If you have further questions feel free to comment on this and I'll try to get to you when I have time. 如果您还有其他问题,请随时对此发表评论,如果有时间,我会尽力与您联系。

This is what solved it for me. 这就是为我解决的问题。 Thank you for all your help. 谢谢你的帮助。

bool sudokuBoard::solvePuzzle() {
    int row, col;

    if (emptyCell(row, col) == false) {
        return true; 
    }

    for (int i = 1; i < 10; i++) {
        cout << "Trying " << i << " in spot [" << row << "][" << col << "]" << endl;
        if (validMove(row, col, i)) {
            board[row][col] = i;
            showBoard();
            if (solvePuzzle()) {
                return true;
            }
            board[row][col] = 0;
        }
    }
    return false;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM