深度优先算法实现数独求解

解决方法

计算机因其强大的算力和稳固的记忆性,使其相比人类求解数独,天然的具备更优越的条件。也使得一些工作量相对大量,逻辑思维要求较强的方法重新回到选择视线当中。笔者主要运用以 “八皇后问题”为例讲解的回溯思想,结合人类求解数独的两类基本思路,得出通过双重循环实现盘面遍历,借助深度优先的递归思维实现填入数字并依情况进行回溯,从而求解数独的思路。

该方法既要借鉴直接观察填入法中的试错思路,通过不断进行符合游戏规则的试错推进解题进度;又要借鉴人类求解数独中“间接候选”思路以减少每递归层的分支,从而降低时间复杂度和空间复杂度,实现简单的算法优化。

与“八皇后问题”中仅通过行遍历或列遍历实现深度优先算法不同,数独因其需要用1~9间的数字填满盘面而不违反规则,使得对数独的遍历回溯必须要进行全盘遍历。即以双重循环为基础,实现盘面遍历。

回溯思路存在深度优先和广度优先两大类,但从数独的一般解题顺序和其复杂的逻辑特征来看,选择深度优先进行递归尝试是较为常规且稳妥的解题思路。即以深度优先的递归思路进行试错,实现填入数据并依情况进行回溯。

算法优化

若采用常规思维,则需构建一个判断函数(用于判断方格内能否再填入一个数)和一个查询函数(用于查询方格内能填入哪些数),并在深度优先遍历过程中反复调用,从而造成大量时间开销。同时需要对9×9盘面内每一个小方格构建一个长度为9的int数组用于存储展示行列粗块(3×3)中哪些数字已经出现,并依次发现未出现的可填入数字,从而造成大量空间开销。

采用二进制优化后,可以通过一个九位二进制数(右起第一位为0位,至最左共9位0-1数,)的十进制整数,来取代长度为9的int数组,当数字i(1<=i<=9)可以填入空格时,该格的九位二进制数第i-1位为1。相反的,当某个十进制整数的九位二进制数第K位(0<=k<=8)为1时,则暗含数字k+1可以填入。例如:九位二进制数(000101010)表示2,4,6已经在该格行、列、粗块中未出现,同时符合逻辑判别,可以填入该格。

通过一定的实验和思考加之查阅相关资料发现:对任意的数i, {i & (-i)}保留 i 二进制表示中最低位的 1;可以用 i 和 i-1进行按位与运算、用i和最低位的 1 进行按位异或运算来消去i二进制表示中最低位的1;将数i与数 {1 << k} 进行按位异或运算,可以对数i的二进制表示中第k位实现二进制0-1转换等,借助这类二进制的简化查询和使用,可以避免查询函数的构造和大量存储空间的需求。

另一方面,当某格九位二进制数中只有一位为1时,可以直接在该格中填入对应位数字,以达到减少递归深度的优化效果。

算法实现

1.基本数据结构:

长度为9的int数组 line:用于存储每行中出现的数字(以九位二进制表示)

长度为9的int数组column:用于存储每列中出现的数字
3×3大小的int二维数组 block:用于存储盘面内9个粗块中出现的数字
[int,int]类型的动态数组space:用于存储盘面内空白块的位置[i,j]

 2.用于在填写盘面和回溯时实现linecolumnblock更新的函数update

  主要原理:将数i与数 {1 << k} 进行按位异或运算,实现对数i的二进制表示中第k位的二进制0-1转换

  3.主调用函数doSudo用于实现数独求解:

1)通过双重循环遍历盘面board,并调用update函数,更新linecolumnblock值:

2)查找出空白格内候选值唯一的块[i,j],直接向其中填入该值,以减少后续递归层数和分支数。

 

3)待直接填入完成后,再查找出需要填入的空白格,用space记录,供dfs函数进行深度优先遍历。

4.深度优先遍历函数dfs:

主要原理:对任意的数i, {i & (-i)}保留 i二进制表示中最低位的1。
                 借助Integer.bitCount(i)自带函数,可以统计i的二进制表示中1的个数。
                 通过line、column、block三者的或运算获取到该空白块不可取的值。
                通过深度优先遍历经典的: “进入->自递归->退出”思路,获得如下算法代码:

 

5.编写主函数类调用该数独求解类:

该类主要用于构建未求解的数独盘面border[][],并通过创建sudo类的对象,调用doSudo函数实现计算机求解数独,并通过双重循环输出,具体如下图:

 实验结果:程序顺利得出符合数独规则结果,耗时6S:

实验取得圆满成功!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值