zoukankan      html  css  js  c++  java
  • #2018BIT软件工程基础#结对项目:四则运算题目生成

    小队成员:

    1120161945 雷云霖

    1120161949 刘镓煜


    一、开发时间

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


    二、思路分析与设计编码

      重点在于两个方面,第一是生成不重复的随机题目,第二是如何计算这个题目。

      对于题目生成的不重复性,有了个人项目数独的实战经验,便不是什么大的问题。当然在这个方面小组的成员还是有一定分歧的,小雷同学决定采用重复概率为百万分之一的随机数方法,小刘同学决定采用排列组合排出必然不相同的一千种。最后取了一个二者折中的方法,如果真发生了重复的情况,一来是对题目的复习,二来也可以告知用户他今日运气特别的好中了亿万分之一的概率,充分体现了我们软件的人性化设计——不仅仅限于做题。关键代码展示如下(C++):

    string takeTest()
    {
        srand((unsigned)time(NULL));
        int num1, num2;
        string signal;
        string ans = "";
    
        signal = getSignal(6);
        num1 = random(0, 100);
        num2 = random(1, 100);
    
        if (signal == "'")
        {
            if (num1<num2)
            {
                swap(num1, num2);
            }
        }
    
        if (signal == "**"||signal=="^")
        {
            num2 /= 35;
        }
    
        if (signal == "/")
        {
            if (num2 == 0)
            {
                swap(num1, num2);
            }
        }
    
        ans = ans + to_string(num1) + signal + to_string(num2);
        signal = getSignal(4);
        ans = ans + signal;
    
        signal = getSignal(6);
        num1 = random(0, 100);
        num2 = random(1, 100);
    
        if (signal == "'")
        {
            if (num1<num2)
            {
                swap(num1, num2);
            }
        }
    
        if (signal == "**" || signal == "^")
        {
            num2 /= 35;
        }
    
        if (signal == "/")
        {
            if (num2 == 0)
            {
                swap(num1, num2);
            }
        }
    
        ans = ans + to_string(num1) + signal + to_string(num2);
    
        return ans;
    }

       如何计算题目,对于我们来说也不是难事,用堆栈的方法实现中缀表达式转后缀表达式即可。能力不强的小刘同学此时发问了:那**这表示乘方的符号有两个字符串怎么办?小雷同学回答他:对算式做预处理,把**改为^。

       对于分数的计算,则采取把所有操作数先通分,在分子上进行四则运算,最后约分化简的的操作。

       关键代码展示如下(C++):

    string MidToBack(string s)       //转后缀表达式,利用栈
    {
        string ret;
        stack<int> stk;
        int pos; 
        while ((pos = s.find("**"))!=s.npos)  //将**转换成^
        {
            s.replace(pos, 2, "^");
        }
        for (string::size_type i = 0; i< s.size(); i++)
        {
            if (isdigit(s[i]))
            {
                ret += s[i];
                if ((i < s.size() - 1 && !isdigit(s[i + 1])) || i == (s.size() - 1))
                    ret += ' ';//这里是用空格将数跟符号隔开。目的是在后缀计算中提取数的时候,有些数可能不止一位,如果没有空格符,一个多位的数会被误认为是多个个位数。
            }
            else
            {
                switch (s[i])
                {
                case '(':
                    stk.push(s[i]);
                    break;
                case ')':
                    while (!stk.empty() && stk.top() != '(')
                    {
                        ret += stk.top();
                        stk.pop();
                    }
                    stk.pop();
                    break;
                case '+':
                case '-':
                    while (!stk.empty() && stk.top() != '(')
                    {
                        ret += stk.top();
                        stk.pop();
                    }
                    stk.push(s[i]);
                    break;
                case '*':
                case '/':
                    while (!stk.empty() && (stk.top() == '*' || stk.top() == '/' || stk.top() == '^'))
                    {
                        ret += stk.top();
                        stk.pop();
                    }
                    stk.push(s[i]);
                    break;
                case '^': stk.push(s[i]); break;
                }
    
            }
        }
        while (!stk.empty())
        {
            ret += stk.top();
            stk.pop();
        }
        return ret;
    }

    三、测试与优化

        测试共花费2小时时间。

        测试用例的设计与运行结果

        1)参数的测试用例

         I. -a abcd 不合法格式的参数,成功输出了错误信息

         II. -a 0或-a -1 越界参数,成功输出了错误信息

         III. -c 100 命令参数错误,成功输出了错误信息

         2)性能优化的测试用例

         -a 1000

         题目生成的速度十分迅速,性能分析图不像个人项目一样波涛汹涌,反而平静如水,其中耗时最大的便是等待用户输入答案的程序段,可优化的空间不大。

        

        

        经过2个人2小时的排查改进,还是发现了问题,采用随机数时产生了分母为0的错误情况,于是加了一个分母不为0的限制,解决了问题。


    四、扩展需求

        能力有限的小刘只能照着小雷同学的C++模板,尝试了用从未学过的JAVA语言重新编写四则运算程序。正好软件工程基础结课后开设了JAVA课程,也算对新课的一次提前了解吧。经过自学和课程的学习,小刘同学不但完成了用JAVA重新编写程序的目标,还自己尝试用JAVA基本实现了做GUI图形界面的需求。

        实现算法同C++实现。

        估计完成所需时间:24小时(平摊到3个星期之中),实际完成时间:24+小时

        程序思路图如下:

       

       效果如下:

       


    五、心得体会

        两个人做,并不比一个人做轻松啊!由于同组的小雷同学的编程水平远远超出我,基本上是他的大腿扛着这个小组在前进,但我也不能光抱着大腿一点活也不干啊。他编程强就由他编,我一方面加紧了学习,在技术上不至于太落后于他,另一方面我也发挥了在评审方面的作用,找到了小雷同学漏掉的需求和程序中一些致命BUG,让我很有成就感。这样的合作,才让我感受到了团队合作的快乐啊!当然伴随着JAVA的开课,有了小雷同学的C++模板参考,我还有什么理由不自己动手呢?于是自己也开始用起了苦功夫自学相关知识去完成JAVA版本,尽管做的十分辛苦,但想想小雷同学只用几个小时就写完了C++版本,自己多花些时间,也是应该的。结果还不错,不但完成了,还尝试着加了个比较简陋的界面。

       我想团队合作的主要目的不是在于说你把程序做得多漂亮,而主要在于两个人之间的磨合。在这次的合作中,小雷发挥了技术优势,我发挥了评审优势,相互补齐了短板,实现了1+1>=2的效果,我觉得这是一个十分激励人心的事情,为我以后的团队合作积累了信心,起码在二人合作中我找到了自己的位置,在团队合作中我相信我也能找到自己的位置。当然,技术上的进步也是一个巨大的收获,毕竟结对伙伴在技术上有优势,就给了我一个向他看齐的动力去学习,老抱大腿多没意思,自己总得有个发光发热的一技之长嘛!

  • 相关阅读:
    zookeeper和Eureka对CAP理论的支持
    redis缓存穿透、缓存击穿、缓存雪崩
    CAP原则和BASE理论
    bin log、redo log、undo log和MVVC
    在ROS中使用Python3
    Windows下使用AutoSSH,并作为服务自启动(不用安装Cygwin)
    回收站的正确使用方法
    Windows下好用到必须开机自启的小工具
    PreferenceScreen监听子项的刷新
    安卓普通类通过classloader访问资源文件
  • 原文地址:https://www.cnblogs.com/bitljy/p/9112604.html
Copyright © 2011-2022 走看看