这个作业属于哪个课程 | 22级计科1班 |
---|---|
这个作业要求在哪里 | 作业要求 |
这个作业的目标 | 自动生成小学四则运算题目 |
github项目链接 | 链接 |
姓名&学号
姓名 | 学号 |
---|---|
万凯毅 | 3122004788 |
周彦安 | 3122004804 |
PSP
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 30 |
· Estimate | 估计这个任务需要多少时间 | 30 | 30 |
Development | 开发 | 1180 | 1180 |
· Analysis | 需求分析(包括学习新技术) | 60 | 60 |
· Design Spec | 生成设计文档 | 60 | 60 |
· Design Review | 设计复审 | 60 | 60 |
· Coding Standard | 代码规范(为当前的开发制定合适的规范) | 60 | 60 |
· Design | 具体设计 | 360 | 360 |
· Coding | 具体编码 | 360 | 360 |
· Code Review | 代码复审 | 100 | 100 |
· Test | 测试(自我测试、修改代码、提交修改) | 120 | 120 |
Reporting | 报告 | 160 | 160 |
· Test Report | 测试报告 | 100 | 100 |
· Size Measurement | 计算工作量 | 30 | 30 |
· Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 30 | 30 |
Total | 合计 | 1370 | 1370 |
功能
生成题目
输出
控制台输出
题目
答案
效能分析
优化前
每生成一个题目就使用append的形式写入文件
优化后
生成所有题目后,一次性写入文件
分析
优化前后生成10000道题目以及答案的耗时都是700ms左右,性能差别主要在文件io上,优化前相当于对文件做了10000次io,优化后只有一次io
设计实现过程
总体流程
生成单个题目流程
判断题目是否重复
利用多级树型结构逐层判断,如果到最后一层出现相同的,则认为是重复表达式:
代码说明
批量随机生成题目
每次生成题目时校验是不是负数、是否重复,如果不满足要求就不断重新生成
/*** 批量随机生成题目** @param range 操作数的大小范围* @param numberOfQuestions 最大题目数* @return 题目,答案*/public static Tuple2<List<String>, List<String>> generateQuiz(int range, int numberOfQuestions) {int duplicateCount = 0;int negativeCount = 0;int totalCount = 0;List<String> quizzes = new ArrayList<>(numberOfQuestions);List<String> answers = new ArrayList<>(numberOfQuestions);for (int i = 1; i <= numberOfQuestions; i++) {int maxOperators = generateRandomOperatorCounts();while (true) {totalCount++;List<String> operands = new ArrayList<>();List<String> operators = new ArrayList<>();for (int j = 0; j < maxOperators + 1; j++) {String lastOperator = operators.isEmpty() ? "" : operators.get(operators.size() - 1);Fraction operand = FractionUtils.generateRandomOperand(lastOperator, range);operands.add(operand.toString());if (j < maxOperators) {operators.add(generateRandomOperator());}}StringBuilder quiz = new StringBuilder();for (int j = 0; j < operands.size(); j++) {quiz.append(operands.get(j));if (j < operators.size()) {quiz.append(" ").append(operators.get(j)).append(" ");}}String expression = quiz.toString();if (operands.size() > 2 && RANDOM.nextBoolean()) {expression = addRandomParentheses(expression);}String postfix = ExpressionUtils.infixToPostfix(expression);Fraction result = ExpressionUtils.evaluatePostfix(postfix);if (Objects.nonNull(result)) {if (!DuplicateChecker.isDuplicate(result, expression)) {quizzes.add(i + ". " + expression);answers.add(i + ". " + result);break;} else {duplicateCount++;}} else {negativeCount++;}}}System.out.println("生成" + numberOfQuestions + "道题目完成,总次数:" + totalCount + ",重复次数:" + duplicateCount + ",负数次数:" + negativeCount);return new Tuple2<>(quizzes, answers);}
题目去重
// key:表达式的结果;value:map(key:表达式的长度,value:map集合(key:表达式中的每一个操作数或操作符;value:该字符串出现的次数))private static final Map<String, Map<Integer, List<Map<String, Integer>>>> DUMPLICATE_MAP = new HashMap<>();public static boolean isDuplicate(Fraction result, String expression) {// 已创建的表达式中,如果有计算结果相同,且表达式中的所有字符和出现的次数都一样,就认为是重复的String resultStr = result.toString();expression = expression.replaceAll("[()]", "");Integer length = expression.length();// 统计表达式中每个操作数和操作符出现的次数Map<String, Integer> characterCountMap = Arrays.stream(expression.split("\\s+")).collect(Collectors.groupingBy(Function.identity(),Collectors.collectingAndThen(Collectors.counting(), Long::intValue)));Map<Integer, List<Map<String, Integer>>> expressionLengthMap = DUMPLICATE_MAP.get(resultStr);if (expressionLengthMap != null) {// 存在计算结果相同的表达式List<Map<String, Integer>> characterCountMapList = expressionLengthMap.get(length);if (characterCountMapList != null) {// 存在长度相同的表达式boolean isDuplicate = characterCountMapList.stream().anyMatch(map -> map.equals(characterCountMap));if (isDuplicate) {// 存在操作数和操作符出现次数相同的表达式return true;}}} else {expressionLengthMap = new HashMap<>();DUMPLICATE_MAP.put(resultStr, expressionLengthMap);}List<Map<String, Integer>> characterCountMapList = expressionLengthMap.computeIfAbsent(length, k -> new ArrayList<>());characterCountMapList.add(characterCountMap);return false;}
判题
输出
控制台输出
结果输出
效能分析
分析:对10000道题目和答案进行校验,耗时主要在题目结果的计算上
测试和异常情况
- args参数:
- -n 和 -r 的参数如果没有就是默认值10
- -n 和 -r 的参数必须是整数类型的值
- -e 和 -a 的参数必须同时存在或不存在
- -e 和 -a 参数不满足格式 xxx.txt
- -e 和 -a 参数的文件必须存在
- 校验题目:题目数和答案数必须一致
- 生成题目:-r 给出的参数不支持生成 -n 的题目数
项目小结
本项目开发了一个智能数学题目生成器,能够随机生成包含加法、减法、乘法和除法的数学表达式,并支持题目的有效性校验与答案计算。加强了代码规范,性能优化,设计分析以及团队合作的能力。