【優化】java生成四則運算


要求:使用C或Java語言完成一個自動生成四則運算試題的程序

軟件基本功能如下。

  1. 自動生成10道100以內的2個操作數的四則運算算式(+ - * /),要求運算結果也在100以內
  2. 剔除重復算式。2+3 和 2+3 是重復算式,2+3 和 3+2 不屬於重復算式
  3. 題目數量可定制
  4. 相關參數可控制
    1. 是否包含乘法和除法
    2. 操作數數值范圍可控
    3. 操作數是否含負數
  5. 生成的運算題存儲到外部文件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 打印算式和答案:

outputFormulasoutputAnswers將算式和答案,分別保存到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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM