数独

算法介绍

本实验采用的是前向搜索的算法,选择约束最大的点作为下一个扩展点,并通过每选择一个点则更新各个点的限制域进行剪枝操作,加快速度。

将棋盘中每个点都设置为一个结构体,结构体的结构如下:

typedef struct {

       int value;                             //表示此点的值

       int limit;                              //表示此点受限制的个数

       int f9[MAX+1];                   //表示此点的限制域

}Node;

主要函数boolDFS(int zeros)逻辑如下:

       1、若是参数zeros为0,则返回true;

       2、寻找整个棋盘中没有值且限制个数最大的点Node0;

       3、若是Node0的限制个数为9,则表示此点没有可填的值,则返回false;

       4、循环寻找此点所能填的值,循环长度为1~9。循环的内部结构如下:

              A 根据限制值域,若是循环因子i可被选为此点的值

                     a 将此点值置为i

                     b 参数zeros自减

                     c 更新整个棋盘所有点的限制值域和限制个数,相当于剪枝操作,避免不必要的搜索

                     d 递归向下调用DFS(zeros)

                     e 若是调用成功,则返回true

                     f 若是调用失败,zeros++,将此点的值重新置为0,更新整个棋盘的所有点的限制值域和限制个数,返回步骤4

       5、若是循环完成,仍没有找到可填的值,则返回false。

       主要函数voidupdate()的功能是更新整个棋盘的所有点的限制值域和限制个数,根据各点的value值进行更新。

       主要函数voidinit()的功能是在每一行随机选择一个点,将此点的值设置为行号+1,赋值完所有行后,更新整个棋盘所有点的限制值域和限制个数。

       生成完整数独的算法就是先使用init函数初始化9个点的值,在使用DFS函数进行数独求解,也即是说将数独的生成问题转化为数独的求解问题。

       生成数独的算法是通过对完整数独进行挖洞。随机生成行列数,挖去此行列数所在的点的值,当挖去规定个数的值之后,更新棋盘所有点的限制值域和限制个数。

       求解数独的算法是直接调用DFS函数进行求解。



Sudoku.h:

#ifndef SUDUKU_H

#define SUDUKU_H

 

#define MAX 9

typedef struct {

       intvalue;                             //表示此点的值

       intlimit;                              //表示此点受限制的个数

       intf9[MAX+1];                          //表示此点的限制域

}Node;

typedef struct {

       introw;                               //行数

       intcol;                                //列数

}Place;

Node map[MAX][MAX];                           //棋局的81个点

 

void init();                                  //初始化,在每行随机挑选一个点,赋值为此行的行数+1

void update();                             //更新棋局每个点的限制域和限制个数

Place getMAX();                                //得到限制域最大的点的位置

bool DFS(int zeros);                    //深度优先搜索

void createSudo(int option);  //创建数独并求解

void solveSudo(int holes);            //求解数独

int digHole(int option);         //在完整的数独上挖洞

void printSudo();                        //打印数独

 

#endif



Sudoku.cpp:

 

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#include <string.h>

#include "Sudoku.h"

 

int main()

{

       intoption;

      

       printf("/*****************************************************/\n");

       printf("/******    Choose the options:                  ******/\n");

       printf("/******       1.Create a simple sudo(35)        ******/\n");

       printf("/******       2.Create a hard sudo(55)          ******/\n");

       printf("/******       3.quit the game                   ******/\n");

       printf("/*****************************************************/\n");

      

       scanf("%d",&option);//从控制台输入选项

       fflush(stdin);

 

       if(option== 1 || option == 2){            

              createSudo(option);

       }elseif(option == 3) {

              exit(-1);

       }  else {

              printf("illegaloption!!! quit the game\n");

       }

       return-1;

}

 

void createSudo(int option)

{

       intholes;

       inttimes = 1;

       clock_tbegin,end;

 

       memset(&map,0, sizeof(map));                  //将棋盘所有点的值全置为0

       init();                                                              //初始化棋盘

       DFS(72);                                                  //通过深度优先搜索求解数独

 

       //打印初始的完整数独

       printf("theinitial sudo:\n");

       printSudo();

 

       holes= digHole(option);                            //根据选项,将完整的数独挖去指定的个数

 

       //打印出挖去指定个数的数独

       printf("thesudo after dig the holes:\n");

       printSudo();

      

       printf("beginto solve the sudo we create.....\n");

       begin= clock();

       solveSudo(holes);                                      //求解数独

       end= clock();

      

       //打印出求解的数独

       printf("thesudo after solved:\n");

       printSudo();

       printf("thealgorithm cost %d ms to solve the sudo\n", end - begin);

}

 

bool DFS(int zeros)

{

       introw, col;

 

       Placeplace = getMAX();                            //得到限制个数最大的点的位置 

       row= place.row;

       col= place.col;     

 

       if(zeros== 0)                                    //程序出口,若是所有点都已经赋值完成,返回true

              returntrue;

       if(map[place.row][place.col].limit== 9)      //若是得到的点的限制数为9,则不能再扩展,返回false

              returnfalse;

 

       for(int i = 1; i < MAX + 1; i++) {                    //循环寻找此点所能填的值

              if(map[row][col].f9[i]== 0) {                   

                     map[row][col].value= i;

                     zeros--;                                            

                     update();                                           //更新棋盘所有点的限制值域,剪枝操作

                     if(!DFS(zeros)){                               //递归调用深度优先算法去寻求下一点的解,若是下一个点不成功则需要回溯,重新选择当前点的值

                            zeros++;                                    

                            map[row][col].value= 0;

                            update();

                     }else {                                             //若是成功,返回true

                            returntrue;

                     }

              }

       }

       returnfalse;                                                     //若是此点的所有值都不能满足,返回false

}

 

void update()

{

       inti,j;

       intcol, row;

 

       //更新限制值域

       for(i = 0; i < MAX; i++) {                 //i,j为棋盘各点的位置坐标

              for(j = 0; j <MAX; j++) {

                     for(int k = 1; k < MAX + 1; k++) {           //将i,j位置点的限制值域全部置为0

                            map[i][j].f9[k]= 0;

                     }

 

                     for(col = 0; col < MAX; col++) {              //检查同一列,更新此点的限制值域

                            if(map[i][col].value!= 0)                   

                                   map[i][j].f9[map[i][col].value]= 1;

                     }

                     for(row = 0; row < MAX; row++) {          //检查同一行,更新此点的限制值域

                            if(map[row][j].value!= 0)

                                   map[i][j].f9[map[row][j].value]= 1;

                     }    

                     row= i / 3 * 3;

                     col= j / 3 * 3;

                     for(int r = 0; r <  3; r++) {               //检查同一个3*3的小宫格,更新此点的限制值域

                            for(int c = 0; c < 3; c++) {  

                                   if(map[row+ r][col + c].value != 0)

                                          map[i][j].f9[map[row+ r][col + c].value] = 1;

                            }

                     }

              }

       }

       //更新限制个数

       for(i = 0; i < MAX; i++) {

              for(j = 0; j <MAX; j++) {

                     if(map[i][j].value!= 0) {                           //若是此点的值非0,则将此点的限制值域全置为1,限制个数置为9

                            for(int k = 1; k < MAX + 1; k++) {

                                   map[i][j].f9[k]= 1;

                            }

                            map[i][j].limit= 9;

                     }else {                                                    //若是此点的值为0,根据限制值域更新限制个数

                            map[i][j].limit= 0;

                            for(intk = 1; k < MAX + 1; k++) {

                                   if(map[i][j].f9[k] == 1)

                                          map[i][j].limit++;

                            }

                     }

              }

       }

}

 

void init()

{

       inti,col;

       time_tt;

       srand((unsigned)time(&t));

       for(i= 0; i < MAX; i++) {

              col= rand() % 9;

              map[i][col].value= i + 1;

       }

       update();

}

 

int digHole(int option)

{

       introw, col;

       time_tt;

       intholes;

       srand((unsigned)time(&t));

 

       if(option== 1) {

              holes= 35;

       }else {

              holes=55;

       }

 

       for(int k = 0; k < holes; k++) {

              row= rand() % 9;

              col= rand() % 9;

              if(map[row][col].value!= 0) {

                     map[row][col].value= 0;

              }else {

                     k--;

              }    

       }

       update();

       returnholes;

}

 

void solveSudo(int holes)

{

       DFS(holes);

}

 

 

Place getMAX()

{

       inti,j;

       intlimit = 0;

       Placemax;

 

       for(i = 0; i < MAX; i++) {

              for(j = 0; j <MAX; j++) {   

                     if(map[i][j].limit> limit && map[i][j].value == 0) { //若是此点的limit值大于之前的limit值,而且此点的value为0

                            max.row= i;

                            max.col= j;

                            limit= map[i][j].limit;

                     }

              }

       }

       returnmax;

}

 

void printSudo()

{

       inti,j;

 

       for(i = 0; i < MAX; i++) {

              for(j = 0; j < MAX; j++) {

                     printf("%d\t",map[i][j].value);

              }

              printf("\n");

       }

       printf("\n");

}



最终结果:




总结:

求解难的数独的时间比简单数独的时间慢,而且由于难的数独挖去了55个数,而且挖洞的时候,我们也没有考虑挖去洞之后的解是否唯一,所以当挖去的数比较多的时候,重新求解的数独跟初始生成的数独有可能不一样,但是重新求解的数独是满足要求,这一点儿从图3可以看出。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值