像二柱子那樣,花二十分鐘寫一個能自動生成三十道小學四則運算題目的 「軟件」,要求:除了整數之外,還要支持真分數的四則運算(須要驗證結果的正確性)、題目避免重複、可定製出題的數量。(有能力者改編成網頁版)java
Github源碼:git@github.com:54Simo/Java-Tutorial.gitgit
Arithmetic.javagithub
package cn.simo;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 四則運算表達式計算
* @see 功能:能夠用arithmetic返回一個double類型的結果,在calculate方法中由「劉立偉」設置了精度解決某些BUG。
* @author penli
*/
public class Arithmetic {
public static double arithmetic(String exp){
String result = parseExp(exp).replaceAll("[\\[\\]]", "");
return Double.parseDouble(result);
}
/**
* 解析計算四則運算表達式,例:2+((3+4)*2-22)/2*3
* @param expression
* @return String
*/
public static String parseExp(String expression){
//String numberReg="^((?!0)\\d+(\\.\\d+(?<!0))?)|(0\\.\\d+(?<!0))$";
expression=expression.replaceAll("\\s+", "").replaceAll("^\\((.+)\\)$", "$1");
String checkExp="\\d";
String minExp="^((\\d+(\\.\\d+)?)|(\\[\\-\\d+(\\.\\d+)?\\]))[\\+\\-\\*\\/]((\\d+(\\.\\d+)?)|(\\[\\-\\d+(\\.\\d+)?\\]))$";
//最小表達式計算
if(expression.matches(minExp)){
String result=calculate(expression);
return Double.parseDouble(result)>=0?result:"["+result+"]";
}
//計算不帶括號的四則運算
String noParentheses="^[^\\(\\)]+$";
String priorOperatorExp="(((\\d+(\\.\\d+)?)|(\\[\\-\\d+(\\.\\d+)?\\]))[\\*\\/]((\\d+(\\.\\d+)?)|(\\[\\-\\d+(\\.\\d+)?\\])))";
String operatorExp="(((\\d+(\\.\\d+)?)|(\\[\\-\\d+(\\.\\d+)?\\]))[\\+\\-]((\\d+(\\.\\d+)?)|(\\[\\-\\d+(\\.\\d+)?\\])))";
if(expression.matches(noParentheses)){
Pattern patt=Pattern.compile(priorOperatorExp);
Matcher mat=patt.matcher(expression);
if(mat.find()){
String tempMinExp=mat.group();
expression=expression.replaceFirst(priorOperatorExp, parseExp(tempMinExp));
}else{
patt=Pattern.compile(operatorExp);
mat=patt.matcher(expression);
if(mat.find()){
String tempMinExp=mat.group();
expression=expression.replaceFirst(operatorExp, parseExp(tempMinExp));
}
}
return parseExp(expression);
}
//計算帶括號的四則運算
String minParentheses="\\([^\\(\\)]+\\)";
Pattern patt=Pattern.compile(minParentheses);
Matcher mat=patt.matcher(expression);
if(mat.find()){
String tempMinExp=mat.group();
expression=expression.replaceFirst(minParentheses, parseExp(tempMinExp));
}
return parseExp(expression);
}
/**
* 計算最小單位四則運算表達式(兩個數字)
* @param exp
* @return String
*/
public static String calculate(String exp){
exp=exp.replaceAll("[\\[\\]]", "");
String number[]=exp.replaceFirst("(\\d)[\\+\\-\\*\\/]", "$1,").split(",");
BigDecimal number1=new BigDecimal(number[0]);
BigDecimal number2=new BigDecimal(number[1]);
BigDecimal result=null;
/*
* 設置精度,不然報錯(計算機並不知道要保留幾位,因此很乾脆的報錯)
* By 劉立偉
*/
MathContext mc = new MathContext(2, RoundingMode.HALF_DOWN);
//精度爲2,舍入模式爲大於0.5進1,不然捨棄。
String operator=exp.replaceFirst("^.*\\d([\\+\\-\\*\\/]).+$", "$1");
if("+".equals(operator)){
result=number1.add(number2);
}else if("-".equals(operator)){
result=number1.subtract(number2);
}else if("*".equals(operator)){
result=number1.multiply(number2);
}else if("/".equals(operator)){
result=number1.divide(number2,mc);
}
return result!=null?result.toString():null;
}
}
ChangeToFenshuDemo.java算法
package cn.simo;
/**
* 小數轉分數
* @author Happy_Girl2012
*/
public class ChangeToFenshuDemo {
/**
* 取得公約數
* @param a
* @param b
* @return int
*/
public static int getGongYueShu(int a, int b) {//求兩個數的最大公約數
int t = 0;
if(a < b){
t = a;
a = b;
b = t;
}
int c = a % b;
if(c == 0){
return b;
}else{
return getGongYueShu(b, c);
}
}
/**
* 轉化成分數
* @param xiaoshu
* @return String
*/
public static String toFenshu(String xiaoshu) {
String[] array = new String[2];
array = xiaoshu.split("\\.");
int a = Integer.parseInt(array[0]);//獲取整數部分
int b = Integer.parseInt(array[1]);//獲取小數部分
int length = array[1].length();
int FenZi = (int) (a * Math.pow(10, length) + b);
int FenMu = (int) Math.pow(10, length);
int MaxYueShu = getGongYueShu(FenZi, FenMu);
return new String(FenZi / MaxYueShu + "/" + FenMu / MaxYueShu);
}
}
CT.javaexpress
package cn.simo;
import java.util.Scanner;
/**
* 小學生出題系統(30道四則運算題)
* @author www.cnsimo.cn
* @since 2017-03-07 21:53:14
*/
public class CT {
public static String str = "";//保存題目的字符串
public static int num = 5;//每題中數的個數
public static int num_i = 0;//題目中已有數的個數
public static int numberRange = 100;//運算中數的最大取值
public static double sum = 0;//記錄結果
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("注意:結果保留1位小數!");
System.out.println("共30道題目:");
Scanner in = new Scanner(System.in);
double answer = 0;
double result = 0;
String[] question = new String[30];
int questionNumber = 0;
int answerTrue = 0;
boolean flag;
for(;;) {
answer = 0; result = 0; flag = true; str="";
if((questionNumber + 1)%5 != 0) {
GetQuestion_int();
} else {
GetQuestion_div();
}
for(int j = questionNumber-1; j >= 0; j --) {
if(question[j].equals(str)) {
flag = false; break;
}
}
if(!flag) continue;
else {question[questionNumber] = new String(str); questionNumber++;}
System.out.print("" + questionNumber + ". " + str+" = ");
answer = in.nextDouble();
if(!str.isEmpty()) {
result = Arithmetic.arithmetic(str);
}
if(answer == result) {
System.out.println(" ✔️");
answerTrue++;
} else {
System.out.println(" ❌ " + " 正確答案:" + result);
}
if(questionNumber == 30) break; // 滿30個跳出
}
System.out.println("你的正確機率:" + answerTrue + "/30");
in.close();
}
private static void GetQuestion_int() {
//獲得問題函數,在這裏調用遞歸函數quesGrow()。
str = "";
sum = 0;
num_i = num;//用前都清零
quesGrow_int();
}
private static void GetQuestion_div() {
str = "";
sum = 0;
num_i = num;//用前都清零
quesGrow_div();
}
private static void quesGrow_int() {
//
if( num_i > 1 ) {
int j = num_i;//記錄這是第幾層調用。
num_i--;
quesGrow_int();//遞歸
int w=1+(int)(Math.random()*numberRange);//隨機生成一個數
int t=1+(int)(Math.random()*100);//向左生成,仍是向右生成,相似於樹。
int f=1+(int)(Math.random()*100);//運算符控制
if(t>50)//新數往右加
{
if(f>50) {
sum = sum + w;
str = str + "+" + String.valueOf( w );
}
else {
sum = sum - w;
str = str + "-" + String.valueOf( w );
}
}
else//不然 新數往左加
{
if(f>50) {
sum = w + sum;
str = String.valueOf( w ) + "+" + str;
}
else {
if( j < 3 ) {//3——摸索出的數,不用給本身套上括號。實際上就是j=2
sum = w - sum;
str = String.valueOf( w ) + "-" + str;
}
else {
sum = w - sum;
str = String.valueOf( w ) + "-" + "(" +str+ ")";
//向左添減法的時候加括號,打破順序計算模式。
}
}
}
}
else if( num_i == 1 ) {
//最後一層,也是輸出的第一層
int w=1+(int)(Math.random()*numberRange);
sum = sum + w;
str = str + String.valueOf( w );
}
}
private static void quesGrow_div() {
if( num_i > 1 ) {
int j = num_i;//記錄這是第幾層調用。
num_i--;
quesGrow_div();//遞歸
double w=Math.random();//隨機生成一個數
int t=1+(int)(Math.random()*100);//向左生成,仍是向右生成,相似於樹。
int f=1+(int)(Math.random()*100);//運算符控制
if(t>50)//新數往右加
{
if(f>50) {
sum = sum + w;
str = str + "+" + ChangeToFenshuDemo.toFenshu((""+(w+0.01)).substring(0, 4));
}
else {
sum = sum - w;
str = str + "-" + ChangeToFenshuDemo.toFenshu((""+(w+0.01)).substring(0, 4));
}
}
else//不然 新數往左加
{
if(f>50) {
sum = w + sum;
str = ChangeToFenshuDemo.toFenshu((""+(w+0.01)).substring(0, 4)) + "+" + str;
}
else {
if( j < 3 ) {//3——摸索出的數,不用給本身套上括號。實際上就是j=2
sum = w - sum;
str = ChangeToFenshuDemo.toFenshu((""+(w+0.01)).substring(0, 4)) + "-" + str;
}
else {
sum = w - sum;
str = ChangeToFenshuDemo.toFenshu((""+(w+0.01)).substring(0, 4)) + "-" + "(" +str+ ")";
//向左添減法的時候加括號,打破順序計算模式。
}
}
}
}
else if( num_i == 1 ) {
//最後一層,也是輸出的第一層
double w=Math.random();
sum = sum + w;
str = str + ChangeToFenshuDemo.toFenshu((""+(w+0.01)).substring(0, 4));
}
}
}
平時要善於積累經常使用的算法,以及本身寫的以爲有用的代碼;數組
寫代碼時,千萬不要全部的東西都疊在一個方法裏面,根據功能寫相應的方法,之後用的時候能夠直接調用;app