shiqi323

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
这个作业属于哪个课程 计科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;
	}
}

2.检验
点击查看代码
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;
    }

}

3.生成题目
点击查看代码
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;
    }

}

4.答案
点击查看代码
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;
	}
}



#测试分析



posted on 2024-09-28 23:54  shiqilin  阅读(37)  评论(0编辑  收藏  举报