软件工程实践2019第三次作业

一、Github地址

二、PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟 实际耗时(分钟)
Planning 计划 30 45
-Estimate -估计这个任务需要多少时间 30 45
Development 开发 1740 2400
-Analysis -需求分析(包括学习新技术) 360 480
-Design Spec -生成设计文档 30 30
-Design Review -设计复审 10 10
-Coding Standard -代码规范(为目前的开发制定合适的规范) 20 20
-Design -具体设计 30 40
-Coding -具体编码 960 1200
-Code Review -代码复审 30 20
-Test -测试 300 600
Reporting 报告 50 75
-Test Repor -测试报告 20 30
-Size Measurement -计算工作 20 30
-Postmortem & Process Improvement Plan -事后总结, 并提出过程改进计划 10 15
合计 1820 2520
三、计算模块接口的设计与实现过程

(1)思路历程
      打开这次作业的时候,首先,没玩过数独的我一点思路也没有,一连串的“命令行参数”、“文件输入”、“文件输入”、“Github源代码管理”也直接把我整懵了。原本想直接右上角逃避,但一想到这个作业要按时提交,又觉得这是个锻炼自己实践能力和意志力的好机会,我还是硬着头皮地开始计划了。
      因为暑假的时候自学了一点JAVA,同时我的第二次作业写的也是JAVA学习计划,头铁的我决定用JAVA来做这次作业。前几天我花了大量的时间把JAVA的基础知识过了一遍,后面几天则是一边学新东西一边硬刚代码。我先从网上搜了一些有关数独的博客,发现有很多解数独的方法,其中用到最多的就是回溯法了,然后又在JAVA的书籍上(一开始看的《Thinking in Java》,后来发现实在看不懂,只好换成《Java从入门到精通》)快速地过一遍I/O系统、异常处理、数组、字符串等内容,命令行参数则是通过助教发在群里的代码才弄懂的。

(2)代码实现
      我的代码用了一个类,定义了两个方法:backTrace方法用来解数独,loadArags方法用来解析命令行参数。在主方法中,先解析参数,接着从输入文件中读取数独到二维数组,再用三个boolen类型把数独划分出行、列、宫,然后解数独,最后输出答案到输出文件。如此循环这个过程,就能重复地读→解→写,直到把所有数独都解完。
      代码如下:

1.读文件和写文件部分

public static void main(String[] args) {	
		loadArgs(args);
		File file = new File(inputFilename);	
		int a[][]=new int[m][m];
		//从输入文件中读取数独到二维数组
		try {			
			FileReader fr = new FileReader(file);	
			BufferedReader bufr = new BufferedReader(new FileReader(file));	
		    PrintWriter out = new PrintWriter(outputFilename);
			String line = null;	
			int q=0,w=0;int e=0;
			while((line=bufr.readLine())!=null) {
				int k = 0;
				 if(!line.equals(""))
				 { 
						 e++;
						 String[] s = line.split(" ");
					 for(w=0;w<m;w++)
					 {
						 a[q][w]=Integer.parseInt(s[w]);
					 }
					 q++; 
				 }
				 if(e==m)
				 {
					 //加载数独游戏
					boolean[][] rows = new boolean[m][m];	
				    boolean[][] cols = new boolean[m][m];	
				    boolean[][] blocks = new boolean[m][m];	 
				    for (int i = 0; i < m; i++) {
						for (int j = 0; j < m; j++) {
							if(a[i][j]!=0){
								if(m==4) {
									k=i/2*2+j/2;
								}
								if(m==6) {
									k=i/2*2+j/3;
								}
								if(m==8) {
									k=i/4*4+j/2;
								}
								if(m==9) {
									k=i/3*3+j/3;
								}
								int val=a[i][j]-1;
								  rows[i][val] = true;
						          cols[j][val] = true;
						          blocks[k][val] = true;
							}
						}
				    }
				    	//数独游戏加载完毕后调用backTrace方法
				    backTrace(a, m,cols, rows, blocks);
				    
				    	//输出解完的数独到输出文件
				    	for (int i = 0; i < m; i++) {
							for (int j = 0; j < m-1; j++) {
								out.print(a[i][j]+" ");
							}
							out.print(a[i][m-1]+"\r\n");
					    }
						out.print("\r\n");
				    q=0;e=0;
				 }			 
			}			
			fr.close();
			bufr.close();
			out.close();
			}catch (IOException e) {
				e.printStackTrace();
			}
}

2.命令行参数的解析

        // 初始化命令行参数	
public static String inputFilename;
	public static String outputFilename;
	public static int m;
	public static int n;
	
		//	解析命令行参数	
    public static void loadArgs(String args[]){
    	if(args.length>0&&args!=null){
    		for(int i=0;i<args.length;i++){
    			switch (args[i]) {
				case "-i":
					inputFilename  =   args[++i];
					break;
				case "-o": 
					outputFilename =   args[++i];
					break;
				case "-m": 
					m=Integer.valueOf(args[++i]);
					break;
				case "-n":
					n=Integer.valueOf(args[++i]);
				    break;
				default  :
					break;
				}
    		}
    	}
    }

3.数独算法

public static boolean backTrace(int[][] a,int g,boolean[][] cols,boolean[][] rows,boolean[][] blocks) {
		int k = 0 ;
		for (int i = 0; i < g; i++) {
			for (int j = 0; j < g; j++) {
				if(a[i][j]==0){
					//不考虑宫的情况		
					if(g==3||g==5||g==7) {
						//从数字1到阶数判断是否在行列中唯一
						for (int l = 0; l < g; l++) {
							if(!cols[j][l]&&!rows[i][l]){
								   rows[i][l] = cols[j][l] = true;
								   //若满足唯一,则赋值,然后继续深度遍历
			                        a[i][j] = 1 + l;
			                        if(backTrace(a, g,cols, rows, blocks)) return true;
			                        //不满足条件则回溯
			                        rows[i][l] = cols[j][l] = false;
			                        a[i][j] = 0;  
							}
						}
					}
					//考虑宫的情况
					else {
						//按照不同的阶数划分宫
						if(g==4) {
							k=i/2*2+j/2;
						}
						if(g==6) {
							k=i/2*2+j/3;
						}
						if(g==8) {
							k=i/4*4+j/2;
						}
						if(g==9) {
							k=i/3*3+j/3;
						}
						//从数字1到阶数判断是否在行列宫中唯一
						for (int l = 0; l < g; l++) {
							if(!cols[j][l]&&!rows[i][l]&&!blocks[k][l]){
								   rows[i][l] = cols[j][l] = blocks[k][l] = true;
								   //若满足唯一,则赋值,然后继续深度遍历
			                        a[i][j] = 1 + l;
			                        if(backTrace(a, g,cols, rows, blocks)) return true;
			                      //不满足条件则回溯
			                        rows[i][l] = cols[j][l] = blocks[k][l] = false;
			                        a[i][j] = 0;  
							}
						}
					}

					return false;
				}
			}
		}
		return true;
	}

(3)算法的关键
      这个算法是我是从网上各种博客上看了很多资料结合自己的理解找到的个人感觉最好的算法吧,因为它把检查填入数字的唯一性和回溯结合在一起。输入输出的方法也较为繁琐。这次要从三宫格做到九宫格我觉得也是一个难点,因为网上的资料不是三宫格就是九宫格,只能靠自己慢慢的转化然后在代码中加入判断宫格的部分。最后,可能是我代码的局限性,助教在群里发的例子都是两个数字间有两个空格,而题目中写了只有一个空格,以至于在测试时一直出错,浪费掉不少的时间!

四、性能改进

      我用了JProfile来进行性能分析,这是我又一个从零开始学的东西,搞了一下午才搞出结果,但分析的结果看的不是很明白,个人理解应该就是每个方法占用的资源吧。

五、单元测试

三阶

四阶

五阶

六阶

七阶

八阶

九阶

六、总结与思考

      写到这其实我已经头昏眼花了,这几天每天从早到晚都坐在电脑前自学,查资料,反复的改代码,找BUG……只能说是自己以前的不努力给自己挖了很大的一个坑,但如果没有这门科目的作业,没有这个deadline,相信我也不能在短时间内学到这么多东西。很庆幸我算是勉强完成了这次作业,也在自己JAVA的学习计划中又往前进了一小小步。接下来的路,希望自己保持这股拼劲,把每一周都当做是期末考试周来对待。

posted @ 2019-09-25 15:45  nevermores  阅读(284)  评论(4编辑  收藏  举报