面向对象——四则运算题目生成程序

一、题目描述:
       实践能力的提高当然就是得多动手了,那么就从第一个个人项目开始吧,用一周的时间完成一个基于控制台的四则运算程序,实现一个自动生成小学四则运算题目的命令行程序。
从《构建之法》第一章的 “程序” 例子出发,像阿超那样,花二十分钟写一个能自动生成小学四则运算题目的命令行 “软件”,满足以下需求:
(以下参考博客链接:http://www.cnblogs.com/jiel/p/4810756.html
 
1. 使用 -n 参数控制生成题目的个数,例如
       Myapp.exe -n 10 -o Exercise.txt
将生成10个题目。
2. 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如 
      Myapp.exe -r 10
 将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。
3. 生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数
4. 每道题目中出现的运算符个数不超过3个。
5. 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,23 + 45 = 和45 + 23 = 是重复的题目,6 × 8 = 和8 × 6 = 也是重复的题目。3+(2+1)和1+2+3这两个题目是重复的,由于+是左结合的,1+2+3等价于(1+2)+3,也就是3+(1+2),也就是3+(2+1)。但是1+2+3和3+2+1是不重复的两道题,因为1+2+3等价于(1+2)+3,而3+2+1等价于(3+2)+1,它们之间不能通过有限次交换变成同一个题目。
生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:
     1. 四则运算题目1
     2. 四则运算题目2
          ……
 
其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。
6. 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:
    1. 答案1
    2. 答案2
 
    特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。
7. 程序应能支持一万道题目的生成。
8. 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,并会输出所有题目中重复的题目,输入参数如下:
     Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt -o Grade.txt
 
统计结果输出到文件Grade.txt,格式如下:
 
Correct: 5 (1, 3, 5, 7, 9)
Wrong: 5 (2, 4, 6, 8, 10)
Repeat:2
RepeatDetail:
(1)   2,45+32  Repeat 3,32+45                    
(2)   5,3+(2+1)  Repeat 7,1+2+3
 
解释:
Correct: 5 ----5道题目正确,正确的题号 1,3,5,7,9
Wrong:5 -----5道题目错误,错误的题号 2,4,6,8,10
Repeat:2   2---组题目重复
(1) 第一组 题号2,题目 45+32  与题号3的题目重复,题号3为 32+45
(2)第二组  题号5,题目 3+(2+1) 与题号7的题目重复,题号7为 1+2+3
 
其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。
 
二、需求分析
        目前互联网技术飞速发展,传统的课堂教育也要跟上时代,针对于小学的基础教育更为关键。小学教师发布课后或是随堂作业时也要采取新的形式,四则运算生成程序可以帮助老师完成出题、收集作业、批改作业等繁琐的工作。
学生们也可以快捷的完成训练任务,简化了抄题、收缴作业带来的麻烦,让老师以更加高效的方式进行教学。所以说四则运算生成程序是很有价值的程序,整个功能完善后会有挺不错的商业价值和应用前景。
 
三、功能设计
       四则运算生成程序包括基本功能:生成题目、产生答案、在线答题、正误判断、查重。
                                          扩展功能:批改与评分、验证改错
四、设计实现
       四则运算生成程序分为arithmetic,method,calculate,output 4个类
arithmetic类——主程序,用来测试和运行整个程序。包含主方法main
method类——用来生成题目、对题目进行解析分成纯整式和分式以及在控制台输出题目以及答案。包含build(生成算式),sort(对算式进行分类),output(输出题目和答案)三个方法
calculate类——计算整式和分式,分别采取不同的处理方法。包含calculate1(计算整式),calculate2(计算分式),fracAdd,fracSub,fracMul,fracDiv(四个方法进行分数的计算)
 
五、代码说明
1.生成题目的build方法:

public String[][] build(String[][] total, int subject_num, int num_range) { // 生成算式
int[] so_count = new int[subject_num]; // 记录单运算符的下标

for (int i = 0; i < subject_num; i++) {
int operator_num = rd.nextInt(3) + 1;// 随机生成1-3个运算符个数
int flag = rd.nextInt(2); // 分数标志

先存放首个运算数,再以运算符+运算数的形式存放

*************************

int factor = rd.nextInt(num_range - 1) + 1;// 先存放首个数字字符

String str = String.valueOf(factor);
total[i][0] = str;
for (int j = 1; j < operator_num * 2; j = j + 2) {
if (operator_num == 1)
so_count[i] = i;
total[i][j] = getOperator(operator); // 再存放一组运算符加数字字符
if (flag == 1) { // 生成分数
int factor1 = rd.nextInt(num_range - 1) + 1;
int factor2 = rd.nextInt(num_range - 1) + 1;
if (factor1 > factor2) {
int temp = factor2;
factor2 = factor1;
factor1 = temp;
}
String str1 = String.valueOf(factor1);
String str2 = String.valueOf(factor2);
total[i][j + 1] = str1 + "/" + str2;
fraction_count.add(i);

} else { // 无分数
factor = rd.nextInt(num_range - 1) + 1;
str = String.valueOf(factor);
total[i][j + 1] = str;
}
}
}

********************************

生成括号

for (int i = 0; i < subject_num; i++) { // 生成括号
for (int k = 0; k < subject_num; k++) {
if (so_count[k] == i)
i++;
}
int flag_bracket = rd.nextInt(4);
if (flag_bracket == 0) {
for (int j = 9; j > 0; j--) {
total[i][j] = total[i][j - 1];
}
total[i][0] = "(";
for (int j = 9; j > 4; j--) {
total[i][j] = total[i][j - 1];
}
total[i][4] = ")";
}

if (flag_bracket == 1) {
for (int j = 9; j > 2; j--) {
total[i][j] = total[i][j - 1];
}
total[i][2] = "(";
for (int j = 9; j > 6; j--) {
total[i][j] = total[i][j - 1];
}
total[i][6] = ")";
}
}

return total;
}

*********************

2.进行分类的sort方法:

public void sort(String[][] total, int subject_num) {

把之前定义的fraction_count数组排除重复元素,生成的fraction_counts数组用于存放分式的下标
LinkedHashSet<Integer> set = new LinkedHashSet<Integer>(fraction_count);
ArrayList<Integer> fraction_counts = new ArrayList<Integer>(set); // 删除重复后的分数组,用来区分算法

********************
String[][] inter = new String[subject_num][15]; // 存放整数算式
String[][] fraction = new String[subject_num][15]; // 存放分数算式
int inter_i = 0; // 整式下标计数
int fraction_i = 0; // 分式下标计数
int flag = 0; // 判断标志
for (int i = 0; i < subject_num; i++) {
for (int j = 0; j < fraction_counts.size(); j++) {
if (fraction_counts.get(j) != i)
flag++;
else
flag--;
}

如果当前下标的值与fraction_counts数组中任意匹配时,说明为分式,进行划分

if (flag == fraction_counts.size()) {
inter[inter_i] = total[i];
inter_i++;
} else {
fraction[fraction_i] = total[i];
fraction_i++;
}
flag = 0;
}

3.分数计算calculate2方法:

先计算括号的内容,把结果放入sum_count中

for (int i = 0; i < fraction.length; i++) { // 先算括号
for (int j = 0; j < 15; j++) {
if (fraction[i][j] == "(") {
if (fraction[i][j + 2] == "+")
sum_count[i] = fracAdd(compute[i][j + 1], compute[i][j + 2], compute[i][j + 3],
compute[i][j + 4]);
if (fraction[i][j + 2] == "-")
sum_count[i] = fracSub(compute[i][j + 1], compute[i][j + 2], compute[i][j + 3],
compute[i][j + 4]);
if (fraction[i][j + 2] == "x")
sum_count[i] = fracMul(compute[i][j + 1], compute[i][j + 2], compute[i][j + 3],
compute[i][j + 4]);
if (fraction[i][j + 2] == "÷")
sum_count[i] = fracDiv(compute[i][j + 1], compute[i][j + 2], compute[i][j + 3],
compute[i][j + 4]);
}
}
}

********************************

分数的加法,输入运算的分子分母,输出结果的分子和分母

// 分数相加
public static int[] fracAdd(int first_numerator, int first_denominator, int second_numrator,
int second_denominator) {
// 以下代码能够在控制台上显示结果
// 需要调用求最大公约数的函数
// 需要调用求最小公倍数的函数
int a = lcm(first_denominator, second_denominator);
int numerator, denominator;
numerator = first_numerator * (a / first_denominator) + second_numrator * (a / second_denominator);
denominator = a;
int b = gcd(numerator, denominator);
numerator = numerator / b;
denominator = denominator / b;
int[] sum = new int[2];
sum[0] = numerator;
sum[1] = denominator;
return sum;
}

*************************

六、测试运行

输入题目数与数值范围:

生成题目(有分数和整式以及混合式):

 

 

结果:

七、小结

      本次四则运算的练习还是做得不够完善,运用的方法比较笨重,以后考虑二叉树的形式,目前还未完成查重、写入文件、答题与批改等功能,此系统还会继续完善下去。

通过此次作业的练习可以从前期的需求分析开始逐步对系统进行完善和改进,对于了解整个软件的体系结构和面向对象的概念有很好的帮助,需求分析是每一个软件项目都

必须完成的。所以今后在进行软件开发的时候一定要注意前期的准备和设计阶段,这些步骤不能大意和忽视。

 

项目链接:https://gitee.com/laoxu10086/four_operations/tree/master

posted @ 2018-03-31 21:54  老许10086  阅读(456)  评论(0编辑  收藏  举报