我正在努力制作一个Sudoku Solving程序几天,但我坚持使用这些方法。我在这里找到了这个算法,但我真的不明白它:
- 从第一个空单元格开始,并在其中放入1。
- 检查整个电路板,看看是否有任何冲突
- 如果电路板上有coflicts,请将当前单元格中的数字增加1(因此将1更改为2,将2更改为3等)
- 如果电路板干净移动,请再次从第一步开始。
- 如果给定单元格中的所有九个可能的数字都会导致电路板发生冲突,那么您将此单元格设置为空,返回上一个单元格,然后从步骤3重新开始(这是'回溯'来的地方在)。
醇>
这是我的代码。我认为我的 Help_Solve(...)功能出了问题。你能帮我解决一下这个问题吗?
#include <iostream>
#include <iomanip>
#include <time.h>
#include <cstdlib>
#include <windows.h>
using namespace std;
class Sudoku
{
private:
int board[9][9];
int change[9][9];
public:
Sudoku();
void Print_Board();
void Add_First_Cord();
void Solve();
void Help_Solve(int i, int j);
bool Check_Conflicts(int p, int i, int j);
};
Sudoku Game;
void setcolor(unsigned short color) //The function that you'll use to
{ //set the colour
HANDLE hcon = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hcon,color);
}
Sudoku::Sudoku()
{
for(int i = 1; i <= 9; i++)
for(int j = 1; j <= 9; j++)
board[i][j] = 0;
}
void Sudoku::Print_Board()
{
for(int i = 1; i <= 9; i++)
{
for(int j = 1; j <= 9; j++)
{
if(change[i][j] == 1)
{
setcolor(12);
cout << board[i][j] << " ";
setcolor(7);
}
else cout << board[i][j] << " ";
if(j%3 == 0) cout << "| ";
}
cout << endl;
if(i%3 == 0) cout << "------+-------+---------" << endl;
}
}
void Sudoku::Add_First_Cord()
{
board[1][1] = 5; change[1][1] = 1;
board[1][2] = 3; change[1][2] = 1;
board[1][5] = 7; change[1][5] = 1;
board[2][1] = 6; change[2][1] = 1;
board[2][4] = 1; change[2][4] = 1;
board[2][5] = 9; change[2][5] = 1;
board[2][6] = 5; change[2][6] = 1;
board[3][2] = 9; change[3][2] = 1;
board[3][3] = 8; change[3][3] = 1;
board[3][8] = 6; change[3][8] = 1;
board[4][1] = 8; change[4][1] = 1;
board[4][5] = 6; change[4][5] = 1;
board[4][9] = 3; change[4][9] = 1;
board[5][1] = 4; change[5][1] = 1;
board[5][4] = 8; change[5][4] = 1;
board[5][6] = 3; change[5][6] = 1;
board[5][9] = 1; change[5][9] = 1;
board[6][1] = 7; change[6][1] = 1;
board[6][5] = 2; change[6][5] = 1;
board[6][9] = 6; change[6][9] = 1;
board[7][2] = 6; change[7][2] = 1;
board[7][7] = 2; change[7][7] = 1;
board[7][8] = 8; change[7][8] = 1;
board[8][4] = 4; change[8][4] = 1;
board[8][5] = 1; change[8][5] = 1;
board[8][6] = 9; change[8][6] = 1;
board[8][9] = 5; change[8][9] = 1;
board[9][5] = 8; change[9][5] = 1;
board[9][8] = 7; change[9][8] = 1;
board[9][9] = 9; change[9][9] = 1;
}
bool Sudoku::Check_Conflicts(int p, int i, int j)
{
for(int k = 1; k <= 9; k++)
if(board[i][k] == p) return false;
for(int q = 1; q <= 9; q++)
if(board[q][j] == p) return false;
/*
*00
000
000
*/
if((j == 1 || j == 4 || j == 7) && (i == 1 || i == 4 || i == 7))
{
if(board[i][j+1] == p || board[i][j+2] == p || board[i+1][j] == p ||
board[i+2][j] == p || board[i+1][j+1] == p || board[i+1][j+2] == p ||
board[i+2][j+1] == p || board[i+2][j+2] == p)return false;
}
/*
000
000
*00
*/
if((j == 1 || j == 4 || j == 7) && (i == 3 || i == 6 || i == 9))
{
if(board[i-1][j] == p || board[i-2][j] == p || board[i][j+1] == p ||
board[i][j+2] == p || board[i-1][j+1] == p || board[i-1][j+2] == p ||
board[i-2][j+1] == p || board[i-2][j+2] == p)return false;
}
/*
000
*00
000
*/
if((j == 1 || j == 4 || j == 7) && (i == 2 || i == 5 || i == 8))
{
if(board[i-1][j] == p || board[i+1][j] == p || board[i-1][j+1] == p ||
board[i][j+1] == p || board[i+1][j+1] == p || board[i+1][j+2] == p ||
board[i][j+2] == p || board[i+1][j+2] == p)return false;
}
/*
0*0
000
000
*/
if((j == 2 || j == 5 || j == 8) && (i == 1 || i == 5 || i == 7))
{
if(board[i-1][j] == p || board[i+1][j] == p || board[i-1][j+1] == p ||
board[i][j+1] == p || board[i+1][j+1] == p || board[i+1][j+2] == p ||
board[i][j+2] == p || board[i+1][j+2] == p)return false;
}
/*
000
0*0
000
*/
if((j == 2 || j == 5 || j == 8) && (i == 2 || i == 5 || i == 8))
{
if(board[i-1][j] == p || board[i-1][j-1] == p || board[i-1][j+1] == p ||
board[i][j+1] == p || board[i][j-1] == p || board[i+1][j+1] == p ||
board[i][j] == p || board[i+1][j-1] == p)return false;
}
/*
000
000
0*0
*/
if((j == 2 || j == 5 || j == 8) && (i == 3 || i == 6 || i == 9))
{
if(board[i][j-1] == p || board[i][j+1] == p || board[i-1][j] == p ||
board[i-1][j+1] == p || board[i-1][j-1] == p || board[i-2][j] == p ||
board[i-1][j+1] == p || board[i-2][j-1] == p) return false;
}
/*
00*
000
000
*/
if((j == 3 || j == 6 || j == 9) && (i == 1 || i == 4 || i == 7))
{
if(board[i][j-1] == p || board[i][j-2] == p || board[i+1][j] == p ||
board[i+1][j-1] == p || board[i+1][j-2] == p || board[i+2][j] == p ||
board[i+2][j-1] == p || board[i+2][j-2] == p) return false;
}
/*
000
00*
000
*/
if((j == 3 || j == 6 || j == 9) && (i == 2 || i == 5 || i == 8))
{
if(board[i-1][j] == p || board[i-1][j-1] == p || board[i-1][j-2] == p ||
board[i][j-1] == p || board[i][j-2] == p || board[i+1][j] == p ||
board[i+1][j-1] == p || board[i+1][j-2] == p) return false;
}
/*
000
000
00*
*/
if((j == 3 || j == 6 || j == 9) && (i == 3 || i == 6 || i == 9))
{
if(board[i][j-1] == p || board[i][j-1] == p || board[i-1][j] == p ||
board[i-1][j-1] == p || board[i-1][j-2] == p || board[i-2][j] == p ||
board[i-2][j-1] == p || board[i-2][j-2] == p) return false;
}
return true;
}
void Sudoku::Help_Solve(int i, int j)
{
if(j <= 0)
{
i = i-1;
j = 9;
}
if(change[i][j] == 1) return Game.Help_Solve(i, j-1);
for(int p = 1; p <= 9; p++)
if(Game.Check_Conflicts(p, i, j))
{
board[i][j] = p;
return;
}
return Game.Help_Solve(i, j-1);
}
void Sudoku::Solve()
{
for(int i = 1; i <= 9; i++)
{
for(int j = 1; j <= 9; j++)
{
if(board[i][j] == 0 && change[i][j] == 0)
{
Game.Help_Solve(i, j);
}
}
}
for(int i = 1; i <= 9; i++)
for(int j = 1; j <= 9; j++)
if(board[i][j] == 0) Game.Help_Solve(i, j);
}
int main()
{
Game.Add_First_Cord();
Game.Solve();
Game.Print_Board();
system("pause");
return 0;
}
编辑:我需要使用递归吗?但也许我给函数的参数是错误的。我真的不知道。在 Add_First_Cord()中,我声明每个数独在开头的起始值。以下是我使用的值:http://bg.wikipedia.org/wiki/%D0%A4%D0%B0%D0%B9%D0%BB:Sudoku-by-L2G-20050714.gif。我希望看到解决的数独,因为它在维基百科中显示。但有些解决的价值是正确的,其他则不是。这是我在控制台
中得到的内容答案 0 :(得分:20)
TNode
TNode => vector<TNode>
S
可以在行N
添加号码I
,列J
,如果:
(I,J)
为空N
I
N
J
N
(I,J)
S
映射到满足这些规则的状态vector
您当前的方法混合了要搜索的图表的规范和搜索算法的实现。如果你混合这两个,你会遇到很多困难。这个问题自然地分为两个不同的部分 - 算法和图形 - 所以你可以而且应该在你的实现中利用它。它会使它变得更加简单。
如果你采用这种分离方式,你获得的另一个好处是,你将能够重复使用你的图搜索算法解决大量问题 - 非常酷!
答案 1 :(得分:4)
以下假设您正在尝试解决给定的棋盘,而不是生成拼图。
创建一个对象可以容纳一块板的类(这里称为board_t
)。这个类可能在内部使用数组,但必须支持复制板。
让函数void solve(board_t const& board);
对每个数字n
重复以下内容:
n
return
。solve(copied_board)
这是一种递归回溯解决方案,可以解决难题。您可以通过适当的修剪或演绎步骤显着加快速度(例如,如果您在插入一个数字后连续输入8个数字,则可以立即输入第九个数字而不进行任何搜索)。
虽然肯定不是一种令人印象深刻的技术,但它很有可能正常工作,因为您只会修改副本以添加单个值。这可以防止您的数据结构损坏(您的想法有一个问题是它会破坏它在回溯时找到的数字,不一定是您刚刚插入的数字,但可能是最初难题的一部分)。
提高性能非常简单,一旦你开始选择更智能的启发式方法(例如,不是按顺序测试方块,你可以选择剩余移动最少的那些并尝试将它们排除在外 - 或者反之亦然...)或者开始做一些演绎和修剪。
注意:The Algorithm Design Manual使用Soduko解算器来显示这些技术对回溯的影响。
答案 2 :(得分:1)
递归算法有一个非常重要的修改:使用最受约束的第一种方法。这意味着首先要解决具有最少可能候选者的单元格(当删除直接行/列/块冲突时)。
另一个修改是:就地更换电路板;不要复制它。在每次递归调用中,您只修改电路板上的一个单元格,并且该单元格过去是空的。如果该调用没有在递归调用树的某个地方的某个已解决的板中结束,只需在返回之前再次清除该单元 - 这会使该板返回到原始状态。
您可以在地址上的C#中找到一个非常简短的快速解决方案:Sudoku Solver。它只在大约100步中解决了任意数独板,这都归功于最受约束的第一个启发式算法。
答案 3 :(得分:0)
这是一个经典的约束满足问题。我建议对该主题进行一些研究,以找出成功的策略。您将需要使用AC-3(Arc Consistency 3)算法以及回溯技术来解决问题。