数独求解 Sudoku Solver

来源:互联网 发布:如何退出手机淘宝账号 编辑:程序博客网 时间:2024/04/29 13:00

数独游戏是个经典的游戏。现在我要通过程序的方法才自动求解一个数独。这个问题在《Puzzles for Programmers and Pros》一书中有讲解。

思路:

最直接的方法是,可以用backtracking进行递归尝试。一旦探测到错误就回溯,最终将在递归的最深处发现正解。

注意backtracking是可以用引用类型参数进行递归深入的,但是递归回溯出来的时候一定要将改变的值回复回原样,这样下一轮的尝试才不会受到影响。


纯粹的递归尝试就相当于穷举法,速度非常慢。想想人脑做数独的时候是怎么进行的?肯定不是上面的方法。而是先找出最容易写出答案的。对于一个位置,如果发现其同行、同列、同方格中已经把9个数的8个都使用过了,那么必然的,这个位置具有唯一的答案。依照这个方法进行重复,可以先把数独填上若干。

当找不到具有唯一确定答案的位置的时候,意味着就必须要尝试性的推测了。这时候,我就找推测范围最小的位置进行尝试。因为范围越小,成功概率越大。这样可以尽量减少错误的递归次数。


代码:

#include <iostream>#include <vector>#include <string>using namespace std;vector<char> checker(vector<vector<char> > &board, int row, int col){    vector<char> posibile;    int bitmap[9];    memset(bitmap, 0, sizeof(bitmap));    int i;    for(i=0;i<9;i++)        if(board[row][i] != '.')            bitmap[board[row][i] -'1'] = 1;        for(i=0;i<9;i++)        if(board[i][col] != '.')            bitmap[board[i][col] - '1'] = 1;        int row_base = (row/3)*3;    int col_base = (col/3)*3;    for(int i = 0;i<3;i++)        for(int j=0;j<3;j++)        {            if(board[row_base + i][col_base + j] != '.')                bitmap[board[row_base + i][col_base + j] - '1'] = 1;        }    for(i=0;i<9;i++)        if(bitmap[i] == 0)        {            posibile.push_back(char('1' + i));        }    return posibile;}bool fun(vector<vector<char> > &board){    size_t min = 10;    vector<char> minoption;    int mini, minj;    // 寻找一个推测范围最小的位置    for(int i=0;i<9;i++)        for(int j=0;j<9;j++)        {            if(board[i][j] != '.')                continue;                        vector<char> option = checker(board, i, j);            if(option.size() < min)            {                min = option.size();                minoption = option;                mini = i;                minj = j;            }        }        if(min == 10)        return true; //min没有改变,可见所有都填满        // 对推测范围最小的位置进行递归尝试    for(int k=0;k<minoption.size();k++)    {        board[mini][minj] = minoption[k];        if(fun(board))            return true;        board[mini][minj] = '.';    }    return false; //这儿很关键!!因为并非option里一定有正确答案,因为可能是之前的数放错了}void solveSudoku(vector<vector<char> > &board){    if(board.size() != 9)        return;    bool flag = true;    while(flag)    {        flag = false;        for(int i=0;i<9;i++)        {            for(int j=0;j<9;j++)            {                if(board[i][j] == '.')                {                    vector<char> tmp = checker(board, i, j);                    if(tmp.size() == 1)                    {                        board[i][j] = tmp[0];                        flag = true;                    }                }            }        }    }        fun(board);}void show(vector<vector<char> > &board){    for(int i=0;i<9;i++)    {        for(int j=0;j<9;j++)            cout<<board[i][j]<<" ";        cout<<endl;    }    cout<<endl;}int main(int argc, const char * argv[]){    vector<vector<char> > board;    vector<char> t;    string s[9] = {"..9748...","7........",".2.1.9...","..7...24.",".64.1.59.",".98...3..","...8.3.2.","........6","...2759.."};        for(int i=0;i<9;i++)    {        for(int j=0;j<9;j++)            t.push_back(s[i][j]);        board.push_back(t);        t.clear();    }    show(board);    cout<<"start..."<<endl;    solveSudoku(board);    cout<<"over"<<endl;    show(board);    return 0;}




1 0
原创粉丝点击