个人项目作业

1.github项目地址

https://github.com/easylliu/gitlearning/tree/master/Sudoku

2.开发时间

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 30 50
Estimate 估计这个任务需要多少时间 10 15
Development 开发
Analysis 需求分析 (包括学习新技术) 120 180
Design Spec 生成设计文档
Design Review 设计复审 (和同事审核设计文档)
Coding Standard 代码规范 (为目前的开发制定合适的规范) 30
Design 具体设计 30 60
Coding 具体编码 120 240
Code Review 代码复审 60 120
Test 测试(自我测试,修改代码,提交修改) 120 180
Reporting 报告
Test Report 测试报告 30 50
Size Measurement 计算工作量 10 20
Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 30 30
合计 560 975

3.解题思路描述

(1).数独终局生成

开始在看到题目时想到的是通过初始点一直往后延伸推导,后来想了一会感觉有点复杂;在思考无果后通过上网查找资料了解到生成终局的两种方法
随机法,矩阵转换法;但相比之下感觉矩阵转换法更加直接,随机法不确定性加大了难度;而由于第一个数已经确定为9,因此第一行第一列不能参与变换,这样通过一个数独变换能产生共计5184种;但我们的要求最多有1000000种,因此想产生200个种子数独,从8个数中取出四位,共有70种取法,而每种取法又对应三种数字交换方式共计210种符合要求,这样产生的数独数就已经超过1000000种符合要求了。

(2).求解数独

在求解数独上,开始自己思考是想通过那些已确定的那些数字出发,沿着他们所在行列块进行推导,但随后感觉有点困难;通过网上查找感觉方法差不多,相对较为直接的就是回溯法求解了,从最开始点一直递归遍历找到所有存在的可能性,在通过行列和3*3块的验证来确定是否符合条件,简单粗暴,在实现上较为简单,初始计划打算就使用了这种方法。

4.设计实现过程

(1).数独终局生成

根据思路想到,首先产生的一个种子出发,首先通过其行列变换最多产生5184个数独,如果还不足的话从那70种取法中随机选出先交换数字产生新的种子数独,然后再行列变换,如此种种,在一定程度上还可以保证随机性,直到产生够所要的数独数即可结束。通过这种思路构建函数也就清晰起来,在数独产生的函数中通过种子数独不断调用矩阵变换函数,然后可将矩阵变换函数拆解成行变换函数和列变换函数,如此这样,函数之间的联系清晰明朗,实现起来目标也很明确。总得来说在这一部分产生4个函数,其直接的关系也是层层调用。通过这样的分析对函数的作用了解也清晰许多,通过他们直接的联系和他们的用途设计单元测试就比较轻松了。

(2).求解数独

通过暴力解法,对从0到81的所有空白进行1到9的选择,然后判断适合符合行列和块的要求,然后递归往下延伸,当后面推不出来递归返回继续延伸推导,直到求解出正确的结果结束。
相对来说这种接法也相对较为直接,从0到81,先填入数字,在判断是否符合行列和块的要求,然后递归向后延伸。如此这样分析,相对复杂点的就是递归的使用,想都来说3个函数足以。主函数调用填空递归函数,递归函数在填入数字后调用检查函数判断是否符合行列块的要求,如此实现。然后相对来说,对递归的单元测试也相对有些麻烦,通过从对函数功能的理解出发,结合和其他函数的联系设计单元测试会更加全面。

5.性能改进

改进:由于产生数独和求解数独的最大值已到达1000000,在输出到文件和读取时的效率问题就显得尤为重要了,因此在这方面花费时间能减就减,因此我就从每次写入到文件的数量上做出了改进,再往文件里写入数独时,我改进到每次写入两百个数独,这样减少写入的次数,提高效率。
由图片中可以看到,调用次数最多的为检查行列块的合理性函数,这也是暴力求解产生的负面效果,其次在面对文件操作时话费的时间较多,仍待改善。

6.代码说明

主要分做两个部分,一个是产生终局数独,一个是求解数独。
这四个为生成终局数独的四个关键函数:
主要就是sudo_create产生种数独通过调用矩阵变换得到扩展的数独,矩阵变换由行变换和列变换组成;

    void sudo_create(int sudo_count, char sudo[9][10], char changenum_array[70][5]);		//生成数独
    int matrix_change(int count, int sudo_count, char sudo[9][10], char output[1000000]);	//矩阵变换
    void line_range(int init, int n, char mid_sudo[9][10], char sudo[9][10]);				//行交换
    void column_range(int init, int n, char mid_sudo[9][10], char sudo[9][10]);				//列变换

其中最为关键一个:产生种子,调用扩展

void sudo_create(int sudo_count, char sudo[9][10], char changenum_array[70][5]) {
	int index_changenum[70] = { 0 };	//是否随机到标识符	
	char sudo_middle[9][10];				//中间种子数独
	int randnum = 0, i, j;
	int count = 0;							//已生成的数独数
	char a, b, c, d;
	for (i = 0; i<9; i++) {									//初始化
		strcpy_s(sudo_middle[i], sudo[i]);
	}
	count = matrix_change(count, sudo_count, sudo_middle, output);	//根据一个种子转换输出
	while (count < sudo_count) {
		if (count == -1) break;
		randnum = rand() % 70 + 0;
		if (index_changenum[randnum] == 0) {				//未随机到
			a = changenum_array[randnum][0];
			b = changenum_array[randnum][1];
			c = changenum_array[randnum][2];
			d = changenum_array[randnum][3];
			for (i = 0; i < 9; i++) {
				for (j = 0; j < 9; j++) {
					if (sudo[i][j] == a) {
						sudo_middle[i][j] = b;
					}
					else if (sudo[i][j] == b) {
						sudo_middle[i][j] = a;
					}
					else if (sudo[i][j] == c) {
						sudo_middle[i][j] = d;
					}
					else if (sudo[i][j] == d) {
						sudo_middle[i][j] = c;
					}
					else
						sudo_middle[i][j] = sudo[i][j];
				}
			}
			count = matrix_change(count, sudo_count, sudo_middle, output);
			if (count == -1) break;

			for (i = 0; i < 9; i++) {
				for (j = 0; j < 9; j++) {
					if (sudo[i][j] == a) {
						sudo_middle[i][j] = c;
					}
					else if (sudo[i][j] == b) {
						sudo_middle[i][j] = d;
					}
					else if (sudo[i][j] == c) {
						sudo_middle[i][j] = a;
					}
					else if (sudo[i][j] == d) {
						sudo_middle[i][j] = b;
					}
					else
						sudo_middle[i][j] = sudo[i][j];
				}
			}
			count = matrix_change(count, sudo_count, sudo_middle, output);
			if (count == -1) break;
			for (i = 0; i < 9; i++) {
				for (j = 0; j < 9; j++) {
					if (sudo[i][j] == a) {
						sudo_middle[i][j] = d;
					}
					else if (sudo[i][j] == b) {
						sudo_middle[i][j] = c;
					}
					else if (sudo[i][j] == c) {
						sudo_middle[i][j] = b;
					}
					else if (sudo[i][j] == d) {
						sudo_middle[i][j] = a;
					}
					else
						sudo_middle[i][j] = sudo[i][j];
				}
			}
			count = matrix_change(count, sudo_count, sudo_middle, output);
			if (count == -1) break;
		}
	}
	//cout << output;
}

这三个为求解数独的主要函数:
主要就是solve_sudo函数调用填空进行递归,填入一个数后就判断其合理性

int solve_sudo(char output[], int count);//求解数独
bool solve_blank(int sudo_num);//填空
bool matrix_judge(int line, int column, int choose);//判断合理性

关键一个函数:递归调用,逐步展开

//填数
bool solve_blank(int sudo_num) {
	int line, column, i;
	line = sudo_num / 9;
	column = sudo_num % 9;
	if (sudo_num >= 81)
		return true;
	if (sudo1[line][column] == 0) {
		for (i = 1; i <= 9; i++) {
			sudo1[line][column] = i;
			if (matrix_judge(line, column, i)) {
				if (solve_blank(sudo_num + 1)) return true;
			}
			sudo1[line][column] = 0;
		}
	}
	else {
		return solve_blank(sudo_num + 1);
	}
	return false;
}
posted @ 2017-09-26 18:20  修炼爱情  阅读(201)  评论(2编辑  收藏  举报