要求:使用C或Java語言完成一個自動生成四則運算試題的程序
軟件基本功能如下。
- 自動生成10道100以內的2個操作數的四則運算算式(+ - * /),要求運算結果也在100以內
- 剔除重復算式。2+3 和 2+3 是重復算式,2+3 和 3+2 不屬於重復算式
- 題目數量可定制
- 相關參數可控制
- 是否包含乘法和除法
- 操作數數值范圍可控
- 操作數是否含負數
- 生成的運算題存儲到外部文件result.txt中
1. 需求分析
某小學里,老師讓家長每天出30道加減法題目給孩子做。於是,想寫一個小程序完成這件事。
2. 功能設計
- 基本功能
- 算式去重可控
- 題目數量可控
- 乘除可控
- 操作數范圍可控
- 操作數正負可控
- 輸出內容
- 擴展功能
- 輸出答案可控
- 判斷負數加括號
- 可實現多數字加減乘除運算
- 打印算式或者答案時,輸出文件保存的絕對路徑
- 算式運算結果范圍限制
3. 設計實現
- java語言,單類完成所有功能。
- 設置默認配置
- 設置自定義配置
- 產生隨機運算數和隨機運算符
- 生成四則運算
- 計算算式的值
- 生成答案
- 打印算式和答案
3.1 設置默認配置:
RandomFormula
類的無參構造方法,用來設置默認值。numberTotal
運算數數量設置為2,formulaToal
運算式數量設置為10,numberRange
數字范圍設置為100,maxResult
運算結果的最大范圍不超過100的絕對值,includeMulAndDiv
包含乘除設置為false,includeNegNum
設置為false。
public RandomFormula() {
this.numberTotal = 2;
this.formulaTotal = 10;
this.numberRange = 100;
this.maxResult = 100;
this.includeMulAndDiv = false;
this.includeNegNum = false;
}
3.2 設置自定義配置:
RandomFormula
類的有參構造方法,用來根據需求初始化。
public RandomFormula(int numberTotal, int formulaTotal, int numberRange, int maxResult, boolean includeMulAndDiv,
boolean includeNegNum) {
this.numberTotal = numberTotal;
this.formulaTotal = formulaTotal;
this.numberRange = numberRange;
this.maxResult = maxResult;
this.includeMulAndDiv = includeMulAndDiv;
this.includeNegNum = includeNegNum;
}
3.3 產生隨機運算數和隨機運算符:
getRandomNumber
方法用來產生隨機數,getRandomOperator
方法用來產生隨機運算符。
/**
* 獲取隨機數
*
* @return 返回一個指定范圍內的數字
*/
public int getRandomNumber() {
Random rand = new Random();
if (this.includeNegNum) {
return (rand.nextInt(this.numberRange) + 1) * (rand.nextDouble() > 0.5 ? 1 : -1);
} else {
return rand.nextInt(this.numberRange) + 1;
}
}
/**
* 得到一個隨機的運算符
*
* @return返回運算符
*/
public String getRandomOperator() {
Random rand = new Random();
String[] operations = { "+", "-", "*", "/" };
return operations[rand.nextInt((this.includeMulAndDiv == true) ? 4 : 2)];
}
3.4 生成四則運算:
根據配置,generateFormula
方法生成一道四則運算,如果isNegNum
方法判斷包含負數,則負數加括號。generateFormulas
生成指定數量的四則運算,用HashSet來存儲,可以自動去重算式。
/**
* 生成算式
*
* @return 返回算式
*/
public String generateFormula() {
String formula = "";
for (int i = 0; i < this.numberTotal; i++) {
if (i >= this.numberTotal - 1) {
formula += isNegNum(this.getRandomNumber());
continue;
}
formula += isNegNum(this.getRandomNumber()) + " " + this.getRandomOperator() + " ";
}
return formula;
}
/**
* 生成算式集合
*
* @return
*/
public HashSet<String> generateFormulas() {
HashSet<String> set = new HashSet<String>();
while (set.size() <= this.formulaTotal) {
String formula=this.generateFormula();
if(this.maxResult>=this.generateAnswer(formula))
set.add(formula);
}
return set;
}
/**
* 若負數,加括號
*
* @param num
* @return
*/
public String isNegNum(int num) {
if (num < 0) {
return "(" + num + ")";
}
return "" + num;
}
3.5 計算算式的值:
generateAnswer
遍歷HashSet的算式,compare
判斷運算符的優先級。根據棧的“先進后出”的特性,若是數字,則入棧;若是字符,判斷優先級,優先級高則直接入棧;優先級低,則出棧數字和字符,進入compute
計算,然后將結果入棧。如此,來計算算式的值。
/**
* 生成算式結果
*
* @param formula
* @return
*/
public int generateAnswer(String formula) {
int length = 0;
String[] formulaArr = formula.split(" ");
String operators = "+-*/";
Stack<Integer> opNumbers = new Stack<Integer>();
Stack<String> opOperators = new Stack<String>();
opOperators.add("#");//字符棧中存儲個#號,防止棧空
while (length < formulaArr.length) {
String op = formulaArr[length++];
if (operators.indexOf(op) > -1) {// 若是運算符,判斷優先級
String sign = opOperators.peek();
int priority = compare(op, sign);// 要入棧的跟棧頂的相比
if (priority >= 0) {// 如果要入棧的運算符高或者相等,出棧兩個數字,和之前的運算符,計算后,將數字入棧,將字符入棧
opNumbers.add(compute(opOperators, opNumbers));
opOperators.add(op);
} else {// 入棧運算符優先級低,直接入棧
opOperators.add(op);
}
continue;
}
// 若是數字,則入棧
opNumbers.add(Integer.parseInt(op.replace("(", "").replace(")", "")));
}
while (opOperators.peek() != "#") {
opNumbers.add(compute(opOperators, opNumbers));
}
return opNumbers.pop();
}
/**
* 比較運算優先級
*
* @return
*/
public int compare(String operator1, String operator2) {
int res = 0;
switch (operator1) {
case "+":
case "-":
if (operator2.equals("+") || operator2.equals("-") || operator2.equals("*") || operator2.equals("/")) {
res = 1;
} else {
res = -1;
}
break;
case "*":
case "/":
if (operator2.equals("*") || operator2.equals("/")) {
res = 1;
} else {
res = -1;
}
break;
}
return res;
}
/**
* 算式求值
*
* @return
*/
public int compute(Stack<String> opOperators, Stack<Integer> opNumbers) {
int num2 = opNumbers.pop();
int num1 = opNumbers.pop();
String _op = opOperators.pop();
int result = 0;
switch (_op) {
case "+":
result = num1 + num2;
break;
case "-":
result = num1 - num2;
break;
case "*":
result = num1 * num2;
break;
case "/":
result = num1 / num2;
break;
}
return result;
}
3.6 生成答案:
generateAnswers
用來生成相應算式數量的值,用一維數組來存儲。
/**
* 生成算式結果數組
*
* @param set
* @return
*/
public int[] generateAnswers(HashSet<String> set) {
int[] arr = new int[set.size()];
int i = 0;
for (String str : set) {
arr[i++] = generateAnswer(str);
}
return arr;
}
3.7 打印算式和答案:
outputFormulas
和outputAnswers
將算式和答案,分別保存到result.txt和answer.txt下,並且輸出文件的絕對路徑。
/** 輸出算式到文件
* @param set
* @return
*/
public String outputFormulas(HashSet<String> set) {
File file=new File("result.txt");
try {
FileWriter fw = new FileWriter(file);
for (String str : set) {
fw.write(str + "\n");
}
fw.close();
} catch (Exception e) {
System.out.println("Error" + e.getMessage());
System.exit(0);
}
return file.getAbsolutePath();
}
/** 輸出答案到文件
* @param arr
* @return
*/
public String outputAnswers(int[] arr) {
File file=new File("answer.txt");
try {
FileWriter fw = new FileWriter(file);
for (int i = 0; i < arr.length; i++) {
fw.write(arr[i]+"\n");
}
fw.close();
} catch (Exception e) {
System.out.println("Error" + e.getMessage());
System.exit(0);
}
return file.getAbsolutePath();
}
4. 測試運行
運行結果
默認設置
自定義設置
代碼覆蓋率
- 多位加減乘除運算 true
- 輸出答案 true
- 操作數范圍可控 true
- 輸出文件保存路徑 true
- 輸入邊界數據時的異常處理 true
- 生成算式答案范圍可控 true
5. 總結
第一版總結:感覺寫的程序比較復雜,所有的功能在一個循環里完成了。感覺好多地方還可以封裝一下,這樣代碼也會比較美觀。
還有我感覺自己變量用的太多了,有點冗余。還有個問題,如果最大操作數是1,輸出10個式子的話,肯定重復了啊。。。
算了,這個是第一版,之后慢慢改進。
第二版總結:第一版是用for循環執行一遍,執行完所有功能的。第二版將代碼重新封裝,優化了一下,界面上更加美觀,而且也沒有了那么多冗余的變量。每個要實現的功能都在各自的方法里實現的。main方法里實現的功能就是單純調用方法,輸入輸出的功能。但是,還有好多bug未解決。
雖然自己編程還是很菜,但是我不會放棄的。努力ing。。。
6. PSP
PSP2.1 | 任務內容 | 計划完成需要時間(min) | 實際完成需要時間(min) |
---|---|---|---|
Planning | 計划 | 5 | 10 |
Estimate | 估計時間,規划步驟 | 5 | 10 |
Development | 開發 | 30 | 145 |
Analysis | 需求分析 | 5 | 20 |
Coding | 具體編碼 | 20 | 120 |
Test | 測試 | 5 | 5 |
Reporting | 報告 | 20 | 40 |
Postmortem&Process Improvement Plan | 總結改進 | 20 | 40 |