使用递归回溯的 C++ 数独求解器不起作用
C++ Sudoku Solver using recursion backtracking not working
我正在尝试使用递归和回溯编写数独解算器。但是我的代码总是returns false。 SudokuSolver() 仅遍历第一行到第四列,然后停止并开始回溯。问题是回溯一直持续到第一个单元格,最后 returns false。结果,none 个空单元格(值为“-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;
}
}
递归函数与非递归函数完全一样。
(这是了解递归最重要的事情。)
这里:
board[row][col]=num;
SolveSudoku();
board[row][col]=-1;
你忽略了函数调用的结果,所以你会递归直到棋盘被填满,然后完全回溯,最后 return false;
.
您可能想要的是(完全未经测试):
if (SolveSudoku())
{
return true;
}
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);
}
但错误地添加了另一个负数。
可能还有比这更多的错误。
SolveSudoku() 需要迭代直到棋盘中的所有 "cells" 都被访问过。
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;
}
}
我正在尝试使用递归和回溯编写数独解算器。但是我的代码总是returns false。 SudokuSolver() 仅遍历第一行到第四列,然后停止并开始回溯。问题是回溯一直持续到第一个单元格,最后 returns false。结果,none 个空单元格(值为“-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;
}
}
递归函数与非递归函数完全一样。
(这是了解递归最重要的事情。)
这里:
board[row][col]=num;
SolveSudoku();
board[row][col]=-1;
你忽略了函数调用的结果,所以你会递归直到棋盘被填满,然后完全回溯,最后 return false;
.
您可能想要的是(完全未经测试):
if (SolveSudoku())
{
return true;
}
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);
}
但错误地添加了另一个负数。
可能还有比这更多的错误。
SolveSudoku() 需要迭代直到棋盘中的所有 "cells" 都被访问过。
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;
}
}