软工个人项目之生成和求解数独
在这次完成个人项目的过程中,我第一次尝试了写csdn博客,用vs进行性能分析,在vs里面写单元测试,这次收获了很多。虽然还有很多需要改进的地方,但我会做得越来越好的~
1、Github地址
首先给出我的github的地址:
https://github.com/hll455/Project-Sodoku
2、psp表格—估计花费时间
psp2.1 | Personal Software Process Stages | 预估耗时(分钟) |
---|---|---|
Planning | 计划 | 30 |
Estimate | 估计这个任务需要多少时间 | 20 |
Development | 开发 | 1440 |
Analysis | 需求分析(包括学习新技术) | 1000 |
Design Spec | 生成设计文档 | 40 |
Design Review | 设计复审(和同事审核设计文档) | 60 |
Coding Standard | 代码规范(为目前的开发制定合适的规范) | 90 |
Design | 具体设计 | 1200 |
Coding | 具体代码 | 1200 |
Code Review | 代码复审 | 600 |
Test | 测试(自我测试、修改代码、修改提交) | 1200 |
Reproting | 报告 | 1200 |
3、解题思路
1)生成数独
之前对数独没有太多了解,只知道数独每一行每一列每一宫都需要满足1-9不重复。我对生成数独的第一理解,就是相当于对全是0的数独的求解,由于第一行第一个数固定,所以第一行固定排列(一共有8!=40320种),之后开始递归搜索遍历,这样生成到最后一个数字时则生成了数独;生成一个数独之后,可对整个数独进行转置40320✖2=80640,再对2、3行,4、5、6行,7、8、9行交换顺序,这样就可以满足(80640✖2✖6✖6>1000000)。但具体实现时,发现这样的话每生成一个数独的递归求解时,会花费很多时间,在结果的性能评分上肯定不行,所以我就去网上找了一下数独有没有什么简单的规律,比如只需要确定一行,剩下的都可以直接写出来,果然,有一种简单数独就是这样的规律,即:只需要确定第一行,后面的8行都可以通过平移第一行来获得。
6 1 2 3 4 5 7 8 9
7 8 9 6 1 2 3 4 5
3 4 5 7 8 9 6 1 2
9 6 1 2 3 4 5 7 8
5 7 8 9 6 1 2 3 4
2 3 4 5 7 8 9 6 1
8 9 6 1 2 3 4 5 7
4 5 7 8 9 6 1 2 3
1 2 3 4 5 7 8 9 6
由上面的数组可以看到,以第一行为基准,9行分别移动的位数为{0,3,6,1,4,7,2,5,8},根据此位移数组,我们可以根据第一行唯一确定一个数独。生成一个数独后,剩余的数独与上面类似,都可以转换成位移数组的位置交换来体现:即3,6可以互换,8,2,5可以互换,7,1,4可以互换,这样可以生成8!✖2✖6✖6=2903040>1000000,符合题目要求。
2)求解数独
我看到求解数独时,思路就是按照我们做数独的思路,当所在位置的数字为0的时候,我们就找这一行、这一列、这一宫有没有1-9中没有出现的数字,有的话则继续往后填写,没有的话则返回上一步,将上一步填写的数字换成另外一个符合要求的数字,依次类推,直到数独中的最后一个0被填充完成,则求解成功。这样的话,只需要递归求解就好,不过如果每次对一个0的那一行那一宫那一列遍历的话,时间复杂度会很高,所以我采取“以空间换时间”,对数独进行预处理,直接将行列宫的数字出现与否用数组表示出来,这样,在每次判断是否可以填入某数时,则不需要进行遍历,只需要直接查看该数组的某一个元素的值是否为0。这里我设置了一个三维数组大小为visit[3][10][10]的数组,利用每个元素的值来表示该宫/行/列中的某个数字是否出现过,0为未出现,1为出现。visit数组第一维中0表示宫,1表示行,2表示列,第二维中表示第几行/第几列/第几宫(范围为0到9),第三维表示1-9数字。
4、设计实现过程
1)类与函数及函数间关系
其实最开始写的代码为面向过程的c语言,后来因为单元测试需要类,所以我将面向过程直接改成了面向对象的c++。
只设置了一个类sodoku,将输入的两个参数作为属性;
主要设置了三个函数,其中choosecors函数对solvesodoku和createsodoku函数进行调用。
-
choosecors函数——对输入的参数进行处理,
-
createsodoku函数——生成数独,
-
solvesodoku——求解数独。
流程图如下所示:
2)单元测试的设计
我设计了10个测试用例,其中5个检查生成数独时输入参数的合法性,1个测试非-s和-c的输入的处理,2个测试求解数独的正确性和格式的正确性,2个检查生成数独时输入参数的合法性。完成对所有路径的测试,除了输入时的参数个数问题不能在单元测试中体现。
十个测试用例分别为:其中choosecors函数的输入参数分别为argv[1]和argv[2]
1、int ans = s1.choosecors("-c", "a");
2、int ans = s1.choosecors("-c", "1000001");
3、int ans = s1.choosecors("-c", "123");