2020软件工程03

软件工程作业03

软件工程 https://edu.cnblogs.com/campus/zswxy/software-engineering-2017-1
作业要求 https://edu.cnblogs.com/campus/zswxy/software-engineering-2017-1/homework/10494
作业目标 用c++或java编程实现数独填充
作业正文 见下文
参考文献

1、Github项目地址

https://github.com/20177711LBS

2、PSP表格

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

3、思路描述

拿到题目的时候其实没有看懂到底要求做什么,对于命令行传入参数也是一无所知,在群里面询问大佬们,了解命令行如何传参之后,才正式开始构思如何求解九宫格盘面,好在自己平时也喜欢玩数独,给我一个九宫格的盘面30分钟不到就能解完,可如今要自己来手写代码,让代码来解读,这到难倒我了,以自己目前的水平和知识面,写完估计的要300分钟吧!废话不多说了,先讲讲自己的思路吧:首先我们得知道3-9宫格最终盘面里每个数字所应满足的要求:

三宫格:盘面是3*3。使1-3每个数字在每一行、每一列中都只出现一次,不考虑宫;
四宫格:盘面是2*2四个宫,每一宫又分为2*2四个小格。使1-4每个数字在每一行、每一列和每一宫中都只出现一次;
五宫格:盘面是5*5。使1-5每个数字在每一行、每一列中都只出现一次,不考虑宫;
六宫格:盘面是2*3六个宫,每一宫又分为3*2六个小格。使1-6每个数字在每一行、每一列和每一宫中都只出现一次;
七宫格:盘面是7*7。使1-7每个数字在每一行、每一列中都只出现一次,不考虑宫;
八宫格:盘面是4*2八个宫,每一宫又分为2*4八个小格。使1-8每个数字在每一行、每一列和每一宫中都只出现一次;
九宫格:盘面是3*3九个宫,每一宫又分为3*3九个小格。使1-9每个数字在每一行、每一列和每一宫中都只出现一次

根据这个要求写一个方法legal,以判断在九宫格中的坐标(x,y)的位置上插入value,是否符合上述规则,代码如下

    public static Boolean legal(int a[][],int x, int y, int value,int m) {
 
        for (int i = 0; i < m; i++) {
            //如果列中有value,则返回false
            if (i != x && a[i][y] == value) {
                return false;
            }
            //如果行中有value,则返回false
            if (i != y && a[x][i] == value) {
                return false;
            }
        }
        if(m==9){
            //(minX,minY)是(x,y)所属小九宫格的左上角的坐标
            int minX = x / 3 * 3;
            int minY = y / 3 * 3;
     
            for (int i = minX; i < minX + 3; i++) {
                for (int j = minY; j < minY + 3; j++) {
                    //如果小九宫格中的非(x,y)的坐标上的值为value,返回false
                    if (i != x && j != y && a[i][j] == value) {
                        return false;
                    }
                }
            }
        }
        if(m==4){
            //(minX,minY)是(x,y)所属小4宫格的左上角的坐标
            int minX = x / 2 * 2;
            int minY = y / 2 * 2;

            for (int i = minX; i < minX + 2; i++) {
                for (int j = minY; j < minY + 2; j++) {
                    //如果小九宫格中的非(x,y)的坐标上的值为value,返回false
                    if (i != x && j != y && a[i][j] == value) {
                        return false;
                    }
                }
            }
        }
        if(m==8){
            //(minX,minY)是(x,y)所属小8宫格的左上角的坐标
            int minX = x / 4 * 4;
            int minY = y / 2 * 2;
     
            for (int i = minX; i < minX + 4; i++) {
                for (int j = minY; j < minY + 2; j++) {
                    //如果小九宫格中的非(x,y)的坐标上的值为value,返回false
                    if (i != x && j != y && a[i][j] == value) {
                        return false;
                    }
                }
            }
        }
        if(m==6){
            //(minX,minY)是(x,y)所属小6宫格的左上角的坐标
            int minX = x / 2 * 2;
            int minY = y / 3 * 3;
     
            for (int i = minX; i < minX + 2; i++) {
                for (int j = minY; j < minY + 3; j++) {
                    //如果小九宫格中的非(x,y)的坐标上的值为value,返回false
                    if (i != x && j != y && a[i][j] == value) {
                        return false;
                    }
                }
            }
        }
        return true;
    }

legal方法写完之后,并没有结束,求解九宫格的核心思想让我为之思考了一整天,首先想到的是按照平时玩数独的思维来解答:也就是自己常用的排除法,先将每行每列每个宫里面不可能出现的数字排除掉,然后将一些确定的数字填上去,然后再排除,再填......显然这种方法就是没脑子的人才会想的出来的,写完估计都猴年马月了,于是去询问ACM的算法大佬,提示了我一下,让我使用回溯法,刚提完,我瞬间“柳暗花明又一村”,马上有了思路:

public static void shuDu_solution(int k,int m) throws IOException {
        if (k == (m*m)) {
          String src= "D:\\sudoku\\"+outputFilename;
            try{
            FileWriter fw = new FileWriter(src,true);
            for(int i=0;i<m;i++){
                for(int j=0;j<m;j++){ 
                	fw.write(shuDu[i][j]+" ");

                }

                fw.write("\r\n");
            }
            fw.write("\r\n");
            fw.close(); // 最后记得关闭文件  
            }
            catch (Exception e) {  
                e.printStackTrace();  
            }  
            return;
        }
        int x = k / m;
        int y = k % m;
        if (shuDu[x][y] == 0) {
            for (int i = 1; i <= m; i++) {
                shuDu[x][y] = i;
                if (legal(shuDu,x, y, i,m)) {
                    shuDu_solution(k + 1,m);
                }
            }
            shuDu[x][y] = 0;
        } else {
            shuDu_solution(k + 1,m);
        }
    }

初始化命令行的传入的参数

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;
				}
    		}
    	}
    }

最后就是主函数

public static void main(String[] args) throws IOException {
    	loadArgs(args);
    	int generateShuDu[][]=new int[10][10];   
		File myFile = new File("D:\\sudoku",inputFilename);
		Reader reader = new InputStreamReader(new FileInputStream(myFile),"UTF-8"); 
		int tempchar;  int i=0; int j=0;
		    while ((tempchar = reader.read()) != -1) {  
		    if ( (((char) tempchar) != '\n') &&(((char) tempchar) != ' ')) {  
		        if(i<m){
		        	if(j<m){
		        		if(tempchar!=13){
		        			generateShuDu[i][j]=((char) tempchar)-48;
			        		j++;
		        		}
		        	}else{	
		        		i++;
		        		j=0;
		        		generateShuDu[i][j]=((char) tempchar)-48;
		        	}
		        }
		        if(i==m){
		        	if(n!=0){
			            setShuDu(generateShuDu);
			            shuDu_solution(0,m);
			            n--;
			            i=0;j=0;
		        	}
		        	
		        }
		    }  
		}
		reader.close();
    }   

遇到的问题

FileWriter fw = new FileWriter("c.txt",true);
fw.write("hello");
fw.close();

异常处理:主要是对文件的读取进行异常处理



单元测试样例:

最后总结一下:

用java开发感觉if-else流程语句和一些基本语法没有太大的问题,主要是一些常用的类(文件处理类,IO流)使用还不太熟练,导致开发效率低,中途遇到各种各样的bug,以至于气的连晚饭都不想吃了,但自己还是坚持做完了,bug也解决了;除此之外,编辑器的使用也不太熟练,一些类似于命令行传参的细节性的问题也不懂,不过现在懂了,也算是一份收获吧,另外,学会了在github上面上传文件和文件夹,对于github的使用有了初步的认识,希望自己在下次的项目开发中有新的收获,遇到bug不要难过,要勇于挑战bug这样才能不断突破自己!

posted @ 2020-04-02 19:29  刘博殊  阅读(162)  评论(1编辑  收藏  举报