20180925-6 四则运算试题生成

作业要求参照[https://edu.cnblogs.com/campus/nenu/2018fall/homework/2148]

要求1 参考《构建之法》第4章两人合作,结对编程上述功能,要求每人发布随笔1篇 (代码是共同完成的,博客是分别完成的)。 (1) 给出每个功能的重点、难点、编程收获。(2)给出结对编程的体会,以及 (3) 至少5项在编码、争论、复审等活动中花费时间较长,给你较大收获的事件。 (10分)

功能一:重点是随机数和随机符号的实现。在进行此功能的实现时,遇到了两种错误的情况,一种是每次生成的四个随机数和三个操作符号都是一样的,比如“4+4+4+4=”这种,还有一种就是第一次运行生成的20个算式没问题,但是第二次运行会生成和第一次一模一样的算式。后来我们用以时间作为种子,完成随机数和操作符的实现,学会了如何正确产生随机数。

重要代码如下:

int num[8]={0};
char sign[4]={};
for(int i=0;i<4;i++)
{
    sign[i]=RandomSign();
}
for(int i=0;i<8;i++)
{
num[i]=rand()%9+1;
}

计算的时候我们通过生成的中缀表达式转换为后缀表达式,之后再利用后缀表达式来计算。下面给出关键代码:

void RPNotation(vector<char>&st,vector<char>ve)
{
    st.clear();
    stack<char>ssign;
    for(int i=0;i<ve.size()-1;i++)
    {
        if(ve[i]>='0'&&ve[i]<='9')
            st.push_back(ve[i]);
        else
        {
            if(ssign.empty()||ve[i]=='(')
                ssign.push(ve[i]);
            else
            {
                if(ve[i]==')')
                {
                    while (ssign.top()!='(')
                    {
                        st.push_back(ssign.top());
                        ssign.pop();
                    }
                    ssign.pop();
                }
                else
                {
                    if(ve[i]=='*'||ve[i]=='/')
                    {
                        while(!ssign.empty()&&(ssign.top()=='*'||ssign.top()=='/')&&ssign.top()!='(') {
                            st.push_back(ssign.top());
                            ssign.pop();
                        }
                        ssign.push(ve[i]);
                    }
                    else
                    {
                        while(!ssign.empty()&&ssign.top()!='(')
                        {
                            st.push_back(ssign.top());
                            ssign.pop();
                        }
                        ssign.push(ve[i]);
                    }
                }
            }
        }
    }
    while(!ssign.empty())
    {
        st.push_back(ssign.top());
        ssign.pop();
    }
}
View Code

 

功能二:重点在于如何生成括号,以及如何匹配左右括号。一开始我的想法是以一定概率产生左括号,同时用一个数记录左括号个数,当这个数不为零时再以一定概率产生右括号,等式结束时,检查并生成对应左括号数的右括号。但是实现起来就比较困难。然后李文涛查了资料说就四个操作数,能调整的优先级情况就几种,直接用二元数组就行。后来采用的就是这个办法,节省了不少时间。

重要代码如下:

int bracket[9][8]=
{
    { 0, 0, 0, 0, 0, 0, 0, 0 },
    { -1, 0, 0, 1, 0, 0, 0, 0 },
    { 0, 0, -1, 0, 0, 1, 0, 0 },
    { 0, 0, 0, 0, -1, 0, 0, 1 },
    { -1, 0, -1, 0, 0, 2, 0, 0 },
    { -2, 0, 0, 1, 0, 1, 0, 0 },
    { -1, 0, 0, 1, -1, 0, 0, 1 },
    { 0, 0, -2, 0, 0, 1, 0, 1 },
    { 0, 0, -1, 0, -1, 0, 0, 2 }
};

功能1,功能2实现的截图。

功能三:重点是实现将生成的算式输出到文本中去同时避免重复。这儿使用FILE指针建立打开文本实现输出的。每次生成一个算式就放入map中去,后续生成算式先去map中查询是否出现过,如果出现过,就继续生成,直到没有出现过。

重要代码如下:

FILE *fp=fopen("题目.txt","w");
for(int i=0;i<totalnum;i++)
{
    CreateEquation(vec,vec2,correctAns);
    int j;
    for(j=0;j<vec2.size();j++)
    {
        printf("%c",vec2[j]);
        fprintf(fp,"%c",vec2[j]);
    }
    for(;j<50;j++)
    {
        printf(" ");
        fprintf(fp,"%c",32);
    }
    Fraction f1=(correctAns);
    f1.Print();
}    

功能3实现的截图

功能四:重点是要计算分数,结果约分化简。这里我们实在想不出,用后缀表达式不好做,就去网上查了不少资料,最后发现一种通过连分数的方法将小数转化为分数。

同时我们求出最大公约数,来对算式进行化简,而结果能分子分母能整除的直接输出相除之后的结果,不能整除的能化为带分数的化为带分数,不能的直接输出约分后的结果。

而分数我们在生成时也添加了一对括号,这样方便浏览,因为,如果出现1/3+1/2/3/4/5/6这样的不如(1/3)+(1/2)/(3/4)/(5/6)这样看的直观。

下面给出部分重要代码:

int gcd(int m, int n) {
    int a=abs(m);
    int b=abs(n);
    return (a % b == 0) ? b : gcd(b, a % b);
}
void Fraction::Print()
{
    //printf("%d,%d    ",int(a),int(b));
    if(int(a)%int(b)==0)
    {
        std::cout << (sign ? "-" : "") << int(a)/int(b) << std::endl;
    }
    else if(int(a)>int(b))
    {
        int fz = int(a);
        int fm = int(b);
        int zh = fz / fm ;
        int sh = fz - zh * fm ;
        std::cout << (sign ? "-" : "") << zh <<" "<<sh<< "/" << fm  << std::endl;
    }
    else
    {
        std::cout << (sign ? "-" : "") << a << "/" << b << std::endl;
    }
}
void Fraction::Print()
{
    //printf("%d,%d    ",int(a),int(b));
    if(int(a)%int(b)==0)
    {
        std::cout << (sign ? "-" : "") << int(a)/int(b) << std::endl;
    }
    else if(int(a)>int(b))
    {
        int fz = int(a);
        int fm = int(b);
        int zh = fz / fm ;
        int sh = fz - zh * fm ;
        std::cout << (sign ? "-" : "") << zh <<" "<<sh<< "/" << fm  << std::endl;
    }
    else
    {
        std::cout << (sign ? "-" : "") << a << "/" << b << std::endl;
    }
}

功能4实现的截图

体会收获:通过这次编程,我学习到了结对编程的重要性,以及沟通的重要性。一开始我提议使用C++,李文涛提议使用Python,产生了分歧,后来经过讨论决定使用C++。缩进方面李文涛主张Tab,我觉得空4个空格比较稳妥,后来经过试验发现Tab确实有时候不稳定,空的长短不一致,就决定按我说的来。除号方面的话,李文涛想通过rand()%9+1来避免0,但是我发现这样可能导致所有数取不到0,而且还是后有除0情况发生,比如1/(4-4),不能完全避免除0,所以我们想了一个判断函数,在计算正确答案的过程中,遇到“/”号就判断除数是否为0,如果是就继续调用生成函数,直到没有除0情况发生。单元测试这些东西也是很新奇的东西,一开始我们没有头绪,摸索花了很久时间,后来逐渐掌握了技巧,就能越来越快的进行测试、修改和再测试。

要求2 给出照片1张,包括结对的2位同学、工作地点、计算机,可选项包括其他能表达结对编程工作经历的物品或场景。 (5分)

要求3 使用coding.net做版本控制。checkin 前要求清理 临时文件、可执行程序,通常执行 build-clean可以达到效果。(25分)

git代码地址[https://git.coding.net/shishishaonian/four_arithmetic_operation.githttps://git.coding.net/shishishaonian/four_arithmetic_operation.git]

posted @ 2018-10-08 16:42  孙赛佳  阅读(153)  评论(0编辑  收藏  举报