四則運算題目自動生成——基於控制台(java) "個人作業——四則運算題目生成程式(基於控制台)" 項目已提交到碼雲: "UMLProject" 需求分析: 關於輸入、根據提示依次輸入: 數字範圍(樣例:10) 題目數量(樣例:10) 生成的題目: 如果存在形如e1 ÷ e2的子表達式,那麼其結果應 ...
四則運算題目自動生成——基於控制台(java)
個人作業——四則運算題目生成程式(基於控制台)
項目已提交到碼雲:UMLProject
需求分析:
- 關於輸入、根據提示依次輸入:
- 數字範圍(樣例:10)
- 題目數量(樣例:10)
- 生成的題目:
- 如果存在形如e1 ÷ e2的子表達式,那麼其結果應是真分數。
- 每道題目中出現的運算符個數不超過3個。
- 整數表示為1,真分數表示為1/2,假分數表示為2’1/2。
- 程式支持:
- 一萬道題目生成
- 生成題目的同時計算出答案,分別保存本地txt文件。
- 題目進行查重,去除重覆。
- 對個人答案的批改
功能設計:
控制台實現,功能相對簡單,實現按要求生成題目,根據用戶選擇進行是否批改答案。並顯示批改結果。
設計實現
1. 生成題目問題:
通過RandomQuestion類最終生成題目,RandomNum類和RandomSign類分別生成操作數和運算符。
RandomSign類:
public RandomSign(){//隨機生成符號
int random_sign;
random_sign = (int)(Math.random()*4);
switch(random_sign){
case 0 : this.setRandom_sign("+");break;
case 1 : this.setRandom_sign("-");break;
case 2 : this.setRandom_sign("*");break;
case 3 : this.setRandom_sign("÷");break;
}
}
RandomNum類:
public class RandomNum {//隨機生成一個數 a'b/c
private int random_numerator;//a'b/c 的格式拆分成 a b c 隨機生成組合
private int random_denominator;
private int random_front;
public String toStringSpit(){//進行區分 組成一個整體分數
if(this.getRandom_front()==0){
if(this.getRandom_numerator() == this.getRandom_denominator()){
return "1";
}else return this.getRandom_numerator() + "/" + this.getRandom_denominator();//真分數
}
if(this.getRandom_denominator() == this.getRandom_numerator()){
return Integer.toString(this.getRandom_front()+1);//整數
}
if(this.getRandom_numerator() == 0) return Integer.toString(this.getRandom_front());
return this.getRandom_front() + "'" + this.getRandom_numerator() + "/" + this.getRandom_denominator();//假分數
}
public void SplitANum(String str){//把9‘1/5反向 拆分成類(此處預設所有數為如此格式)
String[] parts;
int temp;
parts = str.split("'");
temp = Integer.parseInt(parts[0]);
this.random_front = temp;
parts = parts[1].split("/");
temp = Integer.parseInt(parts[0]);
this.random_numerator = temp;
temp = Integer.parseInt(parts[1]);
this.random_denominator = temp;
}
public String toString(){//預設
return this.getRandom_front() + "'" + this.getRandom_numerator() + "/" + this.getRandom_denominator();//都返回假分數
}
RandomQuestion類:
生成括弧參考(就不貼出來了):參考
public RandomQuestion(int qnum_range){//生成題目 用ArrayList存取
int sign_num = (int)(Math.random()*4+1);//骰子判斷生成題目長度
//this.randomquestion = null;
int flag = 0;
RQ(qnum_range,sign_num,flag);//遞歸實現題目生成 具體就不貼了
}
2. 答案計算:
中綴轉換尾碼 對尾碼表達式進行求答案。
轉尾碼遵循規則:參考
- 遇到操作數,直接輸出;
- 棧為空時,遇到運算符,入棧;
- 遇到左括弧,將其入棧;
- 遇到右括弧,執行出棧操作,並將出棧的元素輸出,直到彈出棧的是左括弧,左括弧不輸出;
- 遇到其他運算符’+”-”*”/’時,彈出所有優先順序大於或等於該運算符的棧頂元素,然後將該運算符入棧;
- 最終將棧中的元素依次出棧,輸出。
ChangeToRPN類:
public class ChangeToRPN {//a+b*c+(d*c+f)g -> abc*+de*f+g*+ 中綴轉尾碼
public ArrayList<Object> changetoRPN(ArrayList<Object> rq){
ArrayList<Object> rpn = new ArrayList<Object>();
Stack sk = new Stack();
String temp_stackpop;
for(int i = 0; i <= rq.size()-2; i++){
if(IsSign(rq.get(i).toString())){//判斷是不是符號
if(sk.getTop() == -1 || rq.get(i).toString() == "("){//棧空 和( 直接入棧
sk.push(rq.get(i).toString());
}else{
if(rq.get(i).toString() == ")"){
while(sk.getTop() != -1 && sk.top().toString() != "("){
temp_stackpop = sk.pop().toString();
if(temp_stackpop != "(") rpn.add(temp_stackpop);
}
}else{//遇到別的操作符 判斷優先順序
while(sk.getTop() != -1 && GetPriority(sk.top().toString(),true)
>= GetPriority(rq.get(i).toString(),false)){
temp_stackpop = sk.pop().toString();
if(temp_stackpop != "(") rpn.add(temp_stackpop);
}
sk.push(rq.get(i));
}
}
}else rpn.add(rq.get(i));
}
while(sk.getTop()!=-1){
temp_stackpop = sk.pop().toString();
if(temp_stackpop != "(") rpn.add(temp_stackpop);
}
return rpn;
}
//flag true->棧內 符號優先順序
int GetPriority(String operator, boolean flag)
{
if (operator == "+" || operator == "-")
{
if (flag) return 3;
else return 2;
}
else if (operator == "*" || operator == "÷")
{
if (flag) return 5;
else return 4;
}
else if (operator == "(")
{
if (flag) return 1;
else return 6;
}
else if (operator == ")")
{
if (flag) return 6;
else return 1;
}
return 0;
}
}
不得不講一下,逆波蘭式在挺早的時候就學過了,然而還是完全忘了。。最終還是複習了下,才想起來整體流程。
Answer類:
public String GetAnswer(ArrayList<Object> rpn){
Stack sk = new Stack();
String rpn_temp;
String num1,num2;
String sk_temp;
for(int i = 0; i <= rpn.size()-1; i++){
rpn_temp = rpn.get(i).toString();
if(IsSign(rpn_temp)){
num2 = sk.pop().toString();//註意出棧順序 這邊應該是 num2 後 num1
num1 = sk.pop().toString();
sk_temp = TwoNumCount(num1,num2,rpn_temp);//對兩個數求值
if(sk_temp != "-1") sk.push(sk_temp);
else return "-1";//返回-1表示該式子求值失敗 調用處continue
}else sk.push(rpn_temp);
}
String result = sk.pop().toString();
RandomNum rq = new RandomNum();
rq.SplitANum(result);
return rq.toStringSpit();
}
}
3. 題目查重:
Tree類:
5+6-2 —>56+2- 的轉化存儲過程:
public class Tree {
private double result;//存取當前節點計算的值
private String value;//存取當前節點值
private Tree lchild;
private Tree rchild;
//生成樹中的規則
public boolean Compare(Tree tree1, Tree tree2){//true——>tree1 左邊
ChangeToRPN rpn = new ChangeToRPN();
int flag = 0;
if(tree1.getResult() > tree2.getResult()) flag = 1;
else if(tree1.getResult() < tree2.getResult()) flag = 2;
else flag = 3;
if(flag == 1) return true; //值大的為左
else if(flag == 2) return false;
else if(tree1.getLchild() == null && tree2.getLchild() == null) return true;//2 2相等但是沒有孩子
else if(rpn.GetPriority(tree1.getValue().toString(), true) // 值相等 運算符大的 左
> rpn.GetPriority(tree2.getValue().toString(), true)) return true;
else if(rpn.GetPriority(tree1.getValue().toString(), true) // 值和運算符都等 則子樹的左子樹值大的 左
== rpn.GetPriority(tree2.getValue().toString(), true)){
if(tree1.getLchild().getResult() > tree2.getLchild().getResult()) flag = 1;
else if(tree1.getLchild().getResult() < tree2.getLchild().getResult()) flag = 2;
else flag = 3;
if(flag == 1) return true;
else if(flag == 2) return false;
else return true;
}
return false;//tree2應為左
}
}
4. 運行測試
5. PSP
PSP2.1 | Personal Software Process Stages | Time Senior Student | Time |
---|---|---|---|
Planning | 計劃 | 10 | 10 |
· Estimate | 估計這個任務需要多少時間 | 10 | 10 |
Development | 開發 | 600 | 900 |
· Analysis | 需求分析 (包括學習新技術) | 30 | 30 |
· Design Spec | 生成設計文檔 | - | - |
· Design Review | 設計覆審 | - | - |
· Coding Standard | 代碼規範 | - | - |
· Design | 具體設計 | - | - |
· Coding | 具體編碼 | 480 | 600 |
· Code Review | 代碼覆審 | 240 | 120 |
· Test | 測試(自我測試,修改代碼,提交修改) | 200 | 120 |
Reporting | 報告 | 240 | 180 |
· | 測試報告 | 50 | 50 |
· | 計算工作量 | 10 | 10 |
· | 並提出過程改進計劃 | - | - |
6. 總結
做這個項目還是花了很多時間的,大多是下課後過去圖書館然後做這個了。做的時候碰到挺多問題的吧,最大的是一開始沒有構思好具體思路,到了做著的時候發現行不通,後來還是再構想後,決定重新開始做。
具體編碼這部分雖然一開始就覺得會花很久,結果真正做的時候發現,出現bug是真的耗費時間。
這個表格的時間我其實我並沒有很詳細的算了,因為做的時候零零散散的,所以時間也沒有很好的統計,只是大概估計了下。
測試這一塊我覺得我做的不夠好,可以考慮用下單元測試,就像寫二叉樹查重那塊,我是整體寫完了,才開始測試,所以出現了有的讀空,也有其他的細節小問題,比如出棧數的操作對於減法除法應該反著來的,起初沒考慮到,後面發現才改過來。
後來在進一步優化代碼以及寫寫註釋什麼的,也花了一些時間。
表裡面的有的還不太清楚,所以就沒寫了。
第一次寫博客!偷偷標記!