2016011995 小學四則運算練習軟件項目報告
【coding.net地址】:https://git.coding.net/aspirinone/2016011995week2.git
【測試】在git上clone項目以後,在命令行編譯為class文件,再運行java Main 10
【計劃】項目完成步驟
【開發過程】
一、需求分析
(1) 程序可接收一個輸入參數n,然後隨機產生n道加減乘除練習題,每個數字在 0 和 100 之間,運算符在3個到5個之間。
(2) 每個練習題至少要包含2種運算符,而且練習題在運算過程中不得出現負數與非整數,比如不能出 3÷5+2=2.6,2-5+10=7等算式。
二、功能設計
基本要求:根據需求隨機出題並得出答案。
三、設計實現
(1)Main.java類啟動程序,接收參數n,引用ChuangJian.java
(2) ChuangJian.java類創建一個result.txt文件(如果已有一個txt文件,要事先刪除),輸出學號和題目,引用ChuTi.java類
1 public class Main { 2 public static void main(String[] args) { 3 Scanner input = new Scanner(System.in); 4 System.out.print("請輸入題目數量:"); 5 int n = 0;6 try{ 7 n = input.nextInt(); 8 }catch (Exception e){ 9 Scanner in = new Scanner(System.in); 10 System.out.print("輸入有誤,請輸入數字:"); 11 n = in.nextInt(); 12 } 13 ChuangJian.File(n); 14 } 15 }
1 public class ChuangJian { 2 public staticvoid File(int n){ 3 try{ 4 File file = new File("result.txt"); 5 if (file.exists()) { 6 file.delete(); 7 } 8 if(file.createNewFile()){ 9 FileOutputStream txtfile = new FileOutputStream(file); 10 PrintStream p = new PrintStream(txtfile); 11 p.println("2016011995"); 12 for(int i=0;i<n;i++){ 13 p.println(ChuTi.crePro()); 14 } 15 txtfile.close(); 16 p.close(); 17 System.out.println("文件創建成功!"); 18 } 19 } 20 catch(IOException ioe) { 21 ioe.printStackTrace(); 22 } 23 } 24 }
(3)ChuTi.java類中隨機產生3-5個操作符,並隨機產生相應的數字,隨機產生的操作符是通過下標數組確定的:(部分代碼)
1 public static String crePro(){ 2 Random random = new Random(); 3 String[] operator = {"+","-","×","÷"}; 4 5 int operatorCount = 3+random.nextInt(3); //操作符的個數3-5 6 int[] num = new int[operatorCount+1]; 7 int[] index = index(operatorCount); //操作符的下標 8 String s = new String(); 9 }
1 private static int[] index(int n){ //產生操作符的下標數組 2 Random random = new Random(); 3 int similar=0; 4 int[] a = new int[n]; 5 for(int j=0;j<n;j++){ 6 a[j] = random.nextInt(4); 7 } 8 for(int j=1;j<n;j++){ 9 if(a[0]==a[j]) similar++; 10 } 11 if(similar==n-1) return index(n); 12 else { 13 return a; 14 } 15 }
(4)Jisuan.java類用於判斷運算過程中是否出現負數和小數,使用了調度場算法和逆波蘭表達式,詳見算法詳解部分。
四、算法詳解
在Jisuan.java類中運用了以下思想:
參考了博客《調度場算法與逆波蘭表達式》:https://blog.csdn.net/acdreamers/article/details/46431285
調度場算法規則如下:從左到右遍歷中綴表達式的每個數字和符號,若是數字就輸出,即成為後綴表達式的一部分;若是符號,則判斷其與棧頂符號的優先級,若是右括號或優先級低於棧頂符號,則彈出棧 頂元素並輸出,將當前元素壓入棧,一直到輸出後綴表達式為止。
逆波蘭表達式求值步驟:
*初始化一個空堆棧
*如果字符是一個操作數,把他壓入堆棧
*如果字符是個操作符,彈出兩個操作數,執行恰當操作,然後把結果壓入堆棧,如果不能夠彈出兩個操作數,那麽後綴表達式的語法錯誤
*到後綴表達式末尾,從堆棧中彈出結果,若後綴表達式格式正確,那麽堆棧應該為空
(1)使用HashMap鍵值對規定字符優先級:
1 public static int calculate(String s) { 2 Stack<Integer> stack1 = new Stack<>(); //放數字 3 Stack<String> stack2 = new Stack<>(); //放操作符 4 HashMap<String, Integer> hashmap = new HashMap<>(); //存放運算符優先級 5 hashmap.put("(", 0); 6 hashmap.put("+", 1); 7 hashmap.put("-", 1); 8 hashmap.put("×", 2); 9 hashmap.put("÷", 2);
(2)對字符串中的操作符一個個處理,遇到數字則壓入棧,calculate()函數用於將運算過程中出現負數和小數的情況都返回為負數,後續排除。
1 switch (c) { 2 case ‘=‘: { //遇到等號 3 String stmp; 4 while (!stack2.isEmpty()) { //當前符號棧裏面還有+ - * /,即還沒有算完 5 stmp = stack2.pop(); 6 int a = stack1.pop(); 7 int b = stack1.pop(); 8 int sresulat = calculate(b, a, stmp); 9 if(sresulat<0) 10 return -1; 11 stack1.push(sresulat); 12 } 13 break; 14 } 15 16 default: { //遇到加減乘除等操作符 17 String stmp; 18 while (!stack2.isEmpty()) { //如果符號棧有符號 19 stmp = stack2.pop(); //當前符號棧,棧頂元素 20 if (hashmap.get(stmp) >= hashmap.get(String.valueOf(c))) { //比較優先級 21 int a = stack1.pop(); 22 int b = stack1.pop(); 23 int sresulat =calculate (b, a, stmp); 24 if(sresulat<0) 25 return -1; 26 stack1.push(sresulat); 27 } 28 else { 29 stack2.push(stmp); 30 break; 31 } 33 } 34 stack2.push(String.valueOf(c)); //將符號壓入符號棧 35 break; 36 } 37 }
【測試】
【時間分配】
2016011995 小學四則運算練習軟件項目報告