阿煜的sudoku作业

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/V5ZSQ/article/details/85058510
  • Github项目地址:

https://github.com/LiZ-Samsara/sodoku/tree/master/sudoku

  • 各模块耗时:

在这里插入图片描述

  • 解题思路:

数独是源自18世纪瑞士的一种数学游戏。是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复。

数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次,所以又称“九宫格”。

数独游戏应该或多或少大家都有玩过,手动解数独每个人都有不同的技巧方法,但是如果是作为程序编成代码来实现手动解数独的思考过程和技巧运用就有了很大的难度和不可复制性。

首先我们考虑生成数独终局的部分。

sudoku项目要求生成1e6种不同的数独,那么通过分析一个有效成立的数独终局来看,对于任何一个1-9的全排列,都可以通过每一行是第一行右移3、6、1、4、7、2、5、8列的结果。这样就可以获得9!种数独终局,显然满足1e6种。另外,项目还要求左上角为学号后两位之和mod 9 + 1,那么我的学号是1120161717,后两位(1+7)%9+1=9,因此第一排初始化为912345678.

第一个数字9不动,后八位进行任意全排列。这里运用C++STL库中的next_permutation函数,这个函数可以生成一个数列的全排列。

最后注意避免重复即可。

然后是求解数独的部分。

从算法的角度来说,首先想到的就是运用最简单的暴力搜索,通过暴搜结果最终得到正解。

这个想法是正确的无疑,但是在我编程完毕测试的时候发现,效率显然非常低,耗时巨大。对于一些较小的用例而言暴搜是行之有效的,但是在面对上千个数独时,暴搜无法在很快的时间内得到答案,由于耗时的原因,简单的暴搜不能作为本题的正确求解算法。

如何寻找到一个适合求解数独的算法,我们需要将这个实际问题进行转化,找到近似的算法模型。

数独的规则大致为:

  1. 每一行都要有不重复出现的1-9
  2. 每一列都要有不重复出现的1-9
  3. 每一个九宫格内都有不重复出现的1-9

按照这样的三个原则填满,则可以得到正确结果。

那么在有这样一些规则条件的限制下,我查阅了资料并且咨询了ACM集训队的学长,在他们的建议下,选择了Dancing Links X算法来解决这一问题。

Dancing Links X 主要用于求解精确覆盖问题,从另一个角度来思考求解数独的过程,相当于对一个9*9的宫格图进行精确的数字覆盖。用一个交叉十字双向循环链表维护了递归和回溯过程中的修改,大大提升了修改和回溯的效率。

  • 设计实现过程:

suduko项目大致分为两部分,生成数独和求解数独。因为这两部分基本是相互独立的,所以接下来单独介绍他们的设计。

首先是生成数独的部分,较为简单,主要用到next_permutation函数即可。

求解数独的部分根据之前的思路设计,将用一个DLX类来实现DLX精确覆盖的功能,还需要一个函数读取待解数独,构造DLX求解并输出。

解析命令行指令的部分,由于MSVC没有getopt,所以将会在Github上找一份getopt的开源代码贴到项目中。

对于生成数独的单元测试,先用小用例(大概1000组)来验证生成终局的正确性以及是否冲突,然后再用大用例(1e6组)来测试单元性能。

求解数独的单元测试同理,先用1000组小用例进行测试结果正确性,然后通过大用例来测试单元性能。

猜你喜欢

转载自blog.csdn.net/V5ZSQ/article/details/85058510