自动生成小学四则运算题目

若如初见. 提交于 2020-04-02 00:48:23

结对项目
1.1 Github项目地址

https://github.com/everLuck666/-formula
1.2 项目成员
孙争:3118005106 邓富荣:3118005090
2.1 项目需求
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. 四则运算题目1
2. 四则运算题目2
      .......
其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。
7. 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:
1.
1. 答案1
2. 答案2
  特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。
8. 程序应能支持一万道题目的生成。
9. 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:
  Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt
  统计结果输出到文件Grade.txt,格式如下:
  Correct: 5 (1, 3, 5, 7, 9)
  Wrong: 5 (2, 4, 6, 8, 10)
其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。
2.2 PSP预估时间
PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 20
Estimate 估计这个任务需要多少时间 2880
Development 开发 1440
Analysis 需求分析(包括学习新技术) 40
Design Spec 生成设计文档 20
Design Review 设计复审 20
Coding Standard 代码规范 40
Design 具体设计 1440
Coding 具体编码 1440
Code Review 代码复审 10
Test 测试(自我测试、修改代码、提交修改) 30
Reporting 报告 20
Test Report 测试报告 20
Size Measurement 计算工作量 10
Postmortem&Process Improvement Plan 事后总结,并提出过程改进计划 20
Total 合计
3. 解题思路
编程语言的选择:java
需要用到的知识:IO流、JTable、栈
结构分析
遇到的困难
IO流写入会覆盖之前的内容导致写入内容与预期不符,变量类型的转换

对于计算中心思想:用后缀表达式运算。
用逆波兰法;
中缀表达式转后缀表达式遵循以下原则:
1.遇到操作数,直接输出;
2、栈为空的时候遇到符号,入栈
3、遇到左括号将其入栈。
4.遇到右括号,执行出栈操作,并将出栈的元素输出,直到弹出栈的是左括号,左括号不输出;
5.遇到其他运算符’+’‘一’’*’’/'时,弹出所有优先级大于或等于该运算符的栈项元素,然后将该运算符入栈;
6.最终将栈中的元素依次出栈,输出。
4. 设计实现过程
根据需求,对每个功能进行细分、简化,找出每个功能的详细需求,把每个功能都实现完成,生成函数,然后考虑封装调用,在主函数中实现调用各个功能函数,完成开发。
5.思维导图

 

 

 

6. 代码说明
main方法

public class major {
public static void main(String[] args){

Choose choose = new Choose();

}

主要调用函数

public class Choose extends JFrame{
public Choose() {
super("选择题目数量范围");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(400, 300);
this.setLocation(700, 250);
this.setLocationRelativeTo(null);
this.setResizable(false);
this.setLayout(null);
JLabel titleNumber = new JLabel("生成题目的数量:");
JLabel titleRange = new JLabel("题目的范围:");
Font font = new Font("微软雅黑", Font.BOLD, 15);
titleNumber.setFont(font);
titleRange.setFont(font);

JTextField number = new JTextField();
JTextField range = new JTextField();
JButton create = new JButton("生成");

titleNumber.setBounds(50, 50, 160, 50);
number.setBounds(200, 50, 100, 40);
titleRange.setBounds(50, 120, 150, 50);
range.setBounds(200, 120, 100, 40);
create.setBounds(150, 190, 100, 50);
this.add(titleNumber);
this.add(number);
this.add(create);
this.add(titleRange);
this.add(range);
this.setVisible(true);

create.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
ReadTxt rt = new ReadTxt();
int Number = Integer.parseInt(number.getText());
int Range = Integer.parseInt(range.getText());
try {
String[] title = title(Number,Range);
double[] rs = rt.readTxt("Answers.txt");
JOptionPane.showMessageDialog(null, "成功生成题目!");
dispose();
Jtable jTable_01 = new Jtable(title,rs);
jTable_01.setVisible(true);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
JOptionPane.showMessageDialog(null, "生成失败!");
} //答案文件

}
});
}

//生成题目、答案,并写入文件
public String[] title(int number,int range) throws IOException {
CalculateImp c = new CalculateImp();
Create_title c2 = new Create_title();
WriteTxt wt = new WriteTxt();
Vector title1 = new Vector();

String title_path = "Exercises.txt";
String result_path = "Answers.txt";

HashSet set = new HashSet();
ServerImp serverImp = new ServerImp();
for(int i = 0 ; i < number ; i++) {
String title = c2.create_title(range,3);
double result = Double.parseDouble(c.calculate(title.toCharArray()));

if(result < 0.0 && i != 0) {
i--;
System.out.println(result);
System.out.println("出现负数");
continue;
}

if(!serverImp.removeRepeat(title,set)){ //去除重复的函数,并把结果存储在set中
System.out.println("重复了");
i--;
}

}

Iterator iterator = set.iterator();
int j = 0;
while (iterator.hasNext()){
String title3 = (String)iterator.next();

title1.add(title3);
System.out.println("第" + String.valueOf(j+1) + "道题目:" + title3 + " =");
wt.WriteTxt("第" + String.valueOf(j+1) + "道题目:" + title3 + " =", title_path);
wt.WriteTxt("第" + String.valueOf(j+1) + "道题的答案:" + c.calculate(title3.toCharArray()),result_path);
j++;
}

String[] title3 = new String[title1.size()];
for(int i = 0;i<title1.size();i++){
title3[i] = (String)title1.get(i);
}
return title3;
}
}


生成题目函数Creat_title

public class Create_title {


public String create_title(int range, int num2) {//num:提目数 range:范围 num2:题目组成数量

Random random = new Random(range);
String[] char_arrays = null;
int num3 = (int) (Math.random() * num2);//分数的个数
int num4 = num2 - num3;//正数个数
int charNum = num2 - 1;//符号个数

int bracketL = 0;//(位置

int[] int_arrays = new int[num4 + num3 * 2];
boolean flag = isBracket();
if (flag) {
char_arrays = returnChar(num3, charNum + 2, flag);

} else {
char_arrays = returnChar(num3, charNum, false);
}


for (int w = 0; w < num2; w++) {
int_arrays[w] = random.nextInt(range);
}
String title = "";
if (char_arrays[0].equals("(")) {
for (int i = 0; i < num2; i++) {
title += char_arrays[i] + ((int) (Math.random() * range) + "");
}
if (char_arrays[num2].equals(")")) {
title += ")";
}

} else {
for (int i = 0; i <= num2; i++) {
title += ((int) (Math.random() * range) + "") + char_arrays[i];
}
}
if (title.charAt(title.length() - 1) == '+' || title.charAt(title.length() - 1) == '-' || title.charAt(title.length() - 1) == '/' || title.charAt(title.length() - 1) == '*') {
title = title.substring(0, title.length() - 1);
}


return title;
}

boolean isBracket() {
return true;
}

 


String[] returnChar(int mark, int charNum, boolean flag) {
Random random = new Random();
String[] char_array = new String[charNum];

if (flag) {
int bracketL = random.nextInt(charNum - 2);//(位置,最后一位和倒数第二位不能是(

int j = charNum - bracketL - 2;

int bracketR = bracketL + random.nextInt(j) + 2;//)位置这个随机数可以生成0

if (bracketL != 0) {
char_array[bracketL] = "+(";
} else {
char_array[bracketL] = "(";

}

if (bracketR == charNum - 1) {
char_array[bracketR] = ")";
} else {
char_array[bracketR] = ")-";
}
}
int mark1 = 0;
while (true) {
if (mark1 == mark) {
break;
}
int i = (int) (Math.random() * charNum);
if (char_array[i] == null) {
char_array[i] = "/";
mark1++;
}

}
int num = 0;
while (true) {

if (num == charNum) break;
if (char_array[num] == null) {
int i = (int) (Math.random()*4);
switch (i) {
case 0:
char_array[num] = "*";
break;
case 1:
char_array[num] = "+";
break;
case 2:
char_array[num] = "-";
break;

case 3:
char_array[num] = "÷";
break;

}
}

num++;

}
return char_array;
}
}

计算结果函数CalculateImp

public class CalculateImp {

public String calculate(char[] str) {
int j = 0;

//中缀表达式转后缀表达式
double[] num = new double[100];
char[] operator = new char[100];
int index = 0;

char[] stack = new char[100];
double[] stackj = new double[100];
int top = -1;

int temp = 0; //正在拼写数字
int flag = 0;//表示是否执行数字

int flag2 = 0;//判断什么时候读完了

while (true) {

if (j == str.length) {
flag2 = 1;
}
if (flag2 == 0 && str[j] >= '0' && str[j] <= '9') {//读到数字
flag = 1;
temp *= 10;
temp += str[j] - '0';


} else {//读到了符号,或者结束

if (flag == 1) {//如果在拼写一个数字就将一个数字输出

num[index] = temp;

operator[index] = '?';

index++;
flag = temp = 0;
}

if (flag2 == 1) {//如果字符串已经结束


while (top >= 0) {
int q = top--;
if (stack[q] != '(') {
operator[index++] = stack[q];

}

}
break;

} else {//读到一个符号

if (top == -1 || str[j] == '(') {
stack[++top] = str[j];


} else if (str[j] == ')') {//如果是右括号则一直出栈道左括号

while (top >= 0 && stack[top] != '(') {
operator[index++] = stack[top--];

}


} else if (str[j] == '*' || str[j] == '/') {//如果是乘除,则乘除出栈,新符号入栈
while ((top > 0) && (stack[top] == '*' || stack[top] == '/')) {
operator[index++] = stack[top--];

}
stack[++top] = str[j];

} else {//如果是加减,则四则运算出栈,新符号入栈

while ((top >= 0) && (stack[top] !='(')) {//>=0是为了确保第0位可以出栈
operator[index++] = stack[top--];

}

stack[++top] = str[j];

}

}

}
j++;
}

top = -1;
double temp1 = 0;
double temp2 = 0;

for (int i = 0; i < index; i++) {
if (operator[i] == '?') {
stackj[++top] = num[i];
} else {
temp1 = stackj[top--];

temp2 = stackj[top--];

switch (operator[i]) {
case '+':
stackj[++top] = temp2 + temp1;
break;
case '-':
stackj[++top] = temp2 - temp1;
break;
case '*':
stackj[++top] = temp2 * temp1;
break;
case '÷':
stackj[++top] = temp2 / temp1;
break;
case '/':
stackj[++top] = temp2 / temp1;
break;
}

}
}
double result = stackj[0];
return result + "";
}
}

去重函数ServerImp

public class ServerImp { //去重
boolean flag = true;
public boolean removeRepeat(String title, HashSet set) {
if(title.length()>5){
set.add(title);
}
if (title.charAt(0) == '(') {
if (title.length() == 5) {//只有出现两个数字相加的时候才允许运行
add(title, set, true);
System.out.println("didid");
}

} else if (title.length() == 3) {
add(title, set, false);
System.out.println("lal");
}


return flag;
}

void add(String title, HashSet set, boolean flag) {//用于两个数相加的情况
String a = null;
String b = null;
String c = null;
if (flag) {
a = String.valueOf(title.charAt(1));
c = String.valueOf(title.charAt(2));
b = String.valueOf(title.charAt(3));
} else {
a = String.valueOf(title.charAt(0));
c = String.valueOf(title.charAt(1));
b = String.valueOf(title.charAt(2));
}

String temp = b + c + a;
if (set.add(temp)) {
set.remove(temp);
set.add(a + c + b);
this.flag = true;
} else {
this.flag = false;
System.out.println("出现重复");
}
}
}



6. 测试运行
首先生成20道题目




提交答案后生成答题情况


写入的文件



7. 实现完程序后,实际花费时间
PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 2880
Estimate 估计这个任务需要多少时间 1440
Development 开发 1500
Analysis 需求分析(包括学习新技术) 40
Design Spec 生成设计文档 30
Design Review 设计复审 20
Coding Standard 代码规范 30
Design 具体设计 40
Coding 具体编码 1440
Code Review 代码复审 40
Test 测试(自我测试、修改代码、提交修改) 30
Reporting 报告 20
Test Report 测试报告 30
Size Measurement 计算工作量 10
Postmortem&Process Improvement Plan 事后总结,并提出过程改进计划 20
Total 合计 1590
8. 项目小结

对于一些的特别情况要考虑周全一些。

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!