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

结对项目

时间:2024-09-28 20:01:45浏览次数:1  
标签:结对 String 项目 fenZi fenMu Fraction new public

作业属于的课程 计科22级12班
作业要求 要求
作业目标 团队合作实现一个自动生成小学四则运算题目的命令行程序
姓名 学号
兰勇 3122004783

GitHub链接:链接

一、PSP表格

PSP2.1 描述 预估耗时(分钟) 实际耗时(分钟)
Planning
· Estimate 估计这个任务需要多少时间 60 60
Development
· Analysis 需求分析 (包括学习新技术) 120 180
· Design Spec 生成设计文档 60 60
· Design Review 设计复审 30 30
· Coding Standard 代码规范 (为目前的开发制定合适的规范) 30 30
· Design 具体设计 120 180
· Coding 具体编码 480 600
· Code Review 代码复审 60 30
· Test 测试(自我测试,修改代码,提交修改) 60 60
Reporting
· Test Report 测试报告 120 90
· Size Measurement 计算工作量 10 10
· Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 30 30
合计 1120 1360

二、效能分析


关于程序计算的部分,本来是希望通过将字符串转化为浮点型进行运算,但思考可以将所有的计算直接以字符串的形式进行,只是需要重新编写计算用的类。经过比较两者的性能后,最后决定使用自己编写计算用的Calculate类,比原来的性能有所提高。

三、设计实现过程

3.1项目结构

3.2具体实现

  • Fraction类:自定义分数类,包含分数的分子分母,以及提供分数间加减乘除运算的函数。
点击查看代码
package lan;
import java.util.Random;
public class Fraction {
    //分数的分子分母
    public long fenZi;
    public long fenMu;
    private static Random random=new Random();
    //随机构造分数
    public Fraction(int range){
        this.fenMu=random.nextInt(range-1)+1;
        this.fenZi=random.nextInt(range);
        simplify();
    }
    //由小数构造一个分数
    /*public Fraction(double xiaoShu){
        // 将小数转换为字符串以便处理
        String xiaoShuString = String.valueOf(xiaoShu);
        // 获取小数点的位置
        int decimalPlaces = xiaoShuString.length() - xiaoShuString.indexOf('.') - 1;
        // 计算分母
        this.fenMu = (long)Math.pow(10, decimalPlaces);
        // 计算分子
        this.fenZi = (long) (xiaoShu * fenMu);
        simplify();
    }*/
    //指定分子分母
    public Fraction(long fenZi,long fenMu){
        this.fenMu=fenMu;
        this.fenZi=fenZi;
        simplify();
    }
    // 分数加法
    public static Fraction add(Fraction a,Fraction b) {
        long newFenZi = a.fenZi * b.fenMu + b.fenZi * a.fenMu;
        long newFenMu = a.fenMu * b.fenMu;
        return new Fraction(newFenZi, newFenMu);
    }

    // 分数减法
    public static Fraction sub(Fraction a,Fraction b) {
        long newFenZi = a.fenZi * b.fenMu - b.fenZi * a.fenMu;
        long newFenMu = a.fenMu * b.fenMu;
        return new Fraction(newFenZi, newFenMu);
    }

    // 分数乘法
    public static Fraction mul(Fraction a,Fraction b) {
        long newFenZi = a.fenZi * b.fenZi;
        long newFenMu = a.fenMu * b.fenMu;
        return new Fraction(newFenZi, newFenMu);
    }

    // 分数除法
    public static Fraction div(Fraction a,Fraction b) {
        if (b.fenZi == 0) {
            throw new ArithmeticException("Cannot divide by zero.");
        }
        long newFenZi = a.fenZi * b.fenMu;
        long newFenMu = a.fenMu * b.fenZi;
        return new Fraction(newFenZi, newFenMu);
    }
    // 计算最大公约数并简化分数
    private void simplify() {
        long gcd = Calculate.gcd(this.fenZi, fenMu);
        this.fenZi /= gcd;
        this.fenMu /= gcd;
        // 确保分母为正
        if (fenMu < 0) {
            fenMu = -fenMu;
            fenZi = -fenZi;
        }
    }
    public static void main(String[] args){
        Fraction f1=new Fraction(10);
        System.out.println(Conversion.getFractionToString(f1));
        Fraction f2=new Fraction(10);
        System.out.println(Conversion.getFractionToString(f2));
        System.out.println(Conversion.getFractionToString(add(f1,f2)));
        System.out.println(Conversion.getFractionToString(sub(f1,f2)));
        System.out.println(Conversion.getFractionToString(mul(f1,f2)));
        System.out.println(Conversion.getFractionToString(div(f1,f2)));
    }
}
  • Conversion类:方法类,提供将字符串转化为小数、分数以及将分数转化为字符串的函数。

  • Formula类:自定义公式类,提供合法公式的构造方法,实现获取公式答案以及以字符串形式获取公式。

  • Calculate类:计算方法类,实现计算一个以字符串输入的公式的答案。

  • IOUtil类:实现对文件的写入与读取。

  • MainUI类:提供要求要实现的两个功能,即随机创建一定数量的公式的函数和对比答案文件的函数。

  • Myapp类:主类,供用户使用。

点击查看代码
public class Myapp {
    public static void main(String[] args){
        if(args.length<4){
            System.out.println("参数不足,请按如下格式输入:Myapp.exe -n <题目数量> -r <题目数值范围>");
            return;
        }

        if(args[0].equals("-n")&&args[2].equals("-r")){
            MainUI.creatFormula(Integer.parseInt(args[1]),Integer.parseInt(args[3]));
            System.out.println("题目创建成功!题目文件为Exercise.txt,对应答案文件为Answer.txt。");
            System.out.println("作答请按如下格式输入:Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt");
        }
        else if(args[0].equals("-e")&&args[2].equals("-a")){
            MainUI.checkFormula(args[1],args[3]);
        }
        else {
            System.out.println("输入参数有误,请按如下格式输入:Myapp.exe -n <题目数量> -r <题目数值范围> 或 Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt");
        }
    }
}

四、代码说明

4.1 Formula类构造公式。

定义一个公式类。具体构造方法为,先随机公式符号个数,根据符号个数随机生成数字以及符号,在将其以字符串的形式连接起来后,随机插入括号。然后执行对算式的检查(是否出现负数,公式之间是否重复等),如果检查不通过,则重复上述步骤至生成合法的公式。
点击查看代码
public class Formula {
    private Random random=new Random();
    //符号个数
    private  int symbolNum=random.nextInt(3)+1;
    //算式符号数组
    private  String[] symbol=new String[symbolNum];
    //算式数字数组
    private  String[] num=new String[symbolNum+1];
    //算式字符串
    private  String formula;
    //算式答案
    private  String answer;
    //构造函数
    public Formula(int range) {
        do {
            //生成随机符号
            for (int i = 0; i < symbolNum; i++)
                symbol[i] = getSymbol();
            //生成随机数字
            for (int i = 0; i < symbolNum + 1; i++) {
                if (random.nextInt(2) == 0) {//生成自然数
                    num[i] = String.valueOf(random.nextInt(range));
                } else {//生成分数
                    Fraction fraction = new Fraction(range);
                    num[i] = Conversion.getFractionToString(fraction);
                }
            }
            //降低式子中存在负数的可能
            for (int i = 0; i < symbolNum; i++)
                if (symbol[i].equals(" - "))
                    if (Conversion.getStringToDigit(num[i]) < Conversion.getStringToDigit(num[i + 1])) {
                        //临时变量用于交换
                        String temp;
                        temp = num[i];
                        num[i] = num[i + 1];
                        num[i + 1] = temp;
                    }
            //符号与数字组合得到算式
            formula = num[0];
            for (int i = 0; i < symbolNum; i++) {
                formula = formula + symbol[i] + num[i + 1];
            }
            if (random.nextInt(2) == 0)//0则为算式插入括号
                formula = insertKuoHao(formula);
            formula=formula+" = ";
            answer=Calculate.getFormulaAnswer(formula);
        }while(answer.equals("error"));
    }
    public String getFormula(){return formula;}
    public String getAnswer(){
        return answer;
    }
    //随机插入括号
    private String insertKuoHao(String str){
        StringBuilder newExpression = new StringBuilder();
        int start=random.nextInt(symbolNum);
        int end=random.nextInt(symbolNum-start)+start+1;
        for(int i=0;i<=symbolNum;i++){
            if(i==start)
                newExpression.append("(");
            newExpression.append(num[i]);
            if(i==end)
                newExpression.append(")");
            if(i<symbolNum)
                newExpression.append(symbol[i]);
        }
        return newExpression.toString();
    }
    //给出随机符号
    private String getSymbol(){
        String[] symbol={" + "," - "," × "," ÷ "};
        return symbol[random.nextInt(symbol.length)];
    }
}

4.2 Calculate类实现公式计算。

公式以字符串的形式输入,再根据一定的运算规则得出答案以字符串返回。

点击查看代码
public class Calculate {
    // 计算最大公约数
    public static long gcd(long a, long b) {
        while (b != 0) {
            long temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }
    //计算算式
    public static String getFormulaAnswer(String formula){
        char[] tokens = formula.toCharArray();
        Stack<String> values = new Stack<>(); // 存储操作数
        Stack<Character> operators = new Stack<>(); // 存储操作符
        String answer;
        for (int i = 0; i < tokens.length; i++) {
            // 当前字符是空格,跳过
            if (tokens[i] == ' ') continue;

            // 当前字符是数字
            if (Character.isDigit(tokens[i])) {
                StringBuilder sb = new StringBuilder();
                while (i < tokens.length &&( Character.isDigit(tokens[i]) || tokens[i]=='\'' || tokens[i]=='/')) {
                    sb.append(tokens[i++]);
                }
                values.push(sb.toString());
                i--; // 回退一个位置,继续处理
            }
            // 当前字符是左括号
            else if (tokens[i] == '(') {
                operators.push(tokens[i]);
            }
            // 当前字符是右括号
            else if (tokens[i] == ')') {
                while (!operators.isEmpty() && operators.peek() != '(') {
                    answer=applyOperator(operators.pop(), values.pop(), values.pop());
                    if(isRight(answer))
                        values.push(answer);
                    else
                        return "error";
                }
                operators.pop(); // 弹出左括号
            }
            // 当前字符是操作符
            else if (isOperator(tokens[i])) {
                while (!operators.isEmpty() && hasPrecedence(tokens[i], operators.peek())) {
                    answer=applyOperator(operators.pop(), values.pop(), values.pop());
                    if(isRight(answer))
                        values.push(answer);
                    else
                        return "error";
                }
                operators.push(tokens[i]);
            }
        }
        // 处理剩余的操作符
        while (!operators.isEmpty()) {
            answer=applyOperator(operators.pop(), values.pop(), values.pop());
            if(isRight(answer))
                values.push(answer);
            else
                return "error";
        }

        return values.pop();
    }
    //运算法则
    private static String applyOperator(char op, String stra,String strb) throws IllegalArgumentException, ArithmeticException {
        Fraction a=Conversion.getStringToFraction(stra);
        Fraction b=Conversion.getStringToFraction(strb);
        switch (op) {
            case '+':
                return Conversion.getFractionToString(Fraction.add(a,b));
            case '-':
                return Conversion.getFractionToString(Fraction.sub(a,b));
            case '×':
                return Conversion.getFractionToString(Fraction.mul(a,b));
            case '÷':
                if(Conversion.getFractionToString(b).equals("0"))
                    return "error";
                return Conversion.getFractionToString(Fraction.div(a,b));
            default:
                throw new IllegalArgumentException("Invalid operator: " + op);
        }
    }
    //验证答案合理性
    private static boolean isRight(String answer){
        if(answer.equals("error"))
            return false;
        return !(Conversion.getStringToDigit(answer) <= 0);
    }
    //判断运算符
    private static boolean isOperator(char c) {
        return c == '+' || c == '-' || c == '×' || c == '÷';
    }
    //决定运算符优先级
    private static boolean hasPrecedence(char op1, char op2) {
        if (op2 == '(' || op2 == ')') return false;
        return (op1 != '×' && op1 != '÷') || (op2 != '+' && op2 != '-');
    }
}

4.3 MainUI类实现具体命令。

creatFormula()通过重复调用Formula构造函数,创建Formula实例,然后通过Formula提供的方法将公式以字符串的形式和对应答案按照一定格式写入文件中。
checkAnswer()通过读取两个文件中的内容对比后得知答案的对错,并将其按一定格式写入另一个文件中。
点击查看代码
public class MainUI {
    public static void creatFormula(int range,int amount){
        IOUtil.clearFile("Exercises.txt");
        IOUtil.clearFile("Answers.txt");
        for(int i=0;i<amount;i++){
            Formula formula=new Formula(range);
            IOUtil.writeToFile("Exercises.txt",(i+1)+"."+formula.getFormula());
            IOUtil.writeToFile("Answers.txt",(i+1)+"."+formula.getAnswer());
        }
    }
    public static void checkFormula(String execrisePath,String answerPath){
        ArrayList<String> correct=new ArrayList<>();
        ArrayList<String> wrong=new ArrayList<>();
        try (BufferedReader reader1 = new BufferedReader(new FileReader(answerPath));
             BufferedReader reader2 = new BufferedReader(new FileReader("Answers.txt"))) {
            String line1;
            String line2;
            while ((line1 = reader1.readLine()) != null&&(line2 = reader2.readLine()) != null) {
                String[] parts1=line1.split("\\.");
                String[] parts2=line2.split("\\.");
                if(parts1[1].equals(parts2[1])){
                    correct.add(parts1[0]);
                }else {
                    wrong.add(parts1[0]);
                }
            }
            IOUtil.clearFile("Grade.txt");
            IOUtil.writeToFileAppend("Grade.txt","Correct:"+correct.size()+"(");
            int correctSize= correct.size();
            for(int i=0;i<correctSize;i++) {
                if(i!=correctSize-1)
                    IOUtil.writeToFileAppend("Grade.txt", correct.get(i)+",");
                else{
                    IOUtil.writeToFile("Grade.txt", correct.get(i));
                }
            }
            IOUtil.writeToFile("Grade.txt", ")");
            IOUtil.writeToFileAppend("Grade.txt","Wrong:"+wrong.size()+"(");
            int wrongSize= wrong.size();
            for(int i=0;i<wrongSize;i++){
                if(i!=wrongSize-1)
                    IOUtil.writeToFileAppend("Grade.txt", wrong.get(i)+",");
                else{
                    IOUtil.writeToFileAppend("Grade.txt", wrong.get(i));
                }
            }
            IOUtil.writeToFileAppend("Grade.txt", ")");
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("未找到要检查的文件!");
        }
    }
}

五、测试运行

  1. 测试命令形式输入有误。
  2. 测试Fraction类中分数的加减乘除运算。
    两个分数分别为1和7/9,对比加减乘除运算后发现答案正确。
  3. 测试Calculate类计算是否正确
    测试代码

测试结果

  1. 测试MainUI类生成10000道题目
    测试结果

  2. 测试MainUI类检测答案
    测试例子

测试结果

六、项目小结

这一次项目虽然没有找到人能够结对,但也给我带来了不少收获,提高了我编写代码的能力,同时因为是一个人搞项目,需要考虑的更加全面,提高了我实现项目的能力。但同时这个项目也有点不足,没有完美实现作业要求,对于检查重复题目这个功能的实现,我只能考虑是将公式中的符号数组,数字数字,以及答案相互比较,考虑淘汰掉完全一致的公式,但这样所需要的时间应会明显增多,考虑到完成项目的时间较紧,故而没有能够实现。

标签:结对,String,项目,fenZi,fenMu,Fraction,new,public
From: https://www.cnblogs.com/creatAye/p/18435112

相关文章

  • 结对项目
    这个作业属于哪个课程软件工程这个作业要求在哪里结对项目这个作业的目标组队实现一个自动生成小学四则运算题目的程序成员3122004487林丙昆成员3122004502赵衍锴GitHub地址:地址PSP2.1表格PSP2.1PersonalSoftwareProcessStages预估耗时(分钟......
  • 结对项目
    这个作业属于哪个课程计科22级12班这个作业要求在哪里结对项目这个作业的目标实现一个自动生成小学四则运算题目的命令行程序成员梁晓君(3222004682)阿丽娅·阿力木(3222004678)github地址作业github链接PSP表格PSP2.1PersonalSoftwareProcessStag......
  • 结对项目
    这个作业属于哪个课程https://edu.cnblogs.com/campus/gdgy/CSGrade22-34这个作业要求在哪里https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13230这个作业的目标实现一个自动生成小学四则运算题目的命令行程序github地址:https://github.com/zjm72......
  • 结对项目
    这个作业属于哪个课程https://edu.cnblogs.com/campus/gdgy/CSGrade22-34这个作业要求在哪里https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13230成员李嘉锐(3122004951)林进光(3122004955)github链接https://github.com/bitpurleclude/3122004951Ma......
  • 结对项目:自动生成小学四则运算题目
    这个作业属于哪个课程https://edu.cnblogs.com/campus/gdgy/CSGrade22-34这个作业要求在哪里https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13230这个作业的目标结对实现一个自动生成小学四则运算题目的命令行程序项目一、项目开发人员以及仓库地......
  • 结对项目
    这个作业属于哪个课程班级的链接这个作业要求在哪里作业要求的链接这个作业的目标要求实现一个自动生成小学四则运算题目的命令行程序一、合作成员学号姓名github仓库地址3122004819陈善能https://github.com/alenbear/Elementary_arithmetic31......
  • 结对项目
    这个作业属于哪个课程软件工程这个作业要求在哪里结对项目这个作业的目标实现一个自动生成小学四则运算题目的命令行程序项目成员车峤锐3122004471黄梓洋3122004481Github项目地址:项目链接2.PSP表格(计划时间)PSP表格(个人软件过程)PSP2.1Persona......
  • 结对项目
    结对项目课程链接https://edu.cnblogs.com/campus/gdgy/CSGrade22-34作业要求https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13230作业目标实现四则运算程序,掌握结对合作完成项目的技巧成员1沈思敏3122004877成员2郑灿嘉3122004887git......
  • 结对项目
    结对项目:自动生成小学四则运算题目的命令行程序这个作业属于哪个课程计科22级12班这个作业要求在哪里作业要求这个作业的目标二人合作实现一个自动生成小学四则运算题目的命令行程序姓名学号GitHub链接余李烨3222004854https://github.com/yeyezi1......
  • 结对项目
    这个作业属于哪个课程https://edu.cnblogs.com/campus/gdgy/CSGrade22-12这个作业要求在哪里https://edu.cnblogs.com/campus/gdgy/CSGrade22-12/homework/13221姓名学号阿依努尔•麦麦提艾麦尔3222004805Github项目地址https://github.com/3636492812/36364......