第一題是要求實現一個自動生成小學生加減乘除四則運算題目的程序。後面可以將它擴展為網站或安卓應用或IOS應用或win10應用。
我的思路比較簡單。環境是Java JDK1.8;IDE為Intellij社區版。
首先,我們不考慮有括號的情形,那麼符號只有+、-、*、/四種;涉及到的數包含兩種:整數或分數。
1. 我們注意到,一個運算式中,總有“數的個數比運算符多1”的規則。於是,我們自然地想到創建一個固定長度為n的數組number存放數字,每個數都能隨機生成,通過maxOfNumber變量控制式子中出現的最大數字。創建一個長度為n-1的數組character存放運算符號。
2. 接下來需要初始化數組number和character。number數組每個元素靠maxOfNumber控制最大數、minOfNumber控制最小數、hasFraction控制是否出現分數隨機生成;character數組每個元素靠type變量控制只有加減、只有乘除、加減乘除都有的3種類型隨機生成。
例如:n為5,minOfNumber為0,maxOfNumber為10,hasFraction為出現分數,type為只有加減的類型:5/7+8/7-1/7-2/7
3. 接下來就是結果的計算。對於純整數,計算結果是簡單的。通過查找資料,我發現純整數字符串的計算依靠ScriptEngine類即可完成。函數使用示例如下:
1 //整型表達式的正確計算結果 2 public String calIntResult() { 3 ScriptEngine se = new ScriptEngineManager().getEngineByName("JavaScript"); 4 try { 5 Double result = (Double) se.eval(formular); 6 return String.valueOf(result.doubleValue()); 7 } catch (ScriptException e) { 8 e.printStackTrace(); 9 } 10 return null; 11 }
然而,對於分數,這種方法是不行的。比如1/5+2/3這樣的式子,靠字符串渲染肯定行不通。依靠原始的數學求解方法,意識到需要借助求最大公約數和最小公倍數完成。例:求12和8的最小公倍數。12和8的最大公約數為4,12×8÷4=24 ,所以兩數的最小公倍數是24。下面放上分數加減乘除法的簡單實現。
1 public class Fraction { 2 static int numera = 0; 3 static int deomina = 0; 4 5 public static void main(String[] args) { 6 int a1 = 9; 7 int a2 = 10; 8 int b1 = 3; 9 int b2 = 11; 10 new Fraction().fracAdd(a1,a2,b1,b2);//結果為:11/20 11 System.out.println("分數加法運算:"+numera+"/"+deomina); 12 new Fraction().fracSub(a1,a2,b1,b2);//分數相減 13 System.out.println("分數減法運算:"+numera+"/"+deomina); 14 new Fraction().fracMul(a1,a2,b1,b2);//分數相乘 15 System.out.println("分數乘法運算:"+numera+"/"+deomina); 16 new Fraction().fractDiv(a1,a2,b1,b2);//分數相除 17 System.out.println("分數除法運算:"+numera+"/"+deomina); 18 } 19 20 //定義分數相加函數 21 public void fracAdd(int first_numerator, int first_denominator, int second_numrator, int second_denominator){ 22 int lcm, gcd; 23 lcm = lcm(first_denominator,second_denominator);//需要調用求最小公倍數的函數求的最小公倍數 24 numera = (lcm/first_denominator)*first_numerator+(lcm/second_denominator)* second_numrator;// 未化簡的分子和 25 deomina = lcm; //未化簡的分母 26 gcd = gcd(numera,deomina); //需要調用求最大公約數的函數 27 numera = numera/gcd; //化簡後的分子 28 deomina = deomina/gcd; //化簡後的分母 29 } 30 31 //定義分數相減函數 32 public void fracSub(int first_numerator,int first_denominator,int second_numrator,int second_denominator){ 33 int lcm, gcd; 34 lcm = lcm(first_denominator,second_denominator);//需要調用求最小公倍數的函數求的最小公倍數 35 numera = (lcm/first_denominator)*first_numerator-(lcm/second_denominator)* second_numrator;// 未化簡的分子差 36 deomina = lcm; //未化簡的分母 37 gcd = gcd(numera,deomina); //需要調用求最大公約數的函數 38 numera = numera/gcd; //化簡後的分子 39 deomina = deomina/gcd; //化簡後的分母 40 } 41 42 //定義分數相乘函數 43 public void fracMul(int first_numerator,int first_denominator,int second_numrator,int second_denominator){ 44 int z, m, gcd; 45 z = first_numerator * second_numrator; 46 m = first_denominator * second_denominator; 47 gcd = gcd(z,m); 48 numera = z / gcd; 49 deomina = m / gcd; 50 } 51 52 //定義分數相除函數 53 public void fractDiv(int first_numerator,int first_denominator,int second_numrator,int second_denominator){ 54 int z, a, m, gcd; 55 a = second_denominator; 56 second_denominator = second_numrator; 57 second_numrator = a; 58 z = first_numerator * second_numrator; 59 m = first_denominator * second_denominator; 60 gcd = gcd(z,m); 61 numera = z / gcd; 62 deomina = m / gcd; 63 } 64 65 //求最大公約數 66 public static int gcd(int m,int n){ 67 int i = 2; //定義整型變量i,為循環變量 68 int g, min; 69 min = m>n ? n : m; 70 g = 1; //最大公約數初始值為1 71 while(i <= min) //判斷條件,一直循環到兩個數中較小的那個結束 72 { 73 while (m % i == 0 && n % i == 0) 74 { 75 m = m / i; 76 n = n /i; 77 g = g * i; 78 } 79 i++; 80 } 81 return g; 82 } 83 84 //求最小公倍數函數 85 public static int lcm(int m,int n){ 86 int g, l; 87 g = gcd(m,n); //調用求最大公約數函數 88 l = m * n / g; 89 return l; 90 } 91 }
4. 整數和分數的自動生成已經OK,計算結果方法也實現了。下面放上另一個類的具體代碼。該代碼與上面的代碼一起實現了題目一的要求。
1 public class AutoFormula { 2 static String formular = new String(); 3 static String result = new String(); 4 int numerator; 5 int denominator; 6 7 public static void main(String[] args) { 8 /* 9 * 參數mode為符號模式,0是加減、1是乘除、2是加減乘除; 10 * 參數hasFraction為控制分數個數; 11 * 參數numOfCharacter為符號數量; 12 * 參數minOfNumber為式子中出現的最小數值; 13 * 參數maxOfNumber為式子中出現的最大數值; 14 */ 15 new AutoFormula().generate(0, 1, 3, 0, 10); 16 System.out.println(formular); 17 System.out.println(result); 18 } 19 20 21 public void generate(int mode, int hasFraction, int numOfCharacter, int minOfNumber, int maxOfNumber) { 22 int numOfNumber = numOfCharacter + 1; 23 String[] character = new String[numOfCharacter]; 24 String[] number = new String[numOfNumber]; 25 StringBuilder stringBuilder = new StringBuilder(); 26 27 //初始化符號數組 28 for (int i=0; i<character.length; i++) { 29 character[i] = generateCharacter(mode); 30 } 31 //初始化數字數組 32 for (int i=0; i<number.length; i++) { 33 number[i] = generateNumber(hasFraction, minOfNumber, maxOfNumber); 34 } 35 //鏈接符號和數字 36 stringBuilder.append(number[0]); 37 for (int i=0; i<character.length; i++) { 38 stringBuilder.append(character[i] + number[i+1]); 39 } 40 formular = stringBuilder.toString(); 41 42 if (hasFraction == 0) { 43 result = calIntResult(); 44 } else { 45 result = calFractionResult(number, character); 46 } 47 } 48 49 50 //3種隨機生成模式,隨機生成加減、乘除、加減乘除符號 51 public String generateCharacter(int mode) { 52 Random random = new java.util.Random(); 53 int i; 54 switch (mode) { 55 case 0: 56 //+ - 57 i = random.nextInt(2); 58 return (i==0) ? "+" : "-"; 59 case 1: 60 //* / 61 i = random.nextInt(2); 62 return (i==0) ? "*" : "/"; 63 case 2: 64 //+ - * / 65 i = random.nextInt(4); 66 return (i==0) ? "+" : (i==1) ? "-" : (i==2) ? "*" : "/"; 67 } 68 return null; 69 } 70 71 72 //2種隨機生成模式,隨機生成整數、分數 73 public String generateNumber(int mode, int min, int max) { 74 Random random = new java.util.Random(); 75 switch (mode) { 76 case 0: 77 //隨機生成整數 78 int num = min + random.nextInt(max); 79 while (num == 0) { //整數為0太簡單 80 num = min + random.nextInt(max); 81 } 82 return String.valueOf(num); 83 case 1: 84 //隨機生成分數 85 int numerator; 86 int denominator; 87 do { //分子為0太簡單,分母不能為0或1,分子分母不能相等 88 numerator = min + random.nextInt(max); 89 denominator = min + random.nextInt(max); 90 } while (numerator == 0 || denominator == 0 || denominator == 1 || denominator == numerator); 91 return String.valueOf(numerator) + "/" + String.valueOf(denominator); 92 } 93 return null; 94 } 95 96 97 //整型表達式的正確計算結果 98 public String calIntResult() { 99 ScriptEngine se = new ScriptEngineManager().getEngineByName("JavaScript"); 100 try { 101 Double result = (Double) se.eval(formular); 102 return String.valueOf(result.doubleValue()); 103 } catch (ScriptException e) { 104 e.printStackTrace(); 105 } 106 return null; 107 } 108 109 110 //分數型表達式的正確計算結果 111 public String calFractionResult(String[] number, String[] character) { 112 int first_numerator; 113 int first_denominator; 114 int second_numrator; 115 int second_denominator; 116 splitFraction(number[0]); 117 first_numerator = numerator; 118 first_denominator = denominator; 119 for (int i=0; i<character.length; i++) { 120 splitFraction(number[i+1]); 121 second_numrator = numerator; 122 second_denominator = denominator; 123 Fraction fraction = new Fraction(); 124 switch (character[i]) { 125 case "+": 126 fraction.fracAdd(first_numerator, first_denominator, second_numrator, second_denominator); 127 first_numerator = Fraction.numera; 128 first_denominator = Fraction.deomina; 129 break; 130 case "-": 131 new Fraction().fracSub(first_numerator, first_denominator, second_numrator, second_denominator); 132 first_numerator = Fraction.numera; 133 first_denominator = Fraction.deomina; 134 break; 135 case "*": 136 new Fraction().fracMul(first_numerator, first_denominator, second_numrator, second_denominator); 137 first_numerator = Fraction.numera; 138 first_denominator = Fraction.deomina; 139 break; 140 case "/": 141 new Fraction().fractDiv(first_numerator, first_denominator, second_numrator, second_denominator); 142 first_numerator = Fraction.numera; 143 first_denominator = Fraction.deomina; 144 break; 145 } 146 } 147 148 if (first_numerator == 0) { 149 return "0"; 150 } else if (first_numerator == first_denominator) { 151 return "1"; 152 } else { 153 return first_numerator + "/" + first_denominator; 154 } 155 } 156 157 158 //將分數拆分開 159 public void splitFraction(String fraction) { 160 if (fraction.contains("/")) { 161 String[] tmpNumber = fraction.split("/"); 162 numerator = Integer.parseInt(tmpNumber[0]); 163 denominator = Integer.parseInt(tmpNumber[1]); 164 } else { //整數就直接分子分母相同 165 numerator = Integer.parseInt(fraction); 166 denominator = numerator; 167 } 168 } 169 170 }
5. 做到這裡,您可能會問:
為什麼你要用Java語言實現呢?使用Python語言實現會不會更簡單呢?
答:嗯,Python實現的確更簡單,但是我們最後要做的當然是將它實現成一個網站或App。如果實現成網站,基於Spring MVC+Spring+Hibernate/Mybatis的Java Web擁有著更強大的邏輯表達能力以及更耦合、更穩定的技術架構。Java的強面向對象語言特性也能夠更好地幫助團隊嚴格化代碼的編寫,利於軟件工程團隊協作開發,Python弱語法的特點不利於規范代碼,在後期維護更強大的功能時勢必會復雜。PHP則只能應用於網站建設,後期不利於轉型為Android應用,因此它不在我們的考慮范圍內。.NET則只能用於微軟服務器,這對於摯愛Linux的我。。。無法忍受。
為什麼你要用Intellij IDE呢?Eclipse或者MyEclipse你咋不用?
答:這個問題已經討論透了。我喜歡Intellij的原因是它非常完善的代碼提示功能,即使你在寫CSS或Javascript腳本時,它都有HTML5特性的語法提示,更別提Java各種類、包、函數的精巧提示了;此外,它無條件支持Maven,Gradle這些Java的項目管理工具,必須點個大寫的贊。
6. 既然我們打算將這個服務做成網站,並且是基於Java SSH框架的,那麼前端我暫時使用Bootstrap框架,上手簡單,對於這個項目,足夠了。
我們團隊的Github代碼托管地址為:https://github.com/NorthWolives/。裡面包含基礎的運算式生成代碼以及目前網站前端實現的代碼。
這是我的前端頁面簡單實現:http://server.malab.cn/PupilLearn/
首頁截圖如下,希望未來小朋友們喜歡:
時間:2016年9月9日
作者:萬世想,天津大學計算機學院計算機科學與技術系