Twinning project (java achieve)

A, Github project Address: https://github.com/734635746/MyApp

 


 

Two, PSP form

 

PSP2.1 Personal Software Process Stages Estimated time consuming (minutes) The actual time-consuming (minutes)
Planning plan 30 30
· Estimate • Estimate how much time this task requires 30 30
Development Develop    
· Analysis · demand analysis  120 120
· Design Spec Generate design documents 60 70
· Design Review · Design Review  40 60
· Coding Standard · Code Specification 30 40
· Design · Specific design 100 90
· Coding · Specific coding 800 800
· Code Review · Code Review  60 60 
· Test · Test (self-test, modify the code, submit modifications)    
Reporting report    
· Test Report · testing report    
· Size Measurement · Computing workload    
· Postmortem & Process Improvement Plan · Hindsight, and propose process improvement plan    
total      

 

 


Third, performance analysis

 @ Xie aircraft 


Fourth, the design and implementation process

  After reading the title, the two of us are beginning to think through the design process of a class expression. The randomly generated number of operators and a class attribute, then the corresponding processing is performed by the calculation method of the class. But finally selected direct calculation formula generation process string. In this way it will be more direct and convenient process.

   Specific ideas and receiving -n -r parameters to control the number and range of expression values. Generating a calculation formula by string operators and operands randomly generated. In generating the calculation formula of random strings may be generated with the calculation formula of the bracket by a random number. Then the difficulty is how to calculate the item's randomly generated result value calculation formula.

   Because the expression operators is the priority number and type of random (1-3), the operator also has high and low points, and the calculation order to bring the brackets are not the same. This makes the result of the calculation becomes complex, which eventually found the problem through access to information by shunting-yard algorithm to solve. Scheduling algorithm may be a field used to convert an infix expression to postfix expression of classical algorithm can solve the expression problem results calculated by the algorithm.

   In this project, all operands are calculates an integer (the denominator is an integer) when calculated as a score. A class designed for this fraction ( Fraction ) to be processed, an integer part of the fraction, molecular moiety, the denominator portion. The benefit of this is the time to carry out operations in the two operands can be performed by fractional notation.

   Project structure shown below:

    

 

Function code structure:

   Fraction: fraction class, all the items in the calculation of the operand is converted into Fraction object calculated

   SymbolConstant: Constants class that defines constants used in the project

   CalculateUtil: computing tools, a method of encapsulating data tables required for calculation of formula A

   ExpressionUtil: arithmetic expression tools, a package method of generating the desired expression

   NumberUtil: Method operand operand generation tool, a package for

   OperatorUtil: encapsulating a method of generating the required operator

   PrintFileUtil: encapsulation method for generating answer verification title files and

   Main: main class, for receiving a particular function call parameters

 

 Call relationship flowchart:

 

 


V. Code Description

  Score category

public class Fraction {
    private int inter;//整数部分
    private int numerator;//分子
    private int denominator;//分母
        
  //省略构造方法 set、get方法  
}

 运算式生成的代码

 /**
     * 根据运算符数组和操作数数组生成运算式表达式
     * @param curOperators 运算符数组
     * @param curNumbers 操作数数组
     * @return 运算式字符串
     */
    private static String getExpressStr(Character[] curOperators, String[] curNumbers){  ---->运算符数组和操作数数组是随机生成的,由于生成的相关代码比较简单这里就不展示了
        //操作数的数量
        int number = curNumbers.length;
        //随机判断是否生成带括号的运算式
        int isAddBracket = (int)(Math.random()*10) % 2;  -----> 该运算式是否带括号也是通过随机数来判断的
        //随机生成器
        Random random = new Random();

        if(isAddBracket==1){//生成带括号的表答式
            //当标记为1时代表该操作数已经添加了左括号
            int[] lStamp = new int[number];         --------->这两个数组是用来标记当前操作数是否添加了左、右括号
            //当标记为1时代表该操作数已经添加了右括号    
            int[] rStamp = new int[number];
            //遍历操作数数组,随机添加括号
            for (int index=0;index<number-1;index++) {   -------------->遍历操作数来随机添加左括号,这里没有遍历到最后一个操作数是由于最后一个操作数不可能添加左括号
                int n = (int)(Math.random()*10) % 2;
                if(n == 0 && rStamp[index] != 1) {//判断当前操作数是否标记了左括号
                    lStamp[index] = 1;//标记左括号
                    curNumbers[index] = "(" + curNumbers[index];  //操作数之前加上左括号
                    int k = number - 1;
                    //生成右括号的位置
                    int rbracketIndex = random.nextInt(k)%(k-index) + (index+1);
                    //如果当前操作数有左括号,则重新生成右括号位置
                    while (lStamp[rbracketIndex] == 1){
                        rbracketIndex = random.nextInt(k)%(k-index) + (index+1);
                    }
            rStamp[rbracketIndex] = 1; curNumbers[rbracketIndex]
= curNumbers[rbracketIndex] +")"; } } } //将运算符数组和操作数数组拼成一个运算式字符串 StringBuilder str = new StringBuilder(curNumbers[0]); for (int k = 0; k < curOperators.length; k++) { str.append(curOperators[k]).append(curNumbers[k + 1]); } return str.toString(); }


   运算式结果计算相关代码

/**
 * @author liuyoubin
 * @date 2019/9/28 - 15:06
 * 运算工具类
 */
public class CalculateUtil {

     运算式的结果计算采用了调度场算法吗,该算法的思想是将我们常见的中缀表达式转成后缀表达式。算法如下:
  • 当还有记号可以读取时:
  • 读取一个记号。
  • 如果这个记号表示一个数字,那么将其添加到输出队列中。
  • 如果这个记号表示一个函数,那么将其压入栈当中。
  • 如果这个记号表示一个函数参数的分隔符(例如,一个半角逗号,):
  • 从栈当中不断地弹出操作符并且放入输出队列中去,直到栈顶部的元素为一个左括号为止。如果一直没有遇到左括号,那么要么是分隔符放错了位置,要么是括号不匹配。
  • 如果这个记号表示一个操作符,记做o1,那么:
  • 只要存在另一个记为o2的操作符位于栈的顶端,并且
  • 如果o1是左结合性的并且它的运算符优先级要小于或者等于o2的优先级,或者
  • 如果o1是右结合性的并且它的运算符优先级比o2的要低,那么
  • 将o2从栈的顶端弹出并且放入输出队列中(循环直至以上条件不满足为止);
    • 然后,将o1压入栈的顶端。
    • 如果这个记号是一个左括号,那么就将其压入栈当中。
    • 如果这个记号是一个右括号,那么:
    • 从栈当中不断地弹出操作符并且放入输出队列中,直到栈顶部的元素为左括号为止。
    • 将左括号从栈的顶端弹出,但并不放入输出队列中去。
    • 如果此时位于栈顶端的记号表示一个函数,那么将其弹出并放入输出队列中去。
    • 如果在找到一个左括号之前栈就已经弹出了所有元素,那么就表示在表达式中存在不匹配的括号。
    • 当再没有记号可以读取时:
    • 如果此时在栈当中还有操作符:
    • 如果此时位于栈顶端的操作符是一个括号,那么就表示在表达式中存在不匹配的括号。
    • 将操作符逐个弹出并放入输出队列中。
    • 退出算法
/**
     * 采用调度场算法,获取指定运算式的结果值
     *
     * @param express 运算式
     * @return
     */
    public static String getExpressValue(String express){
        //运算符栈,用于存放运算符包括 +、-、*、÷、(、)
        Stack<Character>  operators = new Stack<Character>();
        //操作数栈,用于存放操作数
        Stack<Fraction> fractions = new Stack<Fraction>();
        //将表达式字符串转成字符数组
        char[] chars = express.toCharArray();
        //遍历获取处理
        for (int i=0;i<chars.length;i++) {
            //获取当前的字符
            char c = chars[i];

            if(c=='('){//如果是左括号,入栈
                operators.push(c);
            }else if(c==')'){//当前字符为右括号
                //当运算符栈顶的元素不为‘(’,则继续
                while(operators.peek()!='('){
                    //拿取操作栈中的两个分数
                    Fraction fraction1 = fractions.pop();
                    Fraction  fraction2 = fractions.pop();
                    //获取计算后的值
                    Fraction result = calculate(operators.pop(), fraction1.getNumerator(), fraction1.getDenominator(),
                            fraction2.getNumerator(), fraction2.getDenominator());
                    //将结果压入栈中
                    fractions.push(result);
                }
                //将左括号出栈
                operators.pop();
            }else if(c=='+'||c=='-'||c=='*'||c=='÷'){//是运算符
                //当运算符栈不为空,且当前运算符优先级小于栈顶运算符优先级
                while(!operators.empty()&&!priority(c, operators.peek())){
                    //拿取操作栈中的两个分数
                    Fraction fraction1 = fractions.pop();
                    Fraction  fraction2 = fractions.pop();
                    //获取计算后的值
                    Fraction result = calculate(operators.pop(), fraction1.getNumerator(), fraction1.getDenominator(),
                            fraction2.getNumerator(), fraction2.getDenominator());
                    //将结果压入栈中
                    fractions.push(result);
                }
                //将运算符入栈
                operators.push(c);
            }else{//是操作数
                if(c>='0'&&c<='9'){
                    StringBuilder buf = new StringBuilder();
                    //这一步主要是取出一个完整的数值 比如 2/5、9、9/12
                    while(i<chars.length&&(chars[i]=='/'||((chars[i]>='0')&&chars[i]<='9'))){
                        buf.append(chars[i]);
                        i++;
                    }
                    i--;
                    //到此 buf里面是一个操作数
                    String val = buf.toString();
                    //标记‘/’的位置
                    int flag = val.length();
                    for(int k=0;k<val.length();k++){
                        if(val.charAt(k)=='/'){//当获取的数值存在/则标记/的位置,便于接下来划分分子和分母生成分数对象
                            flag = k;
                        }
                    }
                    //分子
                    StringBuilder numeratorBuf = new StringBuilder();
                    //分母
                    StringBuilder denominatorBuf = new StringBuilder();
                    for(int j=0;j<flag;j++){
                        numeratorBuf.append(val.charAt(j));
                    }
                    //判断是否为分数
                    if(flag!=val.length()){
                        for(int q=flag+1;q<val.length();q++){
                            denominatorBuf.append(val.charAt(q));
                        }
                    }else{//如果不是分数则分母计为1
                        denominatorBuf.append('1');
                    }
                    //入栈
                    fractions.push(new Fraction(Integer.parseInt(numeratorBuf.toString()), Integer.parseInt(denominatorBuf.toString())));
                }
            }
        }

        while(!operators.empty()){
            Fraction fraction1 = fractions.pop();
            Fraction  fraction2 = fractions.pop();

            //获取计算后的值
            Fraction result = calculate(operators.pop(), fraction1.getNumerator(), fraction1.getDenominator(),
                    fraction2.getNumerator(), fraction2.getDenominator());

            //将结果压入栈中
            fractions.push(result);
        }

        //计算结果
        Fraction result = fractions.pop();
        //获取最终的结果(将分数进行约分)
        return getFinalResult(result);

    }
---------------------------------------------------------------------------------------------------------------------------------------------
    private static String getFinalResult(Fraction result) {
        if(result.getDenominator()==0){
            return "0";
        }
        //获取最大公约数
        int gcd = gcd(result.getNumerator(),result.getDenominator());

        if(result.getDenominator()/gcd==1){//分母为1
            return String.valueOf(result.getNumerator()/gcd);
        }else{
            //如果分子大于分母则化成真分数的形式
            if(result.getNumerator()>result.getDenominator()){  
                result = getRealFraction(result);
                return result.getInter()+"'"+result.getNumerator()/gcd+"/"+result.getDenominator()/gcd;
            }else{
                return result.getNumerator()/gcd+"/"+result.getDenominator()/gcd;
            }
        }
    }
-----------------------------------------------------------------------------------------------------------------------------------------------
    /**
     * 化成真分数
     * @param result
     * @return
     */
    private static Fraction getRealFraction(Fraction result){
        int numerator = result.getNumerator();
        int denominator = result.getDenominator();
        //计算分子部分
        int newNumerator = numerator % denominator;
        //计算整数部分
        int inter = numerator/denominator;
        Fraction fraction = new Fraction(newNumerator, denominator);
        fraction.setInter(inter);                    ------------------------------->整数部分
        return fraction;
    }
-------------------------------------------------------------------------------------------------------------------------------------------------
    /**
     * 判断两个运算符的优先级
     * 当opt1的优先级大于opt2时返回true
     * 这是根据调度场算法给出的实现
     * @return
     */
    private static boolean priority(char opt1,char opt2){    --------------------------->只有当opt1的优先级小于或等于opt2的优先级时才放回true。这是根据调度场算法给出的实现
        if((opt1=='+'||opt1=='-')&&(opt2=='*'||opt2=='÷')){
            return false;
        }else if((opt1=='+'||opt1=='-')&&(opt2=='+'||opt2=='-')){
            return false;
        }else if((opt1=='*'||opt1=='÷')&&(opt2=='*'||opt2=='÷')){
            return false;
        }else{
            return true;
        }
    }
----------------------------------------------------------------------------------------------------------------------------------------------------
    /**
     * 对两个分数进行相应的运算,获取结果
     * @param opt 运算符
     * @param numerator1 分子1
     * @param denominator1 分母1
     * @param numerator2 分子2
     * @param denominator2 分母2
     * @return 结果
     */
    static Fraction calculate(Character opt,int numerator1,int denominator1,int numerator2,int denominator2){
        //结果数组,存放结果的分子与分母
        int[] result = new int[2];
        /**
         * 这里有一个陷阱,因为用于计算的两个数是通过栈来存储,所以取出计算结果的时候两个数的顺序会颠倒
         * 比如式子 1/2*9/12 我取出来的时候其实是 9/12 和 1/2 所以调用此函数的时候是calculate('*',9,12,1,2),所以下面的实现要注意不要踩坑
         */
        switch (opt){
            case '+':
                result[0] = numerator1*denominator2 + numerator2*denominator1; result[1]= denominator1*denominator2;
                return new Fraction(result[0],result[1]);
            case '-':
                result[0] = numerator2*denominator1 - numerator1*denominator2; result[1]= denominator1*denominator2;
                return new Fraction(result[0],result[1]);
            case '*':
                result[0] = numerator1*numerator2; result[1] = denominator1*denominator2;
                return new Fraction(result[0],result[1]);
            case '÷':
                result[0] = numerator2*denominator1; result[1] = numerator1*denominator2;
                return new Fraction(result[0],result[1]);
        }
        return new Fraction(result[0],result[1]);
    }
---------------------------------------------------------------------------------------------------------------------------------------------
    /**
     * 获取分子分母的最大公约数,辗转相除法
     * @param numerator 分子
     * @param denominator 分母
     * @return 最大公约数
     */
    private static int gcd(int numerator,int denominator){
        numerator = Math.abs(numerator);
        denominator = Math.abs(denominator);
        while (denominator != 0) {
            // 求余
            int remainder = numerator % denominator;
            // 交换数,等同递归调用
            numerator = denominator;
            denominator = remainder;
        }
        return numerator;
    }
}

 

 


 

六、测试运行 

 @谢飞机 

Guess you like

Origin www.cnblogs.com/liuyoubin/p/11605034.html