第一题是要求实现一个自动生成小学生加减乘除四则运算题目的程序。后面可以将它扩展为网站或安卓应用或IOS应用或win10应用。
我的思路比较简单。环境是Java JDK1.8;IDE为Intellij社区版。
首先,我们不考虑有括号的情形,那么符号只有+、-、*、/四种;涉及到的数包含两种:整数或分数。
1. 我们注意到,一个运算式中,总有“数的个数比运算符多1”的规则。于是,我们自然地想到创建一个固定长度为n的数组number存放数字,每个数都能随机生成,通过maxOfNumber变量控制式子中出现的最大数字。创建一个长度为n-1的数组character存放运算符号。
2. 接下来需要初始化数组number和character。number数组每个元素靠maxOfNumber控制最大数、minOfNumber控制最小数、hasFraction控制是否出现分数随机生成;character数组每个元素靠type变量控制只有加减、只有乘除、加减乘除都有的3种类型随机生成。
例如:n为5,minOfNumber为0,maxOfNumber为10,hasFraction为出现分数,type为只有加减的类型:5/7+8/7-1/7-2/7
3. 接下来就是结果的计算。对于纯整数,计算结果是简单的。通过查找资料,我发现纯整数字符串的计算依靠ScriptEngine类即可完成。函数使用示例如下:
1 //整型表达式的正确计算结果
2 public String calIntResult() {
3 ScriptEngine se = new ScriptEngineManager().getEngineByName("JavaScript");
4 try {
5 Double result = (Double) se.eval(formular);
6 return String.valueOf(result.doubleValue());
7 } catch (ScriptException e) {
8 e.printStackTrace();
9 }
10 return null;
11 }
然而,对于分数,这种方法是不行的。比如1/5+2/3这样的式子,靠字符串渲染肯定行不通。依靠原始的数学求解方法,意识到需要借助求最大公约数和最小公倍数完成。例:求12和8的最小公倍数。12和8的最大公约数为4,12×8÷4=24 ,所以两数的最小公倍数是24。下面放上分数加减乘除法的简单实现。
1 public class Fraction {
2 static int numera = 0;
3 static int deomina = 0;
4
5 public static void main(String[] args) {
6 int a1 = 9;
7 int a2 = 10;
8 int b1 = 3;
9 int b2 = 11;
10 new Fraction().fracAdd(a1,a2,b1,b2);//结果为:11/20
11 System.out.println("分数加法运算:"+numera+"/"+deomina);
12 new Fraction().fracSub(a1,a2,b1,b2);//分数相减
13 System.out.println("分数减法运算:"+numera+"/"+deomina);
14 new Fraction().fracMul(a1,a2,b1,b2);//分数相乘
15 System.out.println("分数乘法运算:"+numera+"/"+deomina);
16 new Fraction().fractDiv(a1,a2,b1,b2);//分数相除
17 System.out.println("分数除法运算:"+numera+"/"+deomina);
18 }
19
20 //定义分数相加函数
21 public void fracAdd(int first_numerator, int first_denominator, int second_numrator, int second_denominator){
22 int lcm, gcd;
23 lcm = lcm(first_denominator,second_denominator);//需要调用求最小公倍数的函数求的最小公倍数
24 numera = (lcm/first_denominator)*first_numerator+(lcm/second_denominator)* second_numrator;// 未化简的分子和
25 deomina = lcm; //未化简的分母
26 gcd = gcd(numera,deomina); //需要调用求最大公约数的函数
27 numera = numera/gcd; //化简后的分子
28 deomina = deomina/gcd; //化简后的分母
29 }
30
31 //定义分数相减函数
32 public void fracSub(int first_numerator,int first_denominator,int second_numrator,int second_denominator){
33 int lcm, gcd;
34 lcm = lcm(first_denominator,second_denominator);//需要调用求最小公倍数的函数求的最小公倍数
35 numera = (lcm/first_denominator)*first_numerator-(lcm/second_denominator)* second_numrator;// 未化简的分子差
36 deomina = lcm; //未化简的分母
37 gcd = gcd(numera,deomina); //需要调用求最大公约数的函数
38 numera = numera/gcd; //化简后的分子
39 deomina = deomina/gcd; //化简后的分母
40 }
41
42 //定义分数相乘函数
43 public void fracMul(int first_numerator,int first_denominator,int second_numrator,int second_denominator){
44 int z, m, gcd;
45 z = first_numerator * second_numrator;
46 m = first_denominator * second_denominator;
47 gcd = gcd(z,m);
48 numera = z / gcd;
49 deomina = m / gcd;
50 }
51
52 //定义分数相除函数
53 public void fractDiv(int first_numerator,int first_denominator,int second_numrator,int second_denominator){
54 int z, a, m, gcd;
55 a = second_denominator;
56 second_denominator = second_numrator;
57 second_numrator = a;
58 z = first_numerator * second_numrator;
59 m = first_denominator * second_denominator;
60 gcd = gcd(z,m);
61 numera = z / gcd;
62 deomina = m / gcd;
63 }
64
65 //求最大公约数
66 public static int gcd(int m,int n){
67 int i = 2; //定义整型变量i,为循环变量
68 int g, min;
69 min = m>n ? n : m;
70 g = 1; //最大公约数初始值为1
71 while(i <= min) //判断条件,一直循环到两个数中较小的那个结束
72 {
73 while (m % i == 0 && n % i == 0)
74 {
75 m = m / i;
76 n = n /i;
77 g = g * i;
78 }
79 i++;
80 }
81 return g;
82 }
83
84 //求最小公倍数函数
85 public static int lcm(int m,int n){
86 int g, l;
87 g = gcd(m,n); //调用求最大公约数函数
88 l = m * n / g;
89 return l;
90 }
91 }
4. 整数和分数的自动生成已经OK,计算结果方法也实现了。下面放上另一个类的具体代码。该代码与上面的代码一起实现了题目一的要求。
1 public class AutoFormula {
2 static String formular = new String();
3 static String result = new String();
4 int numerator;
5 int denominator;
6
7 public static void main(String[] args) {
8 /*
9 * 参数mode为符号模式,0是加减、1是乘除、2是加减乘除;
10 * 参数hasFraction为控制分数个数;
11 * 参数numOfCharacter为符号数量;
12 * 参数minOfNumber为式子中出现的最小数值;
13 * 参数maxOfNumber为式子中出现的最大数值;
14 */
15 new AutoFormula().generate(0, 1, 3, 0, 10);
16 System.out.println(formular);
17 System.out.println(result);
18 }
19
20
21 public void generate(int mode, int hasFraction, int numOfCharacter, int minOfNumber, int maxOfNumber) {
22 int numOfNumber = numOfCharacter + 1;
23 String[] character = new String[numOfCharacter];
24 String[] number = new String[numOfNumber];
25 StringBuilder stringBuilder = new StringBuilder();
26
27 //初始化符号数组
28 for (int i=0; i<character.length; i++) {
29 character[i] = generateCharacter(mode);
30 }
31 //初始化数字数组
32 for (int i=0; i<number.length; i++) {
33 number[i] = generateNumber(hasFraction, minOfNumber, maxOfNumber);
34 }
35 //链接符号和数字
36 stringBuilder.append(number[0]);
37 for (int i=0; i<character.length; i++) {
38 stringBuilder.append(character[i] + number[i+1]);
39 }
40 formular = stringBuilder.toString();
41
42 if (hasFraction == 0) {
43 result = calIntResult();
44 } else {
45 result = calFractionResult(number, character);
46 }
47 }
48
49
50 //3种随机生成模式,随机生成加减、乘除、加减乘除符号
51 public String generateCharacter(int mode) {
52 Random random = new java.util.Random();
53 int i;
54 switch (mode) {
55 case 0:
56 //+ -
57 i = random.nextInt(2);
58 return (i==0) ? "+" : "-";
59 case 1:
60 //* /
61 i = random.nextInt(2);
62 return (i==0) ? "*" : "/";
63 case 2:
64 //+ - * /
65 i = random.nextInt(4);
66 return (i==0) ? "+" : (i==1) ? "-" : (i==2) ? "*" : "/";
67 }
68 return null;
69 }
70
71
72 //2种随机生成模式,随机生成整数、分数
73 public String generateNumber(int mode, int min, int max) {
74 Random random = new java.util.Random();
75 switch (mode) {
76 case 0:
77 //随机生成整数
78 int num = min + random.nextInt(max);
79 while (num == 0) { //整数为0太简单
80 num = min + random.nextInt(max);
81 }
82 return String.valueOf(num);
83 case 1:
84 //随机生成分数
85 int numerator;
86 int denominator;
87 do { //分子为0太简单,分母不能为0或1,分子分母不能相等
88 numerator = min + random.nextInt(max);
89 denominator = min + random.nextInt(max);
90 } while (numerator == 0 || denominator == 0 || denominator == 1 || denominator == numerator);
91 return String.valueOf(numerator) + "/" + String.valueOf(denominator);
92 }
93 return null;
94 }
95
96
97 //整型表达式的正确计算结果
98 public String calIntResult() {
99 ScriptEngine se = new ScriptEngineManager().getEngineByName("JavaScript");
100 try {
101 Double result = (Double) se.eval(formular);
102 return String.valueOf(result.doubleValue());
103 } catch (ScriptException e) {
104 e.printStackTrace();
105 }
106 return null;
107 }
108
109
110 //分数型表达式的正确计算结果
111 public String calFractionResult(String[] number, String[] character) {
112 int first_numerator;
113 int first_denominator;
114 int second_numrator;
115 int second_denominator;
116 splitFraction(number[0]);
117 first_numerator = numerator;
118 first_denominator = denominator;
119 for (int i=0; i<character.length; i++) {
120 splitFraction(number[i+1]);
121 second_numrator = numerator;
122 second_denominator = denominator;
123 Fraction fraction = new Fraction();
124 switch (character[i]) {
125 case "+":
126 fraction.fracAdd(first_numerator, first_denominator, second_numrator, second_denominator);
127 first_numerator = Fraction.numera;
128 first_denominator = Fraction.deomina;
129 break;
130 case "-":
131 new Fraction().fracSub(first_numerator, first_denominator, second_numrator, second_denominator);
132 first_numerator = Fraction.numera;
133 first_denominator = Fraction.deomina;
134 break;
135 case "*":
136 new Fraction().fracMul(first_numerator, first_denominator, second_numrator, second_denominator);
137 first_numerator = Fraction.numera;
138 first_denominator = Fraction.deomina;
139 break;
140 case "/":
141 new Fraction().fractDiv(first_numerator, first_denominator, second_numrator, second_denominator);
142 first_numerator = Fraction.numera;
143 first_denominator = Fraction.deomina;
144 break;
145 }
146 }
147
148 if (first_numerator == 0) {
149 return "0";
150 } else if (first_numerator == first_denominator) {
151 return "1";
152 } else {
153 return first_numerator + "/" + first_denominator;
154 }
155 }
156
157
158 //将分数拆分开
159 public void splitFraction(String fraction) {
160 if (fraction.contains("/")) {
161 String[] tmpNumber = fraction.split("/");
162 numerator = Integer.parseInt(tmpNumber[0]);
163 denominator = Integer.parseInt(tmpNumber[1]);
164 } else { //整数就直接分子分母相同
165 numerator = Integer.parseInt(fraction);
166 denominator = numerator;
167 }
168 }
169
170 }
5. 做到这里,您可能会问:
为什么你要用Java语言实现呢?使用Python语言实现会不会更简单呢?
答:嗯,Python实现的确更简单,但是我们最后要做的当然是将它实现成一个网站或App。如果实现成网站,基于Spring MVC+Spring+Hibernate/Mybatis的Java Web拥有着更强大的逻辑表达能力以及更耦合、更稳定的技术架构。Java的强面向对象语言特性也能够更好地帮助团队严格化代码的编写,利于软件工程团队协作开发,Python弱语法的特点不利于规范代码,在后期维护更强大的功能时势必会复杂。PHP则只能应用于网站建设,后期不利于转型为Android应用,因此它不在我们的考虑范围内。.NET则只能用于微软服务器,这对于挚爱Linux的我。。。无法忍受。
为什么你要用Intellij IDE呢?Eclipse或者MyEclipse你咋不用?
答:这个问题已经讨论透了。我喜欢Intellij的原因是它非常完善的代码提示功能,即使你在写CSS或Javascript脚本时,它都有HTML5特性的语法提示,更别提Java各种类、包、函数的精巧提示了;此外,它无条件支持Maven,Gradle这些Java的项目管理工具,必须点个大写的赞。
6. 既然我们打算将这个服务做成网站,并且是基于Java SSH框架的,那么前端我暂时使用Bootstrap框架,上手简单,对于这个项目,足够了。
我们团队的Github代码托管地址为:https://github.com/NorthWolives/。里面包含基础的运算式生成代码以及目前网站前端实现的代码。
这是我的前端页面简单实现:http://server.malab.cn/PupilLearn/
首页截图如下,希望未来小朋友们喜欢:
时间:2016年9月9日
作者:万世想,天津大学计算机学院计算机科学与技术系