首页 > 其他分享 >结对项目

结对项目

时间:2024-09-24 21:13:08浏览次数:8  
标签:结对 运算 项目 int denominator new expression append

一、作业信息

Github仓库地址:https://github.com/guzhouyiye/MathFormulaGenerator

这个作业属于哪个课程 软件工程
这个作业要求在哪里 结对项目
这个作业的目标 实现一个自动生成小学四则运算题目的命令行程序
姓名 学号
陈国金 3122004301
廖俊龙 3118005817

二、PSP表格

PSP2.1 Personal Software Process Stages 预估耗时 (分钟) 实际耗时(分钟)
Planning 计划 30 30
·Estimate ·估计这个任务需要多少时间 30 30
Development 开发 1010 1370
·Analysis ·需求分析(包括学习新技术) 50 40
·Design Spec ·生成设计文档 20 20
·Design Review ·设计复审 10 5
·Coding Standard ·代码规范(为目前的开发制定合适的规范) 10 15
·Design ·具体设计 30 40
·Coding ·具体编码 850 1150
·Code Review ·代码复审 30 60
·Test ·测试(自我测试,修改代码,提交修改) 10 40
Reporting 报告 65 75
·Test Repor ·测试报告 30 40
·Size Measurement ·计算工作量 15 10
·Postmortem & Process Improvement Plan ·事后总结,并提出过程改进计划 20 25
合计 1100 1475

三、模块接口的设计与实现过程

项目类与方法设计

1.MainMathFormulaGenerator类

用于接收命令行参数,调用其他工具类实现生成四则运算题目功能

2.CommandLineParser类

用于读取命令行参数,并对参数输入出现的问题进行输出。get方法用来得到所要的参数,has方法用来判断某个参数是否存在

3.EvaluateExpression类

用于计算运算式的类。其中expressionResult方法是主方法,其他类通过调用这个方法来获得计算结果。countExpression方法由expressionResult调用,根据提供的参数对某个范围的运算式进行计算。count方法由countExpression方法调用,用于两个数中间的加减乘除运算。commonDenominator方法用于给分数通分,由count调用,方便分数之间的运算。

4.existJudge类

负责判断运算式是否已经存在的类,通过对每两个式子进行compareNumberCount,compareResult,compareAllOperatorFix,compareAllNumberFix四个方法的调用比较来确定是否存在。

5.Expression类

负责生成运算式的类。Expression为该类的构造函数。generateNumber方法 随机生成自然数或真分数;simplify方法用于约分,gcd方法由simplify调用,来求最大公因数;generateOperator方法随机生成运算符;generateBracket随机生成括号。

6.ExpressionSet类

负责统筹所有运算式生成并写入文件的类。ExpressionSet构造函数由main方法调用,会通过调用其他工具类,根据参数生成对应数量的合法的运算式。并通过expressionsWrite方法和answersWrite方法将要写进文件的内容添加到字符串中,最后将字符串写入文件

7.JudgeExpressions类

负责判题的类。JudgeExpressions为构造函数,由main方法调用,通过调用其他工具类,判断并记录题目对错信息,最后写入文件。writeResult方法将将判题结果写入文件;rightJudge方法比较答案是否相同;separateExpression方法将运算式拆分成运算式、运算符、括号,然后调用EvaluateExpression类进行计算;getNumber将字符串里的运算数转化为Number类。

8.Number

用于创建对象,Number对象以分数形式保存运算数和答案,方便统一运算数的计算

流程图

四、效能分析

使用IDEA内置的IntelliJ Profile进行性能分析

1.热点图

2.方法列表

程序中消耗最大的函数

性能改进

消耗最大的方法是关于判断运算式是否出现过。改进时切换判断的顺序,使判断更高效。消耗时间从130ms减少到70ms

五、代码说明

ExpressionSet类,根据参数批量生成运算式,符合要求则添加,不符要求会排除重新生成。最后把运算式即答案写入文件。

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

// 负责统筹所有运算式生成并写入文件的类
public class ExpressionSet {

    // 所有式子的列表
    List<Expression> allExpression = new ArrayList<>();

    // 创建文件
    FileWriter expressionsTxt = new FileWriter("Exercises.txt");
    FileWriter answersTxt = new FileWriter("Answers.txt");

    StringBuilder expressionsString = new StringBuilder();
    StringBuilder answersString = new StringBuilder();


    public ExpressionSet(int questionCount, int range) throws IOException {

        // 生成questionCount条式子
        for (int i = 0; i < questionCount; i++) {
            // 生成一条式子
            Expression expression = new Expression(range);

            // 如果式子结果不是负数且没有出现过
            ExistJudge existJudge = new ExistJudge();
            if (expression.result.denominator > 0 && !existJudge.existJudge(allExpression, expression)) {
                allExpression.add(expression);
                // 添加运算式到运算式字符串中
                expressionsWrite(expressionsString, allExpression.get(i), i);
                // 添加答案到答案字符串中
                answersWrite(answersString, allExpression.get(i), i);
            }
            // 如果不合法
            else {
                i--;
            }
        }
        expressionsTxt.write(expressionsString.toString());
        answersTxt.write(answersString.toString());
        expressionsTxt.close();
        answersTxt.close();
    }

    // 运算式写入运算式字符串
    private void expressionsWrite(StringBuilder expressionsString, Expression expression, int i) {
        expressionsString.append(i + 1).append(". ");
        // 同个位置判断顺序:'(' 运算数 运算符 ')'
        // 左右括号判断(0左1右)
        int judgeBracket = 0;
        for (int j = 0; j < expression.numberCount; j++) {
            // 添加左括号
            if (expression.allBracket.size() == 2 && expression.allBracket.get(0) == j && judgeBracket == 0) {
                expressionsString.append("(");
                judgeBracket++;
            }
            // 添加运算数
            // 自然数
            if (expression.allNumberFix.get(j).denominator == 1 || expression.allNumberFix.get(j).numerator == 0) {
                expressionsString.append(expression.allNumberFix.get(j).numerator);
            }
            // 分数
            else {
                // 判断真假分数
                int judge = expression.allNumberFix.get(j).numerator / expression.allNumberFix.get(j).denominator;
                // judge<1,真分数
                if (judge < 1) {
                    expressionsString.append(expression.allNumberFix.get(j).numerator).append("/").append(expression.allNumberFix.get(j).denominator);
                }
                // judge>=1,假分数
                else {
                    // 提取假分数的整数部分judge后,需要修改分数部分的分子
                    int fractionNumerator = expression.allNumberFix.get(j).numerator % expression.allNumberFix.get(j).denominator;
                    Number temp = expression.simplify(new Number(fractionNumerator, expression.allNumberFix.get(j).denominator));
                    expressionsString.append(judge).append("’").append(temp.numerator).append("/").append(temp.denominator);
                }
            }
            // 添加右括号
            if (expression.allBracket.size() == 2 && expression.allBracket.get(1) == j + 1 && judgeBracket == 1) {
                judgeBracket++;
                expressionsString.append(")");
            }
            // 添加运算符
            if (j < expression.numberCount - 1) {
                expressionsString.append(" ").append(expression.allOperatorFix.get(j)).append(" ");
            }
        }
        expressionsString.append(" =" + "\n");
    }

    // 答案写入答案字符串
    private void answersWrite(StringBuilder answersString, Expression expression, int i) {
        answersString.append(i + 1).append(". ");
        // 自然数
        if (expression.result.denominator == 1) {
            answersString.append(expression.result.numerator).append("\n");

        }
        // 分数
        else {
            // 判断真假分数
            int judge = expression.result.numerator / expression.result.denominator;
            // judge<1,真分数
            if (judge < 1) {
                answersString.append(expression.result.numerator).append("/").append(expression.result.denominator).append("\n");
            }
            // judge>=1,假分数
            else {
                // 提取假分数的整数部分judge后,需要修改分数部分的分子
                int fractionNumerator = expression.result.numerator % expression.result.denominator;
                answersString.append(judge).append("’").append(fractionNumerator).append("/").append(expression.result.denominator).append("\n");
            }
        }
    }
}

Expression类,用于随机生成运算式中的运算数、运算符和括号位置,十分关键。

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

// 负责生成运算式的类
public class Expression {
    private final Random random = new Random();
    
    int numberCount = random.nextInt(3) + 2;    // 运算式的数量(2,3,4)
    List<Number> allNumber = new ArrayList<>();       // 运算式列表
    List<Character> allOperator = new ArrayList<>();  //  运算符列表
    List<Integer> allBracket = new ArrayList<>();     // 括号位置列表,位于相同索引运算数的左边

    // 用来复制上面两个列表,作为固定列表,在判断运算式是否已经存在时使用
    List<Number> allNumberFix = new ArrayList<>();
    List<Character> allOperatorFix = new ArrayList<>();

    // 用来存储运算结果
    Number result = new Number(); 

    public Expression(int range) {
        // 生成数字、符号、括号列表
        generateNumber(range);
        generateOperator();
        generateBracket();
        EvaluateExpression evaluateExpression = new EvaluateExpression();
        result = evaluateExpression.expressionResult(allNumber, allOperator, allBracket, 0, allOperator.size());
    }
    public Expression() {
    }

    // 随机生成自然数或真分数
    private void generateNumber(int range) {
        for (int i = 0; i < numberCount; i++) {
            Number number = new Number();
            boolean isFraction = random.nextBoolean();
            // 生成分数(分母不为0)
            if (isFraction) {
                number.numerator = random.nextInt(range * (range - 1) -1) + 1;

                //
                int productNumberCount = 0;
                do{
                    number.denominator = random.nextInt(range - 1) + 1;
                    if(productNumberCount++ == 10000000) System.out.println("参数r太小啦");
                }while(number.numerator % number.denominator == 0 || number.numerator / number.denominator >= range);
                // 调用simplify方法化简分数
                number = simplify(number);
            }
            // 生成自然数
            else {
                number.numerator = random.nextInt(range - 1) + 1;
                number.denominator = 1;
            }
            allNumber.add(number);
        }
        // 深拷贝 allNumber 到 allNumberFix
        for (Number num : allNumber) {
            // 这里为每个 num 创建一个新的 Number 对象,确保 allNumberFix 拷贝的是新的对象
            allNumberFix.add(new Number(num.numerator, num.denominator));
        }

    }

    // 约分
    public Number simplify(Number number) {
        int numerator = number.numerator;
        int denominator = number.denominator;
        int gcd = gcd(numerator, denominator);
        number.numerator = numerator / gcd;
        number.denominator = denominator / gcd;
        return number;
    }
    // 求最大公因数
    private int gcd(int a, int b) {
        return b == 0 ? a : gcd(b, a % b);
    }

    // 随机生成运算符
    private void generateOperator(){
        Character[] operator = new Character[]{'+', '-', '×', '÷'}; //运算符号
        // 运算符数量是运算式数量减一
        for (int i = 0; i < numberCount - 1; i++) {
            allOperator.add(operator[random.nextInt(operator.length)]);
        }
        allOperatorFix = new ArrayList<>(allOperator);
    }

    // 随机生成括号
    private void generateBracket(){
        // 括号个数0或1
        if(numberCount > 2 && random.nextBoolean()) {
            int index = random.nextInt(numberCount - 1);
            allBracket.add(index);
            allBracket.add(index + 2);
        }
    }
}

六、测试说明

参数缺失


参数范围不正确


正确的输入输出

-n,-r参数



-e,-a参数

用标准答案,所以全部正确

使用自己故意修改前两道答案的MyAnswers.txt文件

生成一万道题目


七、项目小结

  在这次四则运算自动生成项目中,我们通过分工合作,充分发挥各自的优势,实现了一个功能相对完整的工具。项目主要包括题目的生成、答案的计算与判断,以及结果的输出。
  这次的主要收获有几个方面。一是在开发过程中,我们学会了如何有效地分工与协作。通过定期沟通,确保了各模块之间的顺利对接,提升了项目效率。二是在实现过程中,我们个人的技术得到,对项目开发有更深刻的理解。三是提升问题解决能力,项目中遇到的各种问题,锻炼了我们的调试和解决问题的能力。通过查阅资料和互相讨论,我们逐步克服了困难。
  通过这个项目,我们不仅提升了技术能力,也增进了团队之间的默契,为今后更多的合作奠定了良好的基础。

标签:结对,运算,项目,int,denominator,new,expression,append
From: https://www.cnblogs.com/guzhouyiye/p/18430036

相关文章

  • 如何在django项目中启动websocket服务
    首先下载redis,windows上要下5.0以上的版本,链接为:Releases·tporadowski/redis(github.com)紧接着python要安装redis,channls以及daphne,asgi_redis然后在settings中配置 必须放在第一行,以及channlesWSGI_APPLICATION="start_up_file_km.wsgi.application"ASGI_APPLICATI......
  • 基于SSM的选题管理系统(有报告)。Javaee项目。
    演示视频:基于SSM的选题管理系统(有报告)。Javaee项目。项目介绍:采用M(model)V(view)C(controller)三层体系结构,通过Spring+SpringMvc+Mybatis+Jsp+Maven来实现。MySQL数据库作为系统数据储存平台,实现了基于B/S结构的Web系统。系统设计思想一个成功的网站应明确......
  • Office project 2019安装图文安装教程下载项目管理
    不仅可以快速、准确地创建项目计划,而且可以帮助项目经理实现项目进度、成本的控制、分析和预测,使项目工期大大缩短,资源得到有效利用,提高经济效益。是专案管理软件程序由微软开发销售。软件设计目的在于协助专案经理发展计划、为任务分配资源、跟踪进度、管理预算和分析工作量。......
  • 微服务项目部署
    将微服务部署到云服务器是微服务架构中的常见操作,以下是三种常见的部署方式:使用SpringBoot内嵌Tomcat、Docker容器化、和外部Tomcat部署。1.使用SpringBoot内嵌Tomcat部署这是SpringBoot微服务最常用、也是最简单的部署方式。SpringBoot项目内置了Tomcat......
  • 结对项目:生成四则运算
    个人项目——论文查重这个作业属于哪个课程计科12班这个作业的要求在哪里作业要求这个作业的目标实现一个自动生成小学四则运算题目的命令行程序github链接:github题目需求使用-n参数控制生成题目的个数,例如Myapp.exe-n10将生成10个题目。使用-......
  • 使用通义灵码,参与开源项目全程纪实
    作者:shuipin100-34561背景缘起OceanBase。作为一个充满好奇心的DBA,一直一来想探寻数据库的内部世界。开源为我们这些好奇的猫打开了一扇新世界的大门。OceanBase作为分布式关系型数据库的排头兵,自然进入了我的优选名单。起初走进了OceanBase[1]的世界逛了一圈,这对于一个......
  • 使用通义灵码,参与开源项目全程纪实
    作者:shuipin100-34561背景缘起OceanBase。作为一个充满好奇心的DBA,一直一来想探寻数据库的内部世界。开源为我们这些好奇的猫打开了一扇新世界的大门。OceanBase作为分布式关系型数据库的排头兵,自然进入了我的优选名单。起初走进了OceanBase[1]的世界逛了一圈,这对于一个......
  • 我无法在我的项目 Pycharm 中导入 opencv-python
    (myvenv)PSE:\Python>pipinstallopencv-pythonCollectingopencv-pythonUsingcachedopencv-python-4.10.0.84.tar.gz(95.1MB)Installingbuilddependencies...errorerror:subprocess-exited-with-error×pipsubprocesstoinstallbuilddepe......
  • springboot项目打包部署到linux运行经验:
    打包:1.mvn:clean2.mvn:package 生成的snapshop-jar包在target下。部署(采用idea自带的ftp) 1.idea的tools-deployment中配置虚拟机的连接,包括用户名,密码,端口 2.配置idea的mapping文件目录映射(随便写),就可以打开虚拟机目录了 3.将idea的target下生产的jar包直接拖动到虚......
  • 面试官:项目中如何实现分布式锁?
    分布式锁(DistributedLock)是一种用于分布式系统中的同步机制,主要是为了防止分布式系统中,多个服务实例同时操作一个共享资源所带来的并发安全问题。分布式锁确保在同一时间只有一个实例操作共享资源,从而保证了数据的安全性。1.分布式锁实现方案分布式锁的实现方案有多种,例如以......