1. 程式人生 > >【演算法分析】回溯法解數獨(九宮格)演算法

【演算法分析】回溯法解數獨(九宮格)演算法

    這篇文章,是來詳細介紹怎樣寫出一個演算法,來解出所有的數獨問題。演算法的程式執行時間,縮減在了毫秒級別。等到這篇文章結束,我會抽時間寫一篇文章,介紹如何生成一個隨機的唯一解的數獨問題。
    另外,為了做圖形方便,示範程式碼是用C++,喜歡其他語言的朋友,可以參考一下思路。

數獨,是源自18世紀瑞士的一種數學遊戲。是一種運用紙、筆進行演算的邏輯遊戲。玩家需要根據9×9盤面上的已知數字,推理出所有剩餘空格的數字,並滿足每一行、每一列、每一個粗線宮(3*3)內的數字均含1-9,不重複。

數獨盤面是個九宮,每一宮又分為九個小格。在這八十一格中給出一定的已知數字和解題條件,利用邏輯和推理,在其他的空格上填入1-9的數字。使1-9每個數字在每一行、每一列和每一宮中都只出現一次,所以又稱“九宮格”

利用演算法解數獨,主要採用了回溯法。思路如下:

1 .遍歷已生成的數獨二維陣列,得出空白格子的數目。

2 .從第一個空白格子開始,利用數獨的規範,對比同一列,同一行,以及同一個九宮格的數字,找出其所有可行解,存入陣列(利用整形變數的位運算,會有更高的效率)。利用最後一個可行解,進行下一步運算。

3 .對剩下的格子進行同樣的操作。

4 .如遇到無解的情況,則進行回溯操作。繼續重複上述運算。

5 .當所有空白格子填滿,所得結果,即為數獨的解。

具體演算法實現如下:

#include <iostream>
#include <cstdlib>
#include <time.h>
using namespace std; #define MAX 9 typedef struct node { int col; int row; int value[MAX+1]; }Node; void print_sudoku(int Sudoku[MAX][MAX]); int count_num_empty(int Sudoku[MAX][MAX]); void backtrack(int Sudoku[MAX][MAX], int num_empty, Node *node_stack); int findvalue(int Sudoku[MAX][MAX], Node *node_stack); int
findvalue(int Sudoku[MAX][MAX], Node *node_stack) { int i = node_stack->col; int j = node_stack->row; int k = 0; int n = 0; for(k = 0; k < MAX+1; ++k) node_stack->value[k] = 0; for(k = 1; k < MAX+1; ++k) { node_stack->value[Sudoku[i][k-1]] = 1; node_stack->value[Sudoku[k-1][j]] = 1; } for(k = 0; k < 3; ++k) { for(n = 0; n < 3; ++n) { node_stack->value[Sudoku[i/3*3+k][j/3*3+n]] = 1; } } node_stack->value[0] = 0; for(k = 1; k < MAX+1; ++k) if(node_stack->value[k] == 0) node_stack->value[0]++; for(k = 1; k < MAX+1; ++k) { if(node_stack->value[k] == 0) { node_stack->value[k] = 1; node_stack->value[0]--; break; } } if(k == MAX+1) return -1; else return k; } void backtrack(int Sudoku[MAX][MAX], int num_empty, Node *node_stack) { int i = 0; int j = 0; int k = 0; int flag = 0; while(num_empty) { for(i = 0; i < MAX; ++i) { for(j = 0; j < MAX; ++j) { if(Sudoku[i][j] == 0) { (node_stack + k)->col = i; (node_stack + k)->row = j; Sudoku[i][j] = findvalue(Sudoku, node_stack + k); if(Sudoku[i][j] == -1) { Sudoku[i][j] = 0; k--; while((node_stack + k) -> value[0] == 0) { if(k == 0) { cout << "數獨無解" << endl; exit(1); } Sudoku[(node_stack + k) -> col][(node_stack + k) -> row] = 0; num_empty++; k--; } for(flag = 1; flag < MAX+1; ++flag) { if((node_stack + k)->value[flag] == 0) { Sudoku[(node_stack + k) -> col][(node_stack + k) -> row] = flag; (node_stack + k)->value[flag] = 1; (node_stack + k)->value[0]--; break; } } num_empty++; i = (node_stack + k)->col; j = (node_stack + k)->row; } k++; num_empty--; } } } } free(node_stack); node_stack = NULL; print_sudoku(Sudoku); } int count_num_empty(int Sudoku[MAX][MAX]) { int num = 0; for(int i = 0; i < MAX; ++i) { for(int j = 0; j < MAX; ++j) { if(Sudoku[i][j] == 0) num++; } } return num; } void print_sudoku(int Sudoku[MAX][MAX]) { for(int i = 0; i < MAX; ++i) { for(int j = 0; j < MAX; ++j) { cout << " " <<Sudoku[i][j]; } cout << endl; } } int main(int argc, char const* argv[]) { #if 0 int Sudoku[MAX][MAX]; for(int i = 0; i < MAX; ++i) { for(int j = 0; j < MAX; ++j) { cin >> Sudoku[i][j]; } } #endif #if 1 int Sudoku[MAX][MAX] = { (8,0,0,0,0,0,0,0,0), (0,0,3,6,0,0,0,0,0), (0,7,0,0,9,0,2,0,0), (0,5,0,0,0,7,0,0,0), (0,0,0,0,4,5,7,0,0), (0,0,0,1,0,0,0,3,0), (0,0,1,0,0,0,0,6,8), (0,0,8,5,0,0,0,1,0), (0,9,0,0,0,0,4,0,0) }; #endif int num_empty = count_num_empty(Sudoku); Node * node_stack = (Node *)malloc(sizeof(struct node) * num_empty); backtrack(Sudoku, num_empty, node_stack); return 0; }