结对项目:自动生成小学四则运算题目
软件工程 | https://edu.cnblogs.com/campus/gdgy/CSGrade21-12?filter=all |
---|---|
作业要求 | https://edu.cnblogs.com/campus/gdgy/CSGrade21-12/homework/13016 |
作业目标 | 学习算法,完成简单的四则运算 |
团队成员:
姓名:陈鑫杰
学号:3121004688
姓名:黄嘉俊
学号:3121004696
Git链接:https://gitee.com/heart-knot/operation
题目
实现一个自动生成小学四则运算题目的命令行程序
需求
1.使用 -n 参数控制生成题目的个数,例如
Myapp.exe -n 10
将生成10个题目。
2.使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如
Myapp.exe -r 10
将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。
3.生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1− e2的子表达式,那么e1≥ e2。
4.生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数。
5.每道题目中出现的运算符个数不超过3个。
6.程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,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。
7.在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:
- 1.答案1
- 2.答案2
特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。
8.程序应能支持一万道题目的生成。
9.程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:
Myapp.exe -e
统计结果输出到文件Grade.txt,格式如下:
Correct: 5 (1, 3, 5, 7, 9)
Wrong: 5 (2, 4, 6, 8, 10)
其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。
PSP
PSP2.1 | Personal Software Process Stages | 预估耗费时间 (分钟) | 实际耗时 (分钟) |
---|---|---|---|
Planning | 计划 | 40 | 60 |
Estimate | 估计这个任务需要多少时间 | 120 | 150 |
Development | 开发 | 240 | 300 |
Analysis | 需求分析 (包括学习新技术) | 80 | 80 |
Design Spec | 生成设计文档 | 40 | 50 |
Design Review | 设计复审 | 30 | 30 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 20 | 15 |
Design | 具体设计 | 60 | 60 |
Coding | 具体编码 | 100 | 120 |
Code Review | 代码复审 | 30 | 20 |
Test | 测试(自我测试,修改代码,提交修改) | 40 | 60 |
Reporting | 报告 | 60 | 80 |
Test Report | 测试报告 | 40 | 40 |
Size Measurement | 计算工作量 | 20 | 20 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 20 | 25 |
Total | 合计 | 940 | 1100 |
设计实现
流程图
结构设计:
Operation类:输入参数启动程序
public class Operation {
public static void main(String[] args) throws IOException {
//输入
Scanner scanner = new Scanner(System.in);
//题目数量
System.out.println("请输入题目数量:");
int num = scanner.nextInt();
//数值范围
System.out.println("请输入题目数值范围:");
int range = scanner.nextInt();
Map<String, String[]> map = TopicUtils.generateTopic(num, range);
FileUtils.writeTA("Exercises", map.get("topics"));
FileUtils.writeTA("Answers", map.get("answers"));
String[] topics = map.get("topics");
String[] answers = map.get("answers");
String[] results = new String[topics.length];
for (int i = 0; i < topics.length; i++) {
int j = i + 1;
System.out.println("题目" + j + ": " + topics[i]);
System.out.println("请输入你的答案:");
results[i] = scanner.next();
}
int[] rightCount = new int[results.length];
int right = 0;
int[] wrongCount = new int[results.length];
int wrong = 0;
for (int i = 0; i < results.length; i++) {
if (results[i].equals(answers[i])) {
rightCount[i] = i + 1;
right++;
} else {
wrongCount[i] = i + 1;
wrong++;
}
}
FileUtils.writeG(rightCount, right, wrongCount, wrong);
}
}
TopicUtils类:生成题目并计算答案:
static Random random = new Random();
/**
* 生成题目并计算答案
*
* @param num 题目数量
* @param range 题目数值范围
* @return 题目
*/
public static Map<String, String[]> generateTopic(int num, int range) {
Map<String, String[]> map = new HashMap<>();
String[] expressions = new String[num];
for (int i = 0; i < num; i++) {
//运算符个数
int operatorNum = random.nextInt(2) + 1;
System.out.println("operatorNum:" + operatorNum);
//记录运算符
int[] operators = new int[operatorNum];
for (int j = 0; j < operatorNum; j++) {
//随机生成运算符
int operator = random.nextInt(3);
operators[j] = operator;
}
String[] operands = new String[operatorNum + 1];
//运算数生成
for (int k = 0; k <= operatorNum; k++) {
String operand = generateNum(range);
operands[k] = operand;
}
//表达式生成
StringBuilder expression = new StringBuilder();
for (int j = 0; j < operatorNum; j++) {
expression.append(operands[j]);
if (operators[j] == 0) {
expression.append("+");
} else if (operators[j] == 1) {
//减号可能会出现负数,就修改为整数
while (operands[j].compareTo(operands[j + 1]) < 0) {
operands[j + 1] = generateNum(range);
}
expression.append("-");
} else if (operators[j] == 2) {
expression.append("*");
} else {
expression.append("÷");
}
}
expression.append(operands[operatorNum]);
expressions[i] = expression.toString();
}
map.put("topics", expressions);
String[] compute = compute(expressions);
map.put("answers", compute);
return map;
}
private static String generateNum(int range) {
//判断是否生成分数
boolean b = random.nextBoolean();
//生成分数
if (b) {
//分子
int numerator = random.nextInt(range);
//分母
int denominator = random.nextInt(range - 1) + 1;
//拼接
return reductionOfFraction(numerator, denominator);
} else {//生成自然数
int operand = random.nextInt(range);
return String.valueOf(operand);
}
}
/**
* 计算答案
*
* @param expressions 题目
* @return 答案
*/
private static String[] compute(String[] expressions) {
String[] answers = new String[expressions.length];
int k = 0;
for (String expression : expressions) {
int j = 0;
int[] operators = new int[4];
for (int i = 0; i < expression.length(); i++) {
char c = expression.charAt(i);
if (c == '+' || c == '-' || c == '*' || c == '÷') {
//记录符号位置
operators[j++] = i;
}
}
String result = "0";
boolean flag = true;
for (int i = 0; i < operators.length; i++) {
System.out.println("k:" + k + "operators:" + operators[i]);
if (operators[i] != 0 || flag) {
//第一个运算符
if (i == 0) {
result = expression.substring(0, operators[i]);
} else {
char c = expression.charAt(operators[i - 1]);
String substring;
if (operators[i] == 0) {
substring = expression.substring(operators[i - 1] + 1);
} else {
substring = expression.substring(operators[i - 1] + 1, operators[i]);
}
if (c == '+') {
result = addCompute(result, substring);
} else if (c == '-') {
result = cutCompute(result, substring);
} else if (c == '*') {
result = multiCompute(result, substring);
} else if (c == '÷') {
result = divisCompute(result, substring);
}
}
if (operators[i] == 0) {
flag = false;
}
}
}
answers[k++] = result;
}
return answers;
}
private static String divisCompute(String result, String substring) {
System.out.println("sub:" + substring);
System.out.println("result:" + result);
String mark1;
//第二个运算数为带分数
if (substring.contains(" ")) {
//获取带分数前的整数
int index = substring.indexOf(" ");
System.out.println("index:" + index);
String substring1 = substring.substring(0, index);
//获取带分数的分数部分
String substring2 = substring.substring(index + 1);
//获取分子分母
String[] split = substring2.split("/");
int i3 = Integer.parseInt(split[1]) * Integer.parseInt(substring1) + Integer.parseInt(split[0]);
mark1 = i3 + "/" + split[1];
} else {//其他情况
mark1 = substring;
}
String mark2;
if (result.contains(" ")) {
//获取带分数前的整数
int index = result.indexOf(" ");
System.out.println("index:" + index);
String substring1 = result.substring(0, index);
//获取带分数的分数部分
String substring2 = result.substring(index + 1);
//获取分子分母
String[] split = substring2.split("/");
int i3 = Integer.parseInt(split[1]) * Integer.parseInt(substring1) + Integer.parseInt(split[0]);
mark2 = i3 + "/" + split[1];
} else {
mark2 = result;
}
int i1 = mark1.indexOf("/");
int i2 = mark2.indexOf("/");
System.out.println("mark1:" + mark1);
System.out.println("mark2:" + mark2);
//都为整数
if (i1 < 0 && i2 < 0) {
result = reductionOfFraction(Integer.parseInt(mark2), Integer.parseInt(mark1));
} else if (i1 >= 0 && i2 < 0) {//一个分数
String[] split = mark1.split("/");
result = reductionOfFraction(Integer.parseInt(mark2) * Integer.parseInt(split[1]), Integer.parseInt(split[0]));
} else if (i1 < 0) {//一个分数
String[] split = mark2.split("/");
result = reductionOfFraction(Integer.parseInt(split[0]), Integer.parseInt(split[1]) * Integer.parseInt(mark1));
} else {//都为分数
String[] split1 = mark1.split("/");
String[] split2 = mark2.split("/");
result = reductionOfFraction(Integer.parseInt(split1[1]) * Integer.parseInt(split2[0]), Integer.parseInt(split1[0]) * Integer.parseInt(split2[1]));
}
return result;
}
/**
* 乘法
*
* @param result 前一步结果
* @param substring 运算数
* @return 结果
*/
private static String multiCompute(String result, String substring) {
System.out.println("sub:" + substring);
System.out.println("result:" + result);
String mark1;
//第二个运算数为带分数
if (substring.contains(" ")) {
//获取带分数前的整数
int index = substring.indexOf(" ");
System.out.println("index:" + index);
String substring1 = substring.substring(0, index);
//获取带分数的分数部分
String substring2 = substring.substring(index + 1);
//获取分子分母
String[] split = substring2.split("/");
int i3 = Integer.parseInt(split[1]) * Integer.parseInt(substring1) + Integer.parseInt(split[0]);
mark1 = i3 + "/" + split[1];
} else {//不为带分数
mark1 = substring;
}
String mark2;
if (result.contains(" ")) {
//获取带分数前的整数
int index = result.indexOf(" ");
System.out.println("index:" + index);
String substring1 = result.substring(0, index);
//获取带分数的分数部分
String substring2 = result.substring(index + 1);
//获取分子分母
String[] split = substring2.split("/");
int i3 = Integer.parseInt(split[1]) * Integer.parseInt(substring1) + Integer.parseInt(split[0]);
mark2 = i3 + "/" + split[1];
} else {
mark2 = result;
}
System.out.println("mark1:" + mark1);
System.out.println("mark2:" + mark2);
int i1 = mark1.indexOf("/");
int i2 = mark2.indexOf("/");
//都为整数
if (i1 < 0 && i2 < 0) {
result = Integer.parseInt(mark1) * Integer.parseInt(mark2) + "";
} else if (i1 >= 0 && i2 < 0) {//一个分数
String[] split = mark1.split("/");
result = reductionOfFraction(Integer.parseInt(mark2) * Integer.parseInt(split[0]), Integer.parseInt(split[1]));
} else if (i1 < 0) {//一个分数
String[] split = mark2.split("/");
result = reductionOfFraction(Integer.parseInt(split[0]) * Integer.parseInt(mark1), Integer.parseInt(split[1]));
} else {//都为分数
String[] split1 = mark1.split("/");
String[] split2 = mark2.split("/");
result = reductionOfFraction(Integer.parseInt(split1[0]) * Integer.parseInt(split2[0]), Integer.parseInt(split1[1]) * Integer.parseInt(split2[1]));
}
return result;
}
/**
* 减法
*
* @param result 前一步结果
* @param substring 运算数
* @return 结果
*/
private static String cutCompute(String result, String substring) {
System.out.println("sub:" + substring);
System.out.println("result:" + result);
String mark1;
//第二个运算数为带分数
if (substring.contains(" ")) {
//获取带分数前的整数
int index = substring.indexOf(" ");
System.out.println("index:" + index);
String substring1 = substring.substring(0, index);
//获取带分数的分数部分
String substring2 = substring.substring(index + 1);
//获取分子分母
String[] split = substring2.split("/");
int i3 = Integer.parseInt(split[1]) * Integer.parseInt(substring1) + Integer.parseInt(split[0]);
mark1 = i3 + "/" + split[1];
} else {//不为带分数
mark1 = substring;
}
String mark2;
if (result.contains(" ")) {
//获取带分数前的整数
int index = result.indexOf(" ");
System.out.println("index:" + index);
String substring1 = result.substring(0, index);
//获取带分数的分数部分
String substring2 = result.substring(index + 1);
//获取分子分母
String[] split = substring2.split("/");
int i3 = Integer.parseInt(split[1]) * Integer.parseInt(substring1) + Integer.parseInt(split[0]);
mark2 = i3 + "/" + split[1];
} else {
mark2 = result;
}
int i1 = mark1.indexOf("/");
int i2 = mark2.indexOf("/");
System.out.println("mark1:" + mark1);
System.out.println("mark2:" + mark2);
//都为整数
if (i1 < 0 && i2 < 0) {
int i = Integer.parseInt(mark2) - Integer.parseInt(mark1);
result = i + "";
} else if (i1 >= 0 && i2 < 0) {//一个分数
String[] split = mark1.split("/");
result = reductionOfFraction(Integer.parseInt(mark2) * Integer.parseInt(split[1]) - Integer.parseInt(split[0]), Integer.parseInt(split[1]));
} else if (i1 < 0) {//一个分数
String[] split = mark2.split("/");
result = reductionOfFraction(Integer.parseInt(split[0]) - Integer.parseInt(split[1]) * Integer.parseInt(mark1), Integer.parseInt(split[1]));
} else {//都为分数
String[] split1 = mark1.split("/");
String[] split2 = mark2.split("/");
result = reductionOfFraction(Integer.parseInt(split2[0]) * Integer.parseInt(split1[1]) - Integer.parseInt(split1[0]) * Integer.parseInt(split2[1]), Integer.parseInt(split1[1]) * Integer.parseInt(split2[1]));
}
return result;
}
private static String addCompute(String result, String substring) {
System.out.println("sub:" + substring);
System.out.println("result:" + result);
String mark1;
//第二个运算数为带分数
if (substring.contains(" ")) {
//获取带分数前的整数
int index = substring.indexOf(" ");
System.out.println("index:" + index);
String substring1 = substring.substring(0, index);
//获取带分数的分数部分
String substring2 = substring.substring(index + 1);
//获取分子分母
String[] split = substring2.split("/");
int i3 = Integer.parseInt(split[1]) * Integer.parseInt(substring1) + Integer.parseInt(split[0]);
mark1 = i3 + "/" + split[1];
} else {//不为带分数
mark1 = substring;
}
String mark2;
if (result.contains(" ")) {
//获取带分数前的整数
int index = result.indexOf(" ");
System.out.println("index:" + index);
String substring1 = result.substring(0, index);
//获取带分数的分数部分
String substring2 = result.substring(index + 1);
//获取分子分母
String[] split = substring2.split("/");
int i3 = Integer.parseInt(split[1]) * Integer.parseInt(substring1) + Integer.parseInt(split[0]);
mark2 = i3 + "/" + split[1];
} else {
mark2 = result;
}
int i1 = mark1.indexOf("/");
int i2 = mark2.indexOf("/");
System.out.println("mark1:" + mark1);
System.out.println("mark2:" + mark2);
//都为整数
if (i1 < 0 && i2 < 0) {
int i = Integer.parseInt(mark1) + Integer.parseInt(mark2);
result = i + "";
} else if (i1 >= 0 && i2 < 0) {//一个分数
String[] split = mark1.split("/");
result = reductionOfFraction(Integer.parseInt(mark2) * Integer.parseInt(split[1]) + Integer.parseInt(split[0]), Integer.parseInt(split[1]));
} else if (i1 < 0) {//一个分数
String[] split = mark2.split("/");
result = reductionOfFraction(Integer.parseInt(split[1]) * Integer.parseInt(mark1) + Integer.parseInt(split[0]), Integer.parseInt(split[1]));
} else {//都为分数
String[] split1 = mark1.split("/");
String[] split2 = mark2.split("/");
result = reductionOfFraction(Integer.parseInt(split1[0]) * Integer.parseInt(split2[1]) + Integer.parseInt(split2[0]) * Integer.parseInt(split1[1]), Integer.parseInt(split1[1]) * Integer.parseInt(split2[1]));
}
return result;
}
/**
* 约分
*
* @param numerator 分子
* @param denominator 分母
* @return
*/
private static String reductionOfFraction(int numerator, int denominator) {
System.out.println("numerator:" + numerator);
System.out.println("denominator" + denominator);
int y = 1;
for (int i = numerator; i >= 1; i--) {
if (numerator % i == 0 && denominator % i == 0) {
y = i;
break;
}
}
// 分子
int z = numerator / y;
// 分母
int m = denominator / y;
System.out.println("z:" + z);
System.out.println("m:" + m);
if (z == 0) {
return "0";
}
if (m == 1) {
return z + "";
} else {
return convertToValid(z, m);
}
}
/**
* 假分数转为带分数
*
* @param numerator 分子
* @param denominator 分母
* @return
*/
private static String convertToValid(int numerator, int denominator) {
if (numerator >= denominator) {
int i = numerator / denominator;
int j = numerator % denominator;
if (j == 0) {
return i + "";
} else {
return i + " " + j + "/" + denominator;
}
} else {
return numerator + "/" + denominator;
}
}
}
FileUtils类:文件操作
public class FileUtils {
public static void writeTA(String name, String[] strings) throws IOException {
FileWriter fw;
//题目写入
File f = new File(name + ".txt");
fw = new FileWriter(f, true);
int i = 0;
PrintWriter pw = new PrintWriter(fw);
for (String string : strings) {
pw.println(i + 1 + ". " + string);
pw.flush();
i += 1;
}
fw.flush();
pw.close();
fw.close();
}
public static void writeG(int[] rightCount, int right, int[] wrongCount, int wrong) throws IOException {
FileWriter fw;
File f = new File("Grade.txt");
fw = new FileWriter(f, true);
PrintWriter pw = new PrintWriter(fw);
pw.println(" ");
pw.print("Correct:" + right + "(");
for (int i = 0; i < rightCount.length; i++) {
if (rightCount[i] != 0 && i != rightCount.length - 1) {
pw.print(rightCount[i] + ",");
} else if (rightCount[i] != 0 && i == rightCount.length - 1) {
pw.print(rightCount[i]);
}
}
pw.println(")");
pw.print("Wrong:" + wrong + "(");
for (int j = 0; j < wrongCount.length; j++) {
if (wrongCount[j] != 0 && j != wrongCount.length - 1) {
pw.print(wrongCount[j] + ",");
} else if (wrongCount[j] != 0 && j == wrongCount.length - 1) {
pw.print(wrongCount[j]);
}
}
pw.print(")");
pw.flush();
fw.flush();
pw.close();
fw.close();
}
}
程序测试:
1.输入参数:
2.输入题目答案:
3.生成的文档:
性能分析:
项目小结:
在这次合作中,我们获得了不错的成长。学会了项目分工,每个人各司其职,才能搭建好一个完整的项目。期间遇到的困难也积极解决,在沟通和协调中提升了自己的业务能力和沟通能力。这次项目是非常好的体验,为我们今后的团队合作打下了基础,同时感谢一起合作的小伙伴为这次的结对项目的辛勤付出。