C++ C++;使用递归回溯的数独解算器不起作用

C++ C++;使用递归回溯的数独解算器不起作用,c++,sudoku,recursive-backtracking,C++,Sudoku,Recursive Backtracking,我正在尝试用递归和回溯来编写一个数独解算器。但是我的代码总是返回false。SudokuSolver()只遍历第一行到第四列,然后停止并开始回溯。问题是回溯一直持续到第一个单元格,最后返回false。因此,没有任何空单元(值为“-1”的单元)被任何其他数字(1-9)替换,电路板保持原样 编译器显示[Done]在0.557秒内退出,代码=0 #include<iostream> using namespace std; # define N 9 bool SolveSudoku();

我正在尝试用递归和回溯来编写一个数独解算器。但是我的代码总是返回false。SudokuSolver()只遍历第一行到第四列,然后停止并开始回溯。问题是回溯一直持续到第一个单元格,最后返回false。因此,没有任何空单元(值为“-1”的单元)被任何其他数字(1-9)替换,电路板保持原样

编译器显示[Done]在0.557秒内退出,代码=0

#include<iostream>
using namespace std;
# define N 9

bool SolveSudoku();
pair<int,int> FindUnassigned();
void PrintBoard();
bool NoConflict(int, int, int);
bool inRow(int,int);
bool inCol(int, int);
bool inGrid(int, int, int);
bool isFilled(pair<int,int>);

//board[row][col]==-1 indicates an empty position

int board[N][N] = {{3,-1,6,5,-1,8,4,-1,-1},
                   {5,2,-1,-1,-1,-1,-1,-1,-1},
                   {-1,5,7,-1,-1,-1,-1,3,1},
                   {-1,-1,3,-1,1,-1,-1,8,-1},
                   {9,-1,-1,8,6,3,-1,-1,5},
                   {-1,5,-1,-1,9,-1,6,-1,-1},
                   {1,3,-1,-1,-1,-1,2,5,-1},
                   {-1,-1,-1,-1,-1,-1,-1,7,4},
                   {-1,-1,5,2,-1,6,3,-1,-1}};


int main() {
    if(SolveSudoku()) 
        PrintBoard();
    return 0;
}


bool SolveSudoku() {
    pair<int,int> pos = FindUnassigned();
    int row=pos.first;
    int col=pos.second;
    if(isFilled(pos)) { return true; }
      for(int num=1; num<=9; num++) {
        if(NoConflict(num,row,col)) {
            board[row][col]=num;
            SolveSudoku();
            board[row][col]=-1;
        }  
      }
    return false;
}

pair<int,int> FindUnassigned() {
    pair<int,int> pos;
    pos.first=-1;
    pos.second=-1;
    for(int r=0; r<N; r++) {
        for(int c=0; c<N; c++) {
            if(board[r][c]==-1) {
                pos.first=r;
                pos.second=c;
                return pos;
            } 
        }
    }
    return pos;
}

bool isFilled(pair<int,int> pos) {
    return (pos.first==-1 && pos.second==-1);
}

bool NoConflict(int num, int r, int c) {
    return ((!inRow(numenter code here,r)==false) && (!inCol(num,c)==false) && (!inGrid(num,r-r%3,c-c%3)==false));
}

bool inRow(int num, int r) {
    for(int c=0; c<N; c++) {
        if(board[r][c]==num) return true;
    }
    return false;
}

bool inCol(int num, int c) {
    for(int r=0; r<N; r++) {
        if(board[r][c]==num) return true;
    }
    return false;
}

bool inGrid(int num, int rf, int cf) {
    for(int r=0; r<3; r++) {
        for(int c=0; c<3; c++) {
            if(board[r+rf][c+cf]==num) return true;
        }
    }
    return false;
}

void PrintBoard() {
    for(int r=0; r<N; r++) {
        for(int c=0; c<N; c++) {
            cout<<board[r][c]<<"\t";
        }
 enter code here       cout<<endl;
    }
}
#包括
使用名称空间std;
#定义n9
布尔数独();
配对findunsigned();
作废印刷板();
布尔无冲突(int,int,int);
bool-inRow(int,int);
bool inCol(int,int);
布尔·英格里德(int,int,int);
bool-isFilled(对);
//线路板[row][col]=-1表示空位置
int板[N][N]={{3,-1,6,5,-1,8,4,-1,-1},
{5,2,-1,-1,-1,-1,-1,-1,-1},
{-1,5,7,-1,-1,-1,-1,3,1},
{-1,-1,3,-1,1,-1,-1,8,-1},
{9,-1,-1,8,6,3,-1,-1,5},
{-1,5,-1,-1,9,-1,6,-1,-1},
{1,3,-1,-1,-1,-1,2,5,-1},
{-1,-1,-1,-1,-1,-1,-1,7,4},
{-1,-1,5,2,-1,6,3,-1,-1}};
int main(){
if(数独())
印制板();
返回0;
}
布尔数独{
pair pos=findunsigned();
int row=位置第一;
int col=位置秒;
if(isFilled(pos)){return true;}

for(int num=1;num递归函数的工作方式与非递归函数完全相同。
(这是理解递归最重要的一点。)

在这里:

您忽略函数调用的结果,因此您将递归,直到填充电路板,然后完全回溯,最后
返回false;

您可能想要的是(完全未经测试):

NoConflict
中还有一个bug,相当于

bool NoConflict(int num, int r, int c) {
    return inRow(num,r) && inCol(num,c) && inGrid(num,r-r%3,c-c%3);
}
也就是说,当且仅当
num
已在行、列和网格中时,才不存在冲突

您可能是说
num
不应该在任何地方找到:

bool NoConflict(int num, int r, int c) {
    return !inRow(num,r) && !inCol(num,c) && !inGrid(num,r-r%3,c-c%3);
}
但是错误地添加了另一个负数

可能还有更多的bug。

  • SolveSudoku()需要迭代,直到访问了电路板中的所有“单元”

  • NoConflict()看起来很奇怪,所以为了清晰起见,我将支票拆分

  • 您的示例板在[2][1]处存在冲突值,导致提前终止

我添加了一个验证例程,以确保在尝试解决问题之前有一个有效的电路板。 以下内容似乎提供了数独游戏的解决方案:

#include <iostream>
#include <cstdio>
using namespace std;
# define N 9

bool ValidateSudoku();
bool SolveSudoku();
bool FindUnassigned(pair<int,int>& pos );
void PrintBoard();
bool NoConflict(int, int, int);
bool inRow(int,int);
bool inCol(int, int);
bool inGrid(int, int, int);
bool isFilled(pair<int,int>);

int board[N][N] = {
  { 3,-1, 6,  5,-1, 8,  4,-1,-1},
  { 5, 2,-1, -1,-1,-1, -1,-1,-1},
  {-1, 5 /*this is wrong*/, 7, -1,-1,-1, -1, 3, 1},

  {-1,-1, 3, -1, 1,-1, -1, 8,-1},
  { 9,-1,-1,  8, 6, 3, -1,-1, 5},
  {-1, 5,-1, -1, 9,-1,  6,-1,-1},

  { 1, 3,-1, -1,-1,-1,  2, 5,-1},
  {-1,-1,-1, -1,-1,-1, -1, 7, 4},
  {-1,-1, 5,  2,-1, 6,  3,-1,-1}
};

int main() {
    std::cout<<"Solve:"<<std::endl;
    PrintBoard();
    std::cout<<std::endl;

    if (ValidateSudoku()) {
      if (SolveSudoku()) {
        std::cout<<"Solution:"<<std::endl;
        PrintBoard();
      }
      else {
        std::cout<<"Failed to solve"<<std::endl;
      }
    }
    else {
      std::cout<<"Board is invalid"<<std::endl;
    }
    return 0;
}


bool ValidateSudoku() {
  for (int row=0;row<N; row++) {
    for (int col=0; col<N; col++) {
      int num = board[row][col];
      board[row][col] = -1;
      if (num != -1) {
        if (inRow(num, row)) {
          return false;
        }

        if (inCol(num, col)) {
          return false;
        }

         if (inGrid(num, row-(row%3), col-(col%3))) {
          return false;
        }
      }
      board[row][col] = num;
    }
  }
  return true;
}

bool SolveSudoku() {
    pair<int,int> pos;
    while (FindUnassigned(pos)) {
      int row=pos.first;
      int col=pos.second;
      for(int num=1; num<=9; num++) {
        if(NoConflict(num,row,col)) {
            board[row][col]=num;
            if (SolveSudoku()) {
              return true;
            }

            board[row][col]=-1;
        }  
      }    
      return false;
    }
    return true;
}

bool FindUnassigned(pair<int,int>& pos ) {
    for(int r=0; r<N; r++) {
        for(int c=0; c<N; c++) {
            if(board[r][c]==-1) {
                pos.first=r;
                pos.second=c;
                return true;
            } 
        }
    }
    return false;
}

bool isFilled(pair<int,int> pos) {
    return (pos.first==-1 && pos.second==-1);
}

bool NoConflict(int num, int r, int c) {
    if (inRow(num, r)) {
      return false;
    }
    if (inCol(num, c)) {
      return false;
    }

    if (inGrid(num, r-(r%3), c-(c%3))) {
      return false;
    }
    return true;
    //I think this is buggy: return ((!inRow(num,r)==false) && (!inCol(num,c)==false) && (!inGrid(num,r-r%3,c-c%3)==false));
}

bool inRow(int num, int r) {
    for(int c=0; c<N; c++) {
        if(board[r][c]==num) return true;
    }
    return false;
}

bool inCol(int num, int c) {
    for(int r=0; r<N; r++) {
        if(board[r][c]==num) return true;
    }
    return false;
}

bool inGrid(int num, int rf, int cf) {
    for(int r=0; r<3; r++) {
        for(int c=0; c<3; c++) {
            if(board[r+rf][c+cf]==num) return true;
        }
    }
    return false;
}

void PrintBoard() {   
    for(int r=0; r<N; r++) {
        if (0 == (r % 3)) { std::cout<<std::endl; }
        for(int c=0; c<N; c++) {
           if (0 == (c % 3)) { std::cout<<" "; }
           std::cout.width(3);
           cout<<board[r][c]<<" ";
        }
        std::cout<<std::endl;
    }
}
#包括
#包括
使用名称空间std;
#定义n9
bool-ValidateSudoku();
布尔数独();
布尔Findun分配(配对和位置);
作废印刷板();
布尔无冲突(int,int,int);
bool-inRow(int,int);
bool inCol(int,int);
布尔·英格里德(int,int,int);
bool-isFilled(对);
集成电路板[N][N]={
{ 3,-1, 6,  5,-1, 8,  4,-1,-1},
{ 5, 2,-1, -1,-1,-1, -1,-1,-1},
{-1,5/*这是错误的*/,7,-1,-1,-1,3,1},
{-1,-1, 3, -1, 1,-1, -1, 8,-1},
{ 9,-1,-1,  8, 6, 3, -1,-1, 5},
{-1, 5,-1, -1, 9,-1,  6,-1,-1},
{ 1, 3,-1, -1,-1,-1,  2, 5,-1},
{-1,-1,-1, -1,-1,-1, -1, 7, 4},
{-1,-1, 5,  2,-1, 6,  3,-1,-1}
};
int main(){

std::coutMaybe可能只是我,但如果位置为空,您的isFilled()方法似乎会返回true,这对我来说听起来有点反吗?isFilled()接受一个位置(对),当pos=(-1,-1)
bool isFilled(对pos){return(pos.first=-1&&pos.second=-1);}
findunsigned()返回位置变量pos,该变量包含空单元格的位置(行,列)(单元格值=-1)。填充电路板时,将不会有任何空单元格(单元格值=-1),FindUnassigned将pos返回为(-1,-1)。我知道了,所以我想我是说函数的命名是“向后的”当位置为空时,isFilled返回true…感谢您的评论。但即使应用此选项,代码仍然返回false并退出,而不替换任何空单元格。非常感谢您花费大量时间和精力。我已经为此伤了脑筋一段时间了。
bool NoConflict(int num, int r, int c) {
    return !inRow(num,r) && !inCol(num,c) && !inGrid(num,r-r%3,c-c%3);
}
#include <iostream>
#include <cstdio>
using namespace std;
# define N 9

bool ValidateSudoku();
bool SolveSudoku();
bool FindUnassigned(pair<int,int>& pos );
void PrintBoard();
bool NoConflict(int, int, int);
bool inRow(int,int);
bool inCol(int, int);
bool inGrid(int, int, int);
bool isFilled(pair<int,int>);

int board[N][N] = {
  { 3,-1, 6,  5,-1, 8,  4,-1,-1},
  { 5, 2,-1, -1,-1,-1, -1,-1,-1},
  {-1, 5 /*this is wrong*/, 7, -1,-1,-1, -1, 3, 1},

  {-1,-1, 3, -1, 1,-1, -1, 8,-1},
  { 9,-1,-1,  8, 6, 3, -1,-1, 5},
  {-1, 5,-1, -1, 9,-1,  6,-1,-1},

  { 1, 3,-1, -1,-1,-1,  2, 5,-1},
  {-1,-1,-1, -1,-1,-1, -1, 7, 4},
  {-1,-1, 5,  2,-1, 6,  3,-1,-1}
};

int main() {
    std::cout<<"Solve:"<<std::endl;
    PrintBoard();
    std::cout<<std::endl;

    if (ValidateSudoku()) {
      if (SolveSudoku()) {
        std::cout<<"Solution:"<<std::endl;
        PrintBoard();
      }
      else {
        std::cout<<"Failed to solve"<<std::endl;
      }
    }
    else {
      std::cout<<"Board is invalid"<<std::endl;
    }
    return 0;
}


bool ValidateSudoku() {
  for (int row=0;row<N; row++) {
    for (int col=0; col<N; col++) {
      int num = board[row][col];
      board[row][col] = -1;
      if (num != -1) {
        if (inRow(num, row)) {
          return false;
        }

        if (inCol(num, col)) {
          return false;
        }

         if (inGrid(num, row-(row%3), col-(col%3))) {
          return false;
        }
      }
      board[row][col] = num;
    }
  }
  return true;
}

bool SolveSudoku() {
    pair<int,int> pos;
    while (FindUnassigned(pos)) {
      int row=pos.first;
      int col=pos.second;
      for(int num=1; num<=9; num++) {
        if(NoConflict(num,row,col)) {
            board[row][col]=num;
            if (SolveSudoku()) {
              return true;
            }

            board[row][col]=-1;
        }  
      }    
      return false;
    }
    return true;
}

bool FindUnassigned(pair<int,int>& pos ) {
    for(int r=0; r<N; r++) {
        for(int c=0; c<N; c++) {
            if(board[r][c]==-1) {
                pos.first=r;
                pos.second=c;
                return true;
            } 
        }
    }
    return false;
}

bool isFilled(pair<int,int> pos) {
    return (pos.first==-1 && pos.second==-1);
}

bool NoConflict(int num, int r, int c) {
    if (inRow(num, r)) {
      return false;
    }
    if (inCol(num, c)) {
      return false;
    }

    if (inGrid(num, r-(r%3), c-(c%3))) {
      return false;
    }
    return true;
    //I think this is buggy: return ((!inRow(num,r)==false) && (!inCol(num,c)==false) && (!inGrid(num,r-r%3,c-c%3)==false));
}

bool inRow(int num, int r) {
    for(int c=0; c<N; c++) {
        if(board[r][c]==num) return true;
    }
    return false;
}

bool inCol(int num, int c) {
    for(int r=0; r<N; r++) {
        if(board[r][c]==num) return true;
    }
    return false;
}

bool inGrid(int num, int rf, int cf) {
    for(int r=0; r<3; r++) {
        for(int c=0; c<3; c++) {
            if(board[r+rf][c+cf]==num) return true;
        }
    }
    return false;
}

void PrintBoard() {   
    for(int r=0; r<N; r++) {
        if (0 == (r % 3)) { std::cout<<std::endl; }
        for(int c=0; c<N; c++) {
           if (0 == (c % 3)) { std::cout<<" "; }
           std::cout.width(3);
           cout<<board[r][c]<<" ";
        }
        std::cout<<std::endl;
    }
}