这个作业属于哪个课程 | 计科12班 |
---|---|
这个作业要求在哪里 | 作业要求 |
这个作业的目标 | 要求实现一个自动生成小学四则运算题目的命令行程序 |
成员及仓库
学号 | 成员 | 代码仓库 |
---|---|---|
3122004657 | 林诗淇 | https://github.com/shiqi323/examination |
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时 (分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 30 |
·Estimate | ·估计这个任务需要多少时间 | 30 | 30 |
Development | 开发 | 500 | 600 |
·Analysis | ·需求分析(包括学习新技术) | 50 | 40 |
·Design Spec | ·生成设计文档 | 20 | 20 |
·Design Review | ·设计复审 | 10 | 5 |
·Coding Standard | ·代码规范(为目前的开发制定合适的规范) | 10 | 15 |
·Design | ·具体设计 | 30 | 40 |
·Coding | ·具体编码 | 300 | 350 |
·Code Review | ·代码复审 | 30 | 60 |
·Test | ·测试(自我测试,修改代码,提交修改) | 10 | 40 |
Reporting | 报告 | 65 | 75 |
·Test Repor | ·测试报告 | 30 | 40 |
·Size Measurement | ·计算工作量 | 15 | 10 |
·Postmortem & Process Improvement Plan | ·事后总结,并提出过程改进计划 | 20 | 25 |
合计 | 1050 | 1200 |
需求分析
1.从个人项目出发,将程序改造成一个带用户界面的程序
2.自动生成题目,单个题目最多不能超过4个运算符,操作数小于100
3.用户可以输入答案
4.判断并提示用户输入答案的对错,显示正确答案
代码部分
1.运算
点击查看代码
package main.com.examination.util;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@SuppressWarnings("all")
public class CalculateUtil {
private static final Logger logger = Logger.getLogger("CalculateUtil");
/**
* 加法运算
* @param numerator1 分子1
* @param denominator1 分母1
* @param numerator2 分子2
* @param denominator2 分母2
* @return 返回结果
*/
public static StringBuilder add(int numerator1,int denominator1,int numerator2,int denominator2) {
int numerator,denominator;
StringBuilder result = new StringBuilder();
numerator = numerator1*denominator2+numerator2*denominator1;
denominator = denominator1 * denominator2;
if(numerator!=0) {
//化简分子分母(除以最大公因数)
int gcdNum = HandleUtil.gcd(numerator,denominator);
numerator /= gcdNum;
denominator /= gcdNum;
}
result.append(numerator+"/"+denominator);
return result;
}
/**
* 减法运算
* @param numerator1 分子1
* @param denominator1 分母1
* @param numerator2 分子2
* @param denominator2 分母2
* @return 返回计算结果
*/
public static StringBuilder minus(int numerator1,int denominator1,int numerator2,int denominator2) {
int numerator,denominator;
StringBuilder result = new StringBuilder();
numerator = numerator1*denominator2-numerator2*denominator1;
denominator = denominator1*denominator2;
//化简分子分母(除以最大公因数)
if(numerator!=0) {
int gcdNum = HandleUtil.gcd(numerator,denominator);
numerator /= gcdNum;
denominator /= gcdNum;
}
result.append(numerator+"/"+denominator);
return result;
}
/**
* 乘法运算
* @param numerator1 分子1
* @param denominator1 分母1
* @param numerator2 分子2
* @param denominator2 分母2
* @return 返回计算结果
*/
public static StringBuilder multiply(int numerator1,int denominator1,int numerator2,int denominator2) {
int numerator,denominator;
StringBuilder result = new StringBuilder();
//操作数有一个等于0的情况
if(numerator1==0||numerator2==0) {
result.append(0+"/"+1);
}
//操作数大于0的情况
else {
numerator = numerator1*numerator2;
denominator = denominator1*denominator2;
//化简分子分母(除以最大公因数)
if(numerator!=0) {
int gcdNum = HandleUtil.gcd(numerator,denominator);
numerator /= gcdNum;
denominator /= gcdNum;
}
result.append(numerator+"/"+denominator);
}
return result;
}
/**
* 除法运算
* @param numerator1 分子1
* @param denominator1 分母1
* @param numerator2 分子2
* @param denominator2 分母2
* @return 返回计算结果
*/
public static StringBuilder divide(int numerator1,int denominator1,int numerator2,int denominator2) {
int numerator,denominator;
StringBuilder result = new StringBuilder();
numerator = numerator1*denominator2;
denominator = denominator1*numerator2;
//化简分子分母(除以最大公因数)
if(numerator!=0) {
int gcdNum = HandleUtil.gcd(numerator,denominator);
numerator /= gcdNum;
denominator /= gcdNum;
}
result.append(numerator+"/"+denominator);
return result;
}
/**
* 对运算符号左右的两个数进行运算
* @param index 运算符的位序
* @param extraCopy 待计算的式子
* @return
*/
public static StringBuilder calculate(int index,StringBuilder extraCopy) {
char sign = extraCopy.charAt(index);
int beginIndex = 0, endIndex = -1;
int[] datas;
for(int index1=0; ; beginIndex=index1) {
//找到第一个操作数的开头空格
index1 = extraCopy.indexOf(" ", index1+1);
if(index1==(index-1)) {
break;
}
}
datas = HandleUtil.change(extraCopy, beginIndex);
int numerator1 = datas[1];
int denominator1 = datas[2];
datas = HandleUtil.change(extraCopy, index+1);
int numerator2 = datas[1];
int denominator2 = datas[2];
endIndex = datas[0];
//删除数字部分
extraCopy.delete(beginIndex+1,endIndex);
//根据符号进行相应的运算
switch(sign){
case '+':
extraCopy.insert(beginIndex+1, add(numerator1,denominator1,numerator2,denominator2));
break;
case '-':
if(!HandleUtil.judge(numerator1, denominator1, numerator2, denominator2)) {
//识别答案是否为负数
extraCopy.insert(0, "@ ");
break;
} else{
extraCopy.insert(beginIndex+1, minus(numerator1,denominator1,numerator2,denominator2));
break;
}
case '*':
extraCopy.insert(beginIndex+1, multiply(numerator1,denominator1,numerator2,denominator2));
break;
case '÷':
if(numerator2 == 0) {
//识别答案是否为负数,是的话在开头插入@作为标识
extraCopy.insert(0, "@ ");
break;
} else{
extraCopy.insert(beginIndex+1, divide(numerator1,denominator1,numerator2,denominator2));
break;
}
default: break;
}
return extraCopy;
}
/**
* 按优先级进行运算(* / + -)
* @param extraCopy copy副本
* @return 返回
*/
public static StringBuilder calculateFormula(StringBuilder extraCopy) {
// logger.info(extraCopy.toString());
//记录符号的位序
int index = -1;
//计算式子
Pattern pattern1 = Pattern.compile("[*]|[÷]");
Matcher m1;
while((m1 = pattern1.matcher(extraCopy)).find()) {
index = m1.start();
calculate(index, extraCopy);
if(extraCopy.charAt(0)=='@') {
break;
}
}
//如果式子正确,在进行加运算(从左到右)
if(extraCopy.charAt(0)!='@') {
Pattern pattern2 = Pattern.compile("[-]|[+]");
Matcher m2;
while((m2 = pattern2.matcher(extraCopy)).find()) {
index = m2.start();
calculate(index, extraCopy);
if(extraCopy.charAt(0)=='@') {
break;
}
}
}
//如果运算结束后(式子正确),调整答案格式
if(extraCopy.charAt(0)!='@') {
int datas[];
datas = HandleUtil.change(extraCopy, 0);
//分子
int numerator = datas[1];
//分母
int denominator = datas[2];
//将原存储内容清空
extraCopy.setLength(0);
//将答案换成标准格式
extraCopy.append(HandleUtil.creatNum(numerator, denominator));
}
return extraCopy;
}
}
点击查看代码
package main.com.examination.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@SuppressWarnings("all")
public class CheckUtil {
/**
* 将StringBuilder拆分成string数组
* @param stringBuilder
* @return 返回string[] 数组
*/
public static String[] spiltStringBuilderToArray(StringBuilder stringBuilder){
return stringBuilder.toString().split("\\s+");
}
/**
* 将StringBuilder拆分成List数组
* @param stringBuilder string串串
* @return 返回List数组
*/
public static List<String> spiltStringBuilderToList(StringBuilder stringBuilder){
return Arrays.asList(spiltStringBuilderToArray(stringBuilder));
}
/**
* 将StringBuilder拆分成有序的 List 数组
* @param stringBuilder string串串
* @return 返回List数组
*/
public static List<String> spiltStringBuilderToOrderList(StringBuilder stringBuilder){
List<String> stringList = spiltStringBuilderToList(stringBuilder);
Collections.sort(stringList);
return stringList;
}
/**
* 判断内容是否有重复
* @param formula 判断的式子
* @param lists 排序完的全部 list
* @param answer 答案
* @param answerLists 答案集
* @return 返回是否重复
*/
public static boolean judgeRepeat(StringBuilder formula, List<List<String>> lists,StringBuilder answer,List<StringBuilder> answerLists){
List<String> formulaList = spiltStringBuilderToOrderList(formula);
int i;
for (i = 0;i<lists.size();i++){
if(lists.get(i).equals(formulaList) && answer.toString().equals(answerLists.get(i).toString())){
return true;
}
}
lists.add(formulaList);
return false;
}
}
点击查看代码
package main.com.examination.util;
import main.com.examination.commons.OperatorVar;
import main.com.examination.dao.IODao;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
public class CreateUtil {
//日志输出
private static final Logger logger = Logger.getLogger("CreateUtil");
List<StringBuilder> formula;
//备份式子,存储"分子/分母"结构的式子,便于结果计算
List<StringBuilder> answer;
StringBuilder extraCopy ;
public List<StringBuilder> getFormula() {
return formula;
}
public List<StringBuilder> getAnswer() {
return answer;
}
/**
* 随机生成式子
* 用List存储式子
* @param maxNum 最大值
* @return 返回该字符串
* */
public StringBuilder create(int maxNum) {
StringBuilder formula = new StringBuilder();
extraCopy = new StringBuilder(" ");
//符号个数 (1,2,3)
int signNum = (int)(Math.random()*3+1);
creatNum(formula,maxNum);
for(int i=0; i<signNum; i++) {
createSign(formula);
creatNum(formula,maxNum);
}
formula.append(OperatorVar.EQUAL_SIGN.getExpress() +" ");
return formula;
}
/**
* 随机生成操作数
* 并将操作数存入list中
* @param formula 字符串
* @param maxNum 数
* @return 返回参数的字符串
* */
public StringBuilder creatNum(StringBuilder formula,int maxNum) {
int numerator,denominator,type;
type = (int)(Math.random()*2);
//生成整数
if(type==0) {
do {
numerator =(int)(Math.random()*10);
}while(numerator > maxNum);
//备份分子/分母
extraCopy.append(numerator+"/"+1+" ");
formula.append(numerator+" ");
}
else {
do {
//随机生成分子
numerator = (int)(Math.random()*10);
//保证分母不等于0
while((denominator=(int)(Math.random()*10))==0);
}while(!numRange(numerator, denominator,maxNum));
//备份分子/分母
extraCopy.append(numerator+"/"+denominator+" ");
formula.append(HandleUtil.creatNum(numerator, denominator));
}
return formula;
}
/**
* 随机生成符号
* 并将符号存入list中
* @param formula 符号
* @return 返回符号
*/
public StringBuilder createSign(StringBuilder formula) {
//符号类型(+ - * /)
int signType = (int)(Math.random()*4+1);
switch (signType){
case 1 :
formula.append(OperatorVar.PLUS_SIGN.getExpress());
extraCopy.append(OperatorVar.PLUS_SIGN.getExpress());
break;
case 2 :
formula.append(OperatorVar.MINUS_SIGN.getExpress());
extraCopy.append(OperatorVar.MINUS_SIGN.getExpress());
break;
case 3 :
formula.append(OperatorVar.MULTIPLIED_SIGN.getExpress());
extraCopy.append(OperatorVar.MULTIPLIED_SIGN.getExpress());
break;
case 4 :
formula.append(OperatorVar.DIVISION_SIGN.getExpress());
extraCopy.append(OperatorVar.DIVISION_SIGN.getExpress());
break;
default:
}
extraCopy.append(" ");
formula.append(" ");
return formula;
}
/**
* 设定随机生成一定数目的式子,并将式子和答案分别存在formula和answer中
* @param num 生成的式子数目
* @param maxNum 最大值
*/
public void formulaNum(int num, int maxNum) throws IOException {
Long beginTime = System.currentTimeMillis();
//存放拆分完的式子
List<List<String>> formulaLists = new ArrayList<List<String>>(num);
formula = new ArrayList<StringBuilder>();
answer = new ArrayList<StringBuilder>();
//原始式子
StringBuilder singleFormula;
for(int i=0; formula.size()<num; i++) {
formula.add(singleFormula = create(maxNum));
CalculateUtil.calculateFormula(extraCopy);
//式子不符合规范(结果为负数),并且查重
if(extraCopy.charAt(0)=='@' || CheckUtil.judgeRepeat(singleFormula,formulaLists,extraCopy,answer)) {
formula.remove(formula.size()-1);
continue;
}
answer.add(extraCopy);
}
int i=0;
IODao.storageFile(formula,"Exercises.txt");
IODao.storageFile(answer,"Answers.txt");
System.out.println("生成时间: " + (System.currentTimeMillis()-beginTime));
}
/**
* 设定操作数的最大数值
* @param numerator 分子
* @param denominator 分母
* @param maxNum 最大值
* @return 是否超过最大值
*/
public boolean numRange(int numerator, int denominator,int maxNum) {
if((numerator/denominator)<maxNum) {
return true;
}else if((numerator/denominator)==maxNum) {
if((numerator%denominator)==0) {
return true;
}
}
return false;
}
}
点击查看代码
package main.com.examination.util;
public class HandleUtil {
/**
* 将答案按规范生成出来
* @param numerator 分子
* @param denominator 分母
* @return 式子
*/
public static StringBuilder creatNum(int numerator, int denominator) {
StringBuilder num = new StringBuilder();
int gcdNum = gcd(numerator, denominator);
numerator /= gcdNum;
denominator /= gcdNum;
if (numerator >= denominator) {
//分子大于等于分母
if (numerator % denominator == 0) {
//结果为整数
num.append(numerator / denominator + "");
} else {
//结果为带分数
int interger = numerator / denominator;
numerator = numerator - (interger * denominator);
num.append(interger + "'" + numerator + "/" + denominator + "");
}
} else {
//分子小于分母
if (numerator == 0) {
//分子等于0
num.append(0 + "");
} else {
//其他情况
num.append(numerator + "/" + denominator + "");
}
}
return num;
}
/**
* 求两数的最大公因数
* @param num01 数字1
* @param num02 数字2
* @return 返回公因数
*/
public static int gcd(int num01, int num02) {
int num;
while (num02 != 0) {
num = num01 % num02;
num01 = num02;
num02 = num;
}
return num01;
}
/**
* 将式子中指定字符的所有位序存储起来
* @param str 字符串
* @param formula 式子
* @return 返回位序
*/
public static int[] charFind(String str, StringBuilder formula) {
int[] indexs = new int[20];
for (int i = 0; ; i++) {
if (i == 0) {
indexs[i] = formula.indexOf(str, 0);
continue;
}
if (str.equals(" ") && (indexs[i - 1] == formula.length() - 1)) {
break;
}
if (str.equals(" ") || str.equals("/")) {
indexs[i] = formula.indexOf(str, indexs[i - 1] + 1);
}
if (str.equals("/") && (formula.length() - 1 - indexs[i] <= 4)) {
break;
}
}
return indexs;
}
/**
* 将指定数字字符串转为数字值
* @param formula 带查找的式子
* @param fromIndex 操作数前的空格位序
* @param endIndex 操作数后的空格位序
* @return 返回数字
*/
public static int changeNum(StringBuilder formula, int fromIndex, int endIndex) {
int num = -1;
//根据数字的位数进行转换
int sum = 0, temp;
for (int i = 1; i < (endIndex - fromIndex); i++) {
temp = (int) Math.pow((double) 10, (double) (i - 1));
num = (int) (formula.charAt(endIndex - i) - 48) * temp;
sum += num;
}
return sum;
}
/**
* 判断被减数、减数是否符合规范(true:符合;false:不符合)
* @param numerator1 第一个操作数的分子
* @param denominator1 第一个操作数的分母
* @param numerator2 第二个操作数的分子
* @param denominator2 第二个操作数的分母
* @return 返回是否正确
*/
public static boolean judge(int numerator1, int denominator1, int numerator2, int denominator2) {
int numerator = numerator1 * denominator2 - numerator2 * denominator1;
if (numerator < 0) {
return false;
}
return true;
}
/**
* 通过字符串将操作数的分子分母转成数字
* @param extraCopy 进行操作的字符串
* @param beginIndex 操作数前的空格位序
* @return 返回数字集
*/
public static int[] change(StringBuilder extraCopy, int beginIndex) {
int[] num = new int[3];
//存储空格的位序,方便找到完整的操作数
int[] blanks = charFind(" ", extraCopy);
//反斜杠的位置
int indexBl = -1 ,indexBa = extraCopy.indexOf("/", beginIndex);
for(int i=0; i<blanks.length; i++) {
if(blanks[i]==beginIndex) {
//找到传入空格位序在blanks中的位置
indexBl = i;
break;
}
}
num[0]=blanks[indexBl+1];//操作数后的空格位序
num[1]=changeNum(extraCopy,beginIndex,indexBa);//分子
num[2]=changeNum(extraCopy,indexBa,num[0]);//分母
return num;
}
}