软件工程第三次作业
Github地址Github
PSP表格
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 25 |
Estimate | 估计这个任务需要多少时间 | 480 | 1000+ |
Development | 开发 | 90 | 120 |
Analysis | 需求分析 (包括学习新技术) | 100 | 80 |
Design spec | 生成设计文档 | 30 | 30 |
Design Review | 设计复审 | 20 | 30 |
Coding Standard | 代码规范(为目前的开发制定合适的规范) | 15 | 20 |
Design | 具体设计 | 45 | 60 |
Coding | 具体编码 | 150 | 240 |
Code Review | 代码复审(自我测试,修改代码,提交修改) | 60 | 150 |
Test | 测试 | 60 | 60 |
Reporting | 报告 | 30 | 45 |
Test Report | 测试报告 | 60 | 60 |
Size Measurement | 计算工作量 | 15 | 10 |
Postmortem & Process Improvement Plan | 事后总结,并提出过程修改计划 | 30 | 45 |
合计 |
作业历程
看完题目后,将其看作一个解数独类题目来看,思考后决定初步把这个程序分为两个类,一个类主要功能为输入输出,一个类主要功能为解数独,其中解数独的功能拆分为两种,一种为判断函数,用于判定合法性,并针对不同规格的数独采用不同的判断方式,另一种为深度优先搜索函数,用于进行数独的填充尝试。计算后完整的数独结果需要存储,用于在输出时表达,并用Java来实现(使用深度优先搜索算法)。
代码
解数独类,主要由判断函数和求解函数组成,采用深度优先搜索算法。
public class Sudo {
//用于接收数据的数组
public int [][] array = new int[9][9];
public void setArray(int [][] array){
this.array = array;
}
public void Sudo(int [][] array){
this.array = array;
}
判断函数针对了行、列以及宫并进行了合理性的判断。保证了每行、每列和每个宫不会出现重复的数字。例如m=6时,即六宫格的时候。利用x,y坐标对一系列变换,锁定了在哪一个宫,比如坐标(3,1)将会被定在(1,0)宫上,然后对宫内的所有位置遍历,出现重复的数字返回false。如果行,列,宫都是合理的就返回true。
//judge函数判断该数字是否可以填进该格
private boolean judge(int n,int x,int y,int m){
for(int i=0;i<m;i++){
if(array[x][i]==n)
return false;
if(array[i][y]==n)
return false;
}
if (m==4){//宫数为4时判断与该宫内的数字是否相同
for (int i = (x / 2) * 2; i < (x / 2 + 1) * 2; i++) {
for (int j = (y / 2) * 2; j < (y / 2 + 1) * 2; j++) {
if (array[i][j] == n)
return false;
}
}
}
if(m==6) {//宫数为6时判断与该宫内的数字是否相同
for (int i = (x / 2) * 2; i < (x / 2 + 1) * 2; i++) {
for (int j = (y / 3) * 3; j < (y / 3 + 1) * 3; j++) {
if (array[i][j] == n)
return false;
}
}
}
if(m==8) {
for (int i = (x / 4) * 4; i < (x / 4 + 1) * 4; i++) {
for (int j = (y / 2) * 2; j < (y / 2 + 1) * 2; j++) {
if (array[i][j] == n)
return false;
}
}
}
if(m==9) {
for (int i = (x / 3) * 3; i < (x / 3 + 1) * 3; i++) {
for (int j = (y / 3) * 3; j < (y / 3 + 1) * 3; j++) {
if (array[i][j] == n)
return false;
}
}
}
return true;
}
深度优先搜索函数,以二维方式展开搜索,每次都对下一位置进行判断,如果合法,就先填入,填入以后进行以纵坐标为先的下一次搜索,若出现无法填入则会回退,同时每次进行时都将用另一个数组存下当前的数组以提供以后的返回,i和j则是对位置的一个转换,方便了判断函数的判断。
//用于保存结果的数组
public int [][] outputArray = new int[9][9];
public int [][] getOutputArray(){
return outputArray;
}
//深度优先搜索函数
public void solve(int num,int m)
{
if(num==m*m){//将数组保存到另一数组上
for(int i=0; i<m; i++){
for(int j=0; j<m; j++)
outputArray[i][j]=array[i][j];
}
return ;
}
int i=num/m,j=num%m;
if(array[i][j]!=0) solve(num+1,m);
else{
for(int k=1; k<=m; k++) {
if(judge(k,i,j,m)) {//判断该数字是否可以填入该格
array[i][j]=k;
solve(num+1,m);
}
}
array[i][j]=0;
}
return ;
}
}
输入输出类
public static void main(String [] args) throws IOException{
char[] ch = args[1].toCharArray();
int m = ch[0]-48;
char[] chars = args[3].toCharArray();
int n = chars[0] - 48;
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader(new File(args[5])));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(new File(args[7])));
for (int l = 0; l < n; l++) {
for (int i = 0; i < m; i++) {
String s = bufferedReader.readLine();
String str = s.replaceAll(" ", "");
for (int j = 0; j < m; j++) {
char[] c = str.toCharArray();
array[i][j] = c[j] - 48;
}
}
//调用Sudo类求解数独
Sudo sudo = new Sudo();
sudo.setArray(array);
sudo.solve(0,m);
array = sudo.getOutputArray();
for (int i = 0; i < m; i++) {
for (int j = 0; j < m; j++) {
bufferedWriter.write(array[i][j] + " ");
}
bufferedWriter.write("\n");
}
bufferedWriter.write("\n");
String s = bufferedReader.readLine();
}
bufferedWriter.close();
}catch (IOException e){
return;
}
}
}
测试结果
(1)三宫格
(2)四宫格
(3)五宫格
(4)六宫格
(5)七宫格
(6)八宫格
(7)九宫格
输入参数
Code Quality Analysis工具的分析结果
总体上,判断函数使用的时长为1015微秒,而深搜函数的使用时间为1148微秒,两者没有差很远,尽管单次调用时长上判断函数仅仅为2微秒,而深搜函数为13微秒,但是被调用的次数上判断函数有300多次,而深搜只有80多次,可见每一次的深搜的算法时间复杂度较大而判断函数较小,并且深搜还存在多次调用判断函数的情况可见其复杂性,其瓶颈在于深搜本身的递归算法。
心得体会
最大体会就是和同学交流更容易懂,百度有时候虽然步骤给的十分的详细,但是冷冰冰文字实在是没有同学的言传身教来的容易懂,整个过程下来,总结就是要多动手,多找资料,不懂就要去问,不要因为脸皮薄就不好意思去问。并且学习了PSP,还有如何去运用性能测试。并且学会了如何使用Github的使用方法