【软件工程基础】个人项目-数独

一.Github项目地址

https://github.com/zealyoung/SudokuProject

二.PSP预估时间(实际时间在文章结尾给出)

PSP2.1Personai软件流程阶段预估耗时(分钟)
规划计划60
发展开发480
分析需求分析(包括学习新技术)480

设计规格

生成设计文档120
设计回顾设计复审60
编码标准代码规范60
设计具体设计120
编码具体编码480
代码审查代码复审120
测试测试(自我测试,修改代码,提交修改)720
报告报告180
测试报告测试报告120
尺寸测量计算工作量60

尸检及过程

改进计划

事后总结,并提供过程改进计划120
 合计3180

三,解题思路

       拿到题目之后首先了解到,题目要求分为两部分,第一部分是生成9 * 9的数独终局,第二部分是求解数独,实际上两部分是可以用同一个算法解决的,因为求解数独相当于是给出了一部分数字,让你在此基础上生成数独终局。

       但是!!!虽然说是这么说,但是一开始并没有想到什么太好的思路,对于生成数独终局来说就是将1-9填入81个格子,保证每行每列都没有相同的数字,那就把数字一个一个填进去试试呗......所以就想到了回溯法,用这个方法可以生成所有可能的数独终局,所以不用担心数量问题,但是这个效率的话,想想就觉得不高。

       所以就开始了找资料路程,发现这个项目是有许多前人做过的,在进行终局生成时,比较普遍的方法是通过一个1-9的无重复的排列通过平移一定列数来生成,大致看了一下这个方法的思路,这效率肯定高,但是感觉也有不少人是回溯法解的,所以打算先尝试一下回溯法。

       结果在写代码差不多写完的时候......觉得.... emmmm要怎么确保生成的终局不重复,毕竟要生成100W,回溯的话想了半天也不知道怎么判断,也没有得到到网上合适的方案,于是暂时决定放弃了,准备试试用第二种通过平移和换行的方法来实现,不过前提也是要知道该怎么保证不重复,查找了一番之后找到了next_permutation()这个函数,可以高效的解决这个问题。

        最终也是用了通过一个1-9的无重复的排列通过平移一定列数来生成的方法,因此大致说一下这个方法的思路,给出一个排列例如813579246,将这个数列分别平移3 6 1 4 7 2 5 8列,每三行(即123,456,789三组)平移时都采用间隔为3的倍数的平移方法,则可保证宫内没有重复的数字。即可生成一个数独,因为第一个数字用(学号后两位相加%9 + 1)固定,因此共有8!= 40320种,距离100W差距较大,但将小九宫格内任意两行或两列交换仍可使整个大九宫格满足要求(当然这里除了第一行第一列不可以交换,因为左上角数值固定),只交换4〜6,7〜9行(列)即可,共有40320 * 36种,已经超过100W了。

        对于求解数独来说,也可以回溯,经过查资料发现有一种名为Dancing Links的算法,也就是舞蹈链算法,用来解决精确覆盖问题,精确覆盖问题其实就是回溯法来求解的,但是大概看了一下这个Dancing Links的算法,感觉很酷,因此打算学习一下,尝试一下用这个方法来完成求解数独部分,然而经过更细致的学习算法和看了一些关于DLX和暴力回溯的对比,发现在解数独问题上两者差距并不大,甚至DLX占用空间还要大一点,且构造时间长,如果生成终局较少,要比暴力回溯慢许多,因此最终解数独部分采用了回溯法,对每一格依次填入1-9,满足则进行下一格,直到某一格1-9都无法填入,回溯到上一格,直到生成一个完整的数独。

四,设计实现过程

      共有五个函数:

void OutputSudoku();
bool InputSudoku();
void GenerateSudoku();
void SolveSudoku();
int  Check();

生成数独终局的主体只有一个函数Ge​​nerateSudoku()

求解数独使用两个函数SolveSudoku()及检查()

生成数独终局时调用GenerateSudoku(),GenerateSudoku()会调用OutputSudoku()及库函数next_permutation();

求解数独时依次调用InputSudoku(),SolveSudoku(),OutputSudoku(),在SolveSudoku()运行过程中会调用检查()判断当前格是否可放某个数字。

因为每个函数的调用和本身所实现的功能都不复杂,因此不需要画流程图。

单元测试设计:

sudoku.exe a
sudoku.exe -c
sudoku.exe -c -1
sudoku.exe -c 100  
sudoku.exe -c abc
sudoku.exe -c 100 abc 
sudoku.exe -s
sudoku.exe -s abc
sudoku.exe -s wenjian.txt
sudoku.exe -s wenjian.txt abc

五。性能测试

以下是生成数独终局数目100W时的性能分析图:

性能分析图可以看出GenerateSudoku()消耗12670毫秒,其中有11720毫秒都是OutputSudoku()所消耗的。

改进的话可以将要输出的内容先存在一个巨大的数组里,然后打印字符串到文件中,但因为时间还在预期之内,因此这里并未做改动。

消耗最大的函数OutputSudoku():

      从上图可以看出,大量时间消耗在fputc()函数函数中,也就是向文件输入。

六。代码说明

void GenerateSudoku(int n)
{
	int change[9] = { 0,3,6,1,4,7,2,5,8 };
	for (int i = 0; i < 6 && n; i++)
	{
		if (i)
			next_permutation(change + 3, change + 6);
		for (int j = 0; j < 6 && n; j++)
		{
			if (j) next_permutation(change + 6, change + 9);
			int num[10] = { 8,1,3,5,7,9,2,4,6 };
			for (int k = 0; k < 40320 && n; k++)
			{
				if (k)next_permutation(num + 1, num + 9);
				for (int m = 0; m < 9; m++)
				{
					stor[m][0] = num[change[m] % 9];
					for (int q = 1; q < 9; q++)
					{
						stor[m][q] = num[(q + change[m]) % 9];
					}
				}
				OutputSudoku(stor);
				n--;
				if (n) fputc('\n', fpout);
			}
		}
		change[6] = 2, change[7] = 5, change[8] = 8;
	}
}

        这部分是生成数独终局的代码,第一位固定为8,选用的第一行为813579246,每次循环将数独存入数组STOR,然后调用输出函数,因为最后一次不进行换行,因此没有将每次数独终局完成后输出空格这个任务放在OutputSudoku()函数里。

       求解数独部分就是简单且暴力的回溯,就不放出来了,把求解数独在主函数中的代码说明一下,也就是下图代码,正常会在循环体里依次运行InputSudoku(),SolveSudoku( ),OutputSudoku(),但为了解决最后一次不输出换行符的问题,所以首先调用一次InputSudoku(),之后在循环体中变成SolveSudoku(),OutputSudoku(),InputSudoku()的顺序。还有可能我对FEOF()的理解有问题,起初每次都会多输出一遍,内容全是空格和换行,后来将InputSudoku()改为布尔类型,通过布尔类型判断,当返回假时直接break,解决了这个问题。

	InputSudoku(stor);
	while (!feof(fpin)) 
	{                   
		SolveSudoku(0);
		OutputSudoku(stor);
		if (InputSudoku(stor))
		{
			fputc('\n', fpout);
		}
		else break;
	}

七,感悟与收获

      首先所花费的时间比较长,一开始也是没什么头绪,但是通过查找找到了很多不错的生成和求解算法,通过进一步的了解明确了方向,之后在写代码时遇到的问题也不少,自己的编程能力有待提高,调试也问题不少,主要是输出文件的格式和内容的正确性,小学期的时候我们有一次组队项目,自己也对项目从无到有的流程大致了解,但当时自己基本是划水的,这次只有自己一个人,只能依靠自己的力量,完成这次个人项目也是收获颇多。

八.PSP实际时间

PSP2.1Personai软件流程阶段预估耗时(分钟)
规划计划60
发展开发480
分析需求分析(包括学习新技术)600

设计规格

生成设计文档120
设计回顾设计复审60
编码标准代码规范60
设计具体设计120
编码具体编码600
代码审查代码复审120
测试测试(自我测试,修改代码,提交修改)480
报告报告180
测试报告测试报告60
尺寸测量计算工作量60

尸检及过程

改进计划

事后总结,并提供过程改进计划180
 合计3060
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值