软件工程实践第二次作业

github地址: https://github.com/Kaloneme/sudoku.git

解题思路分析

刚刚接触到这个题目的时候,就想到了二维数组跟回溯法(虽然不是很熟练),但是从一开始我对数独有个误区,就是宫中不能重复这个问题,我的认知中9x9的数独表中不只有九个宫(之前没了解清楚),而是每9个在一起的格子就是一个宫,这无疑给我添加了巨大的麻烦。后来在跟同学探讨的过程中,他告诉我就只有9个宫,让我的心情一下子放松下来(虽然后面才知道原来代码不是最难过的)。
然后我的思路是先分开宫,然后一行一行填,一个一个填过去,每填一个就检查一次该行、该列、该宫是否有这个数字,要是遇到死胡同,就利用回溯法退回重新。

设计过程

语言:c、c++

1.标记函数与初始数独表

int table[11][11];        //数独表 
	int palace[11][11];      //宫标记 
	int line[11][11];       //行标记 
	int list[11][11];      //列标记
	int  seat[11][11];   //该位置所属的宫 
  • table是初始表,而palace、line、list都是标记位,seat是点(i,j)位置所属的宫。设计的过程中很容易把数组的值到底表示的是什么搞混了,写着写着就突然混了一下概念,所以我认为之前在纸上的准备很重要,随时可以让你回归最初的概念,比较好分清楚这些东西。

2.初始化函数(给每个位置标记上该位置所属的宫)

shudu::shudu()
{
	memset(table, 0, sizeof(table));
	memset(line, 0, sizeof(line));
	memset(list, 0, sizeof(list));
	memset(palace, 0, sizeof(palace));
	
	flag = false;
	sum = 0;
	for (int i = 1; i <= 9; i++)
	{
		for (int j = 1; j <= 9; j++)
		{
			int row = (i - 1) / 3;
			int cow = (j - 1) / 3;
			seat[i][j] = row + cow * 3 + 1;     //标记宫 
		}
	}
	
}
  • 最初的想法是把宫分类,然而怎么把分好类的宫与每个位置对应上呢?要是对应上了,这样对于宫是否重复的判断就提供了极大的便利,最后就有了seat二维数组,并且有了这个推算每个位置所属的宫的算式,方便了之后的宫判断,极大的简化了代码的复杂度,顺便把初始表table与其他标记数组初始化。

3.生成数组表(fillNumber()函数)

/*
 利用fillNumber算法,从第二个填起,生成1-9的随机数,每填一个都检查一次是否与
 所填的位置的宫、行、列有重复,没有重复就继续,若有重复则将此数加1继续尝试,
 填到最后一位时若不符合则逐一退回,直到完成数独表。 
*/ 
void shudu::fillNumber(int x, int y)
{
	if (flag)return;
	if (x < 9 || y < 9){
		int a = x;
		int b = y + 1;
		if (b>9)
		{
			b = 1;
			a++;
		}
		int p = seat[a][b];        //p来表示现在所属的宫
		int them = rand() % 9 + 1;   //产生随机数 
		int k = them;
		while (1){
			if (!line[a][k] && !list[b][k] && !palace[p][k]){        //若没出现过则都为0 
				table[a][b] = k, line[a][k] = 1, list[b][k] = 1, palace[p][k] = 1;
				fillNumber(a, b);
				table[a][b] = 0, line[a][k] = 0, list[b][k] = 0, palace[p][k] = 0;
			}
			k++;
			if (k == 10)k = 1;
			if (them == k)break;	           //退回 
		}	
	}
	else{
		sum++;
		int i, j;
		for (i = 1; i <= 9; i++){
			for (j = 1; j <= 9; j++){
				cout << table[i][j];
				if (j < 9)cout << " ";
			}
			cout << endl;
		}
		if (sum != n)cout << endl;
		if (sum == n)flag = true;              //输出n个数独表后结束 
	}
}
  • 这里利用fillNumber函数生成整个数独表,在这里,这些用于标记的数组派上了很大的用场。刚开始对于回溯法其实还不是很顺手,在同学的帮助下,才能是顺利的写出了这个算法。不过最后还是遇到一些问题,通过不断插入输出的办法,终于找到问题所在(个人感觉这办法还是很好用的)。不过在这个过程中我了解到自己的薄弱点的所在,虽然知道用这种方法,但是却用不熟悉,还是同学的帮助让我完成了,所以今后,一定要加大码量,加强练习。

性能测试分析

如下图:

  • 这是用cout输出的结果,数组table使用int型,却用了快要两分钟,cpu测试达到了100000。

  • 这是用putchar输出的结果(听同学说用这个会比较快),数组table使用char类型,只用了29秒,cpu测试达到14000+,这也差太多了,有点搞不懂。

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

总结

  • 这次的作业真是突如其来,让我的暑假如此收尾,跟期末一般。说到这次的作业,代码倒不是很难,但是后续的一些工作简直让人发疯,都没有接触过,要下好多插件,最气的是下完后还是没成功。
    总的来说还是对这些工具不熟悉,有些是见过没用过,有些是没见过没用过,总之就是很生疏。有点哀伤。
posted @ 2017-09-10 16:43  Kaloneme  阅读(140)  评论(4编辑  收藏  举报