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這樣才能不斷突破自己!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM