fighting

Github地址

                 <tr align=center>
		<td>Estimate</td><td>这个任务需要多少时间</td><td>24小时</td><td>36小时</td>		
	</tr>
                               <tr align=center>
		<td>Development</td><td>开发</td></td><td>8小时</td><td>12小时</td>		
	</tr>
                               <tr align=center>
		<td>Analysis</td><td>需求分析(包括学习新技术)</td><td>2小时</td><td>3小时</td>		
	</tr>
                               <tr align=center>
		<td>Design Spec</td><td>生成设计文档</td></td><td>2小时</td><td>4小时</td>		
	</tr>
                               <tr align=center>
		<td>Design Review</td><td>设计复审</td></td><td>3小时</td><td>2小时</td>		
	</tr>
                               <tr align=center>
		<td>Coding Standard</td><td>代码规范</td></td><td>1小时</td><td>3小时</td>		
	</tr>
                               <tr align=center>
		<td>Design</td><td>具体设计</td></td><td>1小时</td><td>40分钟</td>		
	</tr>
                               <tr align=center>
		<td>Coding</td><td>具体编码</td></td><td>6小时</td><td>4小时</td>		
	</tr>
	<tr align=center>
		<td>Code Review</td><td>代码复审</td></td><td>5小时</td><td>3小时</td>		
	</tr>
                 <tr align=center>
		<td>Test</td><td>测试(自我测试,修改代码,提交修改)</td><td>1小时</td><td>2小时</td>	
	</tr>
                               <tr align=center>
		<td>Reporting</td><td>报告</td></td><td>1小时</td><td>2小时</td>		
	</tr>
                               <tr align=center>
		<td>Test Repor</td><td>测试报告</td></td><td>1小时</td><td>2小时</td>		
	</tr>
                               <tr align=center>
		<td>Size Measurement</td><td>计算工作量</td></td><td>1小时</td><td> 1小时</td>		
	</tr>
                               <tr align=center>
		<td>Postmortem</td><td>事后总结,并提出过程改进计划</td></td><td>2小时</td><td> 1小时</td>		
	</tr>
                               <tr align=center>
		<td>Improvement Plan</td><td>过程改进计划</td></td><td>2小时</td><td> 2小时</td>		
	</tr>
                               <tr align=center>
		<td>合计</td><td></td></td><td> 38小时</td><td> 40小时40分钟</td>		
	</tr>
PSP表格
Personal Software Process Stages预估耗时实际耗时
Planning计划2小时1小时

需求

实现一个命令行程序,不妨称之为Sudoku

百度百科简介:

数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次,所以又称“九宫格”。

具体任务:

现在我们想一步一步来,完成从三宫格到九宫格的进阶;完成三宫格和其他博客任务,就算过了初级考核,其他的算升级。具体各阶规则如下:

输入:

输入文件名以命令行参数传入。例如我们在命令行窗口(cmd)中进入Sudoku.java所在文件的目录然后输入:

javac Sudoku.java
java Sudoku -m 9  -n 2  -i input.txt  -o output.txt
-m 宫格阶级(3~9的整数)
-n 待解答盘面数目
-i 指定输入文件(需要在自己的电脑磁盘里面提前创建好)
-o 指定程序的输出文件(也需要在自己的电脑里面提前创建好)

上面语句对应的输入文件如下:

0 0 8 0 0 4 0 0 9
0 6 0 0 0 0 1 0 0
0 3 7 0 0 0 0 0 0
8 0 1 2 6 9 0 0 3
0 2 5 4 7 0 0 6 8
0 9 0 0 0 5 0 0 0
9 0 0 1 5 2 3 7 4
0 4 0 3 9 8 0 1 6
1 5 3 6 4 7 8 0 2

2 0 0 0 0 0 0 0 0
0 0 6 3 0 0 0 7 0
5 0 0 0 0 0 1 0 0
9 6 7 4 0 0 0 0 5 
8 1 3 0 0 0 0 0 0
4 2 0 7 1 8 9 6 3 
3 5 0 0 4 1 6 9 7
6 9 8 2 7 3 5 4 1
0 4 0 0 5 9 2 0 8

输出

输出n个程序解出的盘面,每两个盘面间空一行,每个盘面中,每两个小格之间有一个空格。

上面的命令行对应的输出文件output.txt组织如下:

5 1 8 7 2 4 6 3 9
2 6 9 5 8 3 1 4 7
4 3 7 9 1 6 2 8 5
8 7 1 2 6 9 4 5 3
3 2 5 4 7 1 9 6 8
6 9 4 8 3 5 7 2 1 
9 8 6 1 5 2 3 7 4
7 4 2 3 9 8 5 1 6
1 5 3 6 4 7 8 9 2

2 7 9 1 8 4 3 5 6
1 8 6 3 2 5 4 7 9
5 3 4 9 6 7 1 8 2
9 6 7 4 3 2 8 1 5
8 1 3 5 9 6 7 2 4
4 2 5 7 1 8 9 6 3
3 5 2 8 4 1 6 9 7
6 9 8 2 7 3 5 4 1 
7 4 1 6 5 9 2 3 8

解题思路:

拿到题目的时候其实没有看懂到底要求做什么,对于命令行传入参数也是一无所知,在群里面询问大佬们,了解命令行如何传参之后,才正式开始构思如何求解九宫格盘面,好在自己平时也喜欢玩数独,给我一个九宫格的盘面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的算法大佬,提示了我一下,让我使用回溯法,刚提完,我瞬间“柳暗花明又一村”,马上有了思路:

具体代码和注释如下:

shuDu[][]是用来存放数独游戏的二维数组。

public static int shuDu[][] = new int[9][9];
    public static void setShuDu(int[][] shuDu) {
        Sudu.shuDu = shuDu;
    }

使用回溯法求解数独

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

遇到的问题(这个问题耽误了我6个小时左右):

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

文件写入建议用FileWriter

如果用BufferedWriter会导致多次写入时被覆盖!

String outfile="D:\\sudoku\\out.txt";
File writename = new File(outfile); // 相对路径,如果没有则要建立一个新的out.txt文件  
writename.createNewFile(); // 创建新文件  
BufferedWriter out = new BufferedWriter(new FileWriter(writename)); 
out.write(shuDu[i][j]+" ");

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




单元测试样例:

性能测试截图():



性能测试我使用的是jprofiler9.2,由于是刚开始接触这个插件,对其一些功能还不太熟悉,研究了一个晚上,还是看不懂数据,不知道类方法耗时在哪里体现,后面再花时间去摸索(仅仅下载这个插件和主程序,然后配置成功到运行,就花了一天时间,我太难了,关键是看不懂这个图上的数据)

最后总结一下:

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

posted @ 2019-09-20 15:38  凭实力闯荡  阅读(871)  评论(6编辑  收藏  举报