今天繼續看《算法的樂趣》,學習了阿拉伯數字與中文數字的轉化。
漢字用零一二三四五六七八九作為基本計數,與阿拉伯數字靠數字偏移位置的權位不一樣。中文數字是才有“數字+權位”的方式組成數字,比方百,千,萬。
中文數字每一個數字后都會跟上一個權位。權位就是數字的量值,相當於阿拉伯數字的數位。
中文計數以萬為小節,萬下面沒有節權,萬之上是億為節權。
中文另一個特點是多變的“零”,大概總結為三個規則:
1.以10000為小節。小節的結尾即使是0,也不使用“零”。
2.小節內兩個非0數字之間要使用“零”。
3.當小節的“千”位是0時,若本小節的前一小節無其它數字。則不用“零”,否則就要用“零”。
算法設計:
首先定義三個字符串數組,存放數字位、節權位、權位
然后再分兩個方法實現,一個實現節權的操作處理,另一個實現小節內的操作。
第二個程序是實現中文數字轉化為阿拉伯數字,這個我看不懂書上的一小部分代碼。可是我讀懂了作者的思想。所以我自己寫了一個與書上不一樣的算法出來,可是思想是一樣的。
都是通過節權處分里小節,然后逐個小節計算,然后再合並得到結果。
以下是我的代碼:
</pre><pre name="code" class="java">package 阿拉伯數字與中文數字; public class Main { public static void main(String[] args) { Main ma = new Main(); Tool to = new Tool(); ma.initMain(); } public void initMain(){ testNumberToChinese(); System.out.println("————————————————————————————————————————"); testChineseToNumber(); } public void testNumberToChinese(){ NumberChangeToChinese numToChinese = new NumberChangeToChinese(); System.out.println("0:"+numToChinese.numberToChinese(0)); System.out.println("1:"+numToChinese.numberToChinese(1)); System.out.println("2:"+numToChinese.numberToChinese(2)); System.out.println("3:"+numToChinese.numberToChinese(3)); System.out.println("4:"+numToChinese.numberToChinese(4)); System.out.println("5:"+numToChinese.numberToChinese(5)); System.out.println("6:"+numToChinese.numberToChinese(6)); System.out.println("7:"+numToChinese.numberToChinese(7)); System.out.println("8:"+numToChinese.numberToChinese(8)); System.out.println("9:"+numToChinese.numberToChinese(9)); System.out.println("10:"+numToChinese.numberToChinese(10)); System.out.println("11:"+numToChinese.numberToChinese(11)); System.out.println("110:"+numToChinese.numberToChinese(110)); System.out.println("111:"+numToChinese.numberToChinese(111)); System.out.println("100:"+numToChinese.numberToChinese(100)); System.out.println("102:"+numToChinese.numberToChinese(102)); System.out.println("1020:"+numToChinese.numberToChinese(1020)); System.out.println("1001:"+numToChinese.numberToChinese(1001)); System.out.println("1015:"+numToChinese.numberToChinese(1015)); System.out.println("1000:"+numToChinese.numberToChinese(1000)); System.out.println("10000:"+numToChinese.numberToChinese(10000)); System.out.println("20010"+numToChinese.numberToChinese(20010)); System.out.println("20001"+numToChinese.numberToChinese(20001)); System.out.println("100000:"+numToChinese.numberToChinese(100000)); System.out.println("1000000:"+numToChinese.numberToChinese(1000000)); System.out.println("10000000"+numToChinese.numberToChinese(10000000)); System.out.println("100000000:"+numToChinese.numberToChinese(100000000)); System.out.println("1000000000"+numToChinese.numberToChinese(1000000000)); System.out.println("2000105"+numToChinese.numberToChinese(2000105)); System.out.println("20001007:"+numToChinese.numberToChinese(20001007)); System.out.println("2005010010:"+numToChinese.numberToChinese(2005010010)); } public void testChineseToNumber(){ ChineseChangeToNumber chineseToNumber = new ChineseChangeToNumber(); System.out.println("二十億零五千五百零一萬四千零一十:"+chineseToNumber.ChineseToNumber("二十億零五千五百零一萬四千零一十")); System.out.println("二千萬一千零七:"+chineseToNumber.ChineseToNumber("二千萬一千零七")); System.out.println("二萬零一:"+chineseToNumber.ChineseToNumber("二萬零一")); System.out.println("二萬零一十:"+chineseToNumber.ChineseToNumber("二萬零一十")); System.out.println("一萬:"+chineseToNumber.ChineseToNumber("一萬")); System.out.println("一千零一十五:"+chineseToNumber.ChineseToNumber("一千零一十五")); System.out.println("一千:"+chineseToNumber.ChineseToNumber("一千")); System.out.println("一億:"+chineseToNumber.ChineseToNumber("一億")); } }
這是主類,用於啟動程序和測試。
package 阿拉伯數字與中文數字; public class NumberChangeToChinese { public String numberToChinese(int num){//轉化一個阿拉伯數字為中文字符串 if(num == 0){ return "零"; } int unitPos = 0;//節權位標識 String All = new String(); String chineseNum = new String();//中文數字字符串 boolean needZero = false;//下一小結是否須要補零 String strIns = new String(); while(num>0){ int section = num%10000;//取最后面的那一個小節 if(needZero){//推斷上一小節千位是否為零。為零就要加上零 All = Tool.chnNumChar[0] + All; } chineseNum = sectionTOChinese(section,chineseNum);//處理當前小節的數字,然后用chineseNum記錄當前小節數字 if( section!=0 ){//此處用if else 選擇語句來運行加節權位 strIns = Tool.chnUnitSection[unitPos];//當小節不為0。就加上節權位 chineseNum = chineseNum + strIns; }else{ strIns = Tool.chnUnitSection[0];//否則不用加 chineseNum = strIns + chineseNum; } All = chineseNum+All; chineseNum = ""; needZero = (section<1000) && (section>0); num = num/10000; unitPos++; } return All; } public String sectionTOChinese(int section,String chineseNum){ String setionChinese = new String();//小節部分用獨立函數操作 int unitPos = 0;//小節內部的權值計數器 boolean zero = true;//小節內部的制零推斷。每一個小節內僅僅能出現一個零 while(section>0){ int v = section%10;//取當前最末位的值 if(v == 0){ if( !zero ){ zero = true;//須要補零的操作。確保對連續多個零僅僅是輸出一個 chineseNum = Tool.chnNumChar[0] + chineseNum; } }else{ zero = false;//有非零的數字。就把制零開關打開 setionChinese = Tool.chnNumChar[v];//相應中文數字位 setionChinese = setionChinese + Tool.chnUnitChar[unitPos];//相應中文權位 chineseNum = setionChinese + chineseNum; } unitPos++; section = section/10; } return chineseNum; } }
這第二個是阿拉伯數字轉化成中文數字的類,實現的過程大致跟書上的一樣。
以下是第三個中文轉化成阿拉伯數組的類:
package 阿拉伯數字與中文數字; public class ChineseChangeToNumber { public int ChineseToNumber(String str){ String str1 = new String(); String str2 = new String(); String str3 = new String(); int k = 0; boolean dealflag = true; for(int i=0;i<str.length();i++){//先把字符串中的“零”除去 if('零' == (str.charAt(i))){ str = str.substring(0, i) + str.substring(i+1); } } String chineseNum = str; for(int i=0;i<chineseNum.length();i++){ if(chineseNum.charAt(i) == '億'){ str1 = chineseNum.substring(0,i);//截取億前面的數字,逐個對比表格。然后轉換 k = i+1; dealflag = false;//已經處理 } if(chineseNum.charAt(i) == '萬'){ str2 = chineseNum.substring(k,i); str3 = str.substring(i+1); dealflag = false;//已經處理 } } if(dealflag){//假設沒有處理 str3 = chineseNum; } int result = sectionChinese(str1) * 100000000 + sectionChinese(str2) * 10000 + sectionChinese(str3); return result; } public int sectionChinese(String str){ int value = 0; int sectionNum = 0; for(int i=0;i<str.length();i++){ int v = (int) Tool.intList.get(str.charAt(i)); if( v == 10 || v == 100 || v == 1000 ){//假設數值是權位則相乘 sectionNum = v * sectionNum; value = value + sectionNum; }else if(i == str.length()-1){ value = value + v; }else{ sectionNum = v; } } return value; } }
這真真正正是我自己一個個字寫的,都是通過思考得出的結果,可能有些不嚴謹,可是確實能夠實現。
第四個是工具類,存放字符串數據用的。
package 阿拉伯數字與中文數字; import java.util.ArrayList; import java.util.HashMap; import java.util.Set; public class Tool { //數字位 public static String[] chnNumChar = {"零","一","二","三","四","五","六","七","八","九"}; public static char[] chnNumChinese = {'零','一','二','三','四','五','六','七','八','九'}; //節權位 public static String[] chnUnitSection = {"","萬","億","萬億"}; //權位 public static String[] chnUnitChar = {"","十","百","千"}; public static HashMap intList = new HashMap(); static{ for(int i=0;i<chnNumChar.length;i++){ intList.put(chnNumChinese[i], i); } intList.put('十',10); intList.put('百',100); intList.put('千', 1000); } }
以下的圖片是我的測試結果:
第一幅圖是阿拉伯數字轉化成中文:
第二張圖片是中文數字轉化成阿拉伯數字的:
這個數字轉化算法,我看了幾個小時才看懂一點點,然后在編程的時候,遇到了諸多問題,可是我最后都是獨自一個個攻克了,沒有問別人,遇到了一些基本數據類型的問題,比方int型不能超過21億。" "兩個點的是字符串類型,' '一個點的是字符類型。這個問題我在傳入HashMap隊列中的時候就卡得我非常煩躁,傳入的字符串類型。然后用字符串的CharAt()去調用,結果當然是出現異常錯誤。無法編譯了。
還實用了一個沒有必要的二重循環去檢測,事實上沒有必要,隊列有現成的方法讓我去用。二重循環中還出現了死循環。在第二重里寫錯成了i++(事實上應該是j++),導致循環不斷。
算法學習之路不易,可是貴在堅持,我在編程這幾個小時里經常想不出解決方式。遇到bug無法解決,可是又沒有人問。
可是沒關系,我堅持了下來,攻克了我遇到的一切問題。在算法這條路上再前進了一小步,我相信我以后的IT之路一定能夠越走越遠,越走越寬。