结对开发-四则运算3
日期&&任务 |
听课 | 编写程序 | 阅读相关书籍 | 网上查找资料 | 日总计 |
周一 | 100 | 10 | 20 | 130 | |
周二 | 120 | 30 | 150 | ||
周三 | 30 | 10 | 10 | 50 | |
周四 | 100 | 20 | 120 | ||
周五 | 120 | 30 | 150 | ||
周六 | 45 | 100 | 10 | 155 | |
周日 | 90 | 20 | 110 | ||
周总计 | 200 | 335 | 210 | 120 |
865 |
时间记录日志
3/16
日期 | 开始时间 | 结束时间 | 中断时间 | 净时间 | 活动 | 备注 |
3/14 | 14:00 | 15:50 | 10 | 100 | 听课 | 软件工程上课 |
21:00 | 21: 10 | 10 | 阅读书籍 | 《构建之法》 | ||
22:10 | 22: 30 | 20 | 网上查找资料 | |||
3/15 | 21:00 | 21:30 | 20 | 120 | 编写程序 | 结对开发-四则运算3 |
22: 15 | 22: 25 | 30 | 网上查找资料 | |||
3/16 | 19: 24 | 20: 00 | 6 | 30 | 编写程序 | 结对开发-四则运算3 |
22:00 | 22: 20 | 10 | 阅读书籍 | 《构建之法》 | ||
22:40 | 22: 50 | 10 | 查找资料 | |||
3/17 | 14:00 | 15: 50 | 100 | 上课 | 软件工程上课 | |
18:00 | 18: 20 | 20 | 编写程序 | 结对开发-四则运算3 | ||
3/18 | 14: 00 | 16: 20 | 20 | 120 | 编写程序 | 结对开发-四则运算3 |
11:23 | 12: 00 | 7 | 30 | 网上查找资料 | ||
3/19 | 7: 00 | 9: 30 | 50 | 100 | 阅读书籍 | 阅读《构建之法》 |
10: 00 | 11: 00 | 15 | 45 | 编写程序 | 结对开发-四则运算3 | |
9: 45 | 9: 55 | 10 | 网上查找资料 | |||
3/20 | 9:00 | 10: 53 | 23 | 90 | 阅读书籍 | 阅读《构建之法》 |
14:50 | 15: 10 | 20 | 网上查找资料 |
缺陷记录日志
日期 | 编号 | 类型 | 引入阶段 | 排除阶段 | 修复时间 | 备注 |
3/18 | 1 | 20 | 编码 | 编译 | 5 | 文件关闭出现错误 |
3/18 | 2 | 20 | 编码 | 编译 | 1 | 变量未初始化 |
3/18 | 3 | 20 | 编码 | 编译 | 3 | 定义了相同名字的变量 |
一、题目及题目要求
编程随机生成四则运算,算数包括整数和真分数
1.题目避免重复
2.可定制(数量/打印方式)
3.可以控制下列参数:
是否有乘除法;
是否有括号(最多可以支持十个数参与运算);
数值范围;
加减有无负数;
除法有无余数。
4.输入结果并判断正确
二、设计思路
在上次程序的基础上进行修改,
1.题目避免重复:为避免随机数每次相同用了srand函数。
2.可定制出题数量:通过键盘输入数字,在for循环中控制循环次数。
3. 是否有乘除法:把算符分为两大类,利用case语句选择前四种加和减,后四种乘除。
4. 数值范围:在产生随机数时通过输入控制rand函数的参数,从而使运算数不超过此范围。
5. 加减有无负号:结果无负数,判断两操作数,第一操作数若比第二操作数小,则第二个减第一个。
6.除法有无余数:除法无余数,判断第一操作数模第二操作数的结果,若为0即可输出,否则再循环一次。
7.是否有括号:先随机生成一个数字,代表着生成表达式中操作数的个数。再循环生成一个数字,将其输出,然后等概率生成‘+’‘-’‘*’‘/’中的一个跟在该数字后面,输出。以一定概率生成左括号,若生成了左括号则输出,并进行计数标志当前共有多少个未完成匹配的左括号。若当前有未完成匹配的左括号,则在生成一个数字后,生成一个操作符前,以一定的概率生成右括号。在生成完毕后,生成最后一个数并将为匹配的左括号予以匹配。把产生的式子存入文件。
8.输入结果并判断正确:(1)对于两个运算数的式子,把结果存入数组,调用函数与数组中的值比较。
(2)对于多个运算数的式子,从文件中读取内容,利用中缀表达式转换为后缀表达式,然后就是后缀表达式的计算,最后与用户输入结果对比。
三、代码
#include<iostream> #include<ctime> #include<stack> #include<fstream> #define length 10000//存放答案数组长度 using namespace std; typedef long long ll; ofstream fout("equation.txt"); char Op[] = {'+', '-', '*', '/'}; int rights;//对题数目 int wrong;//错题数目 struct num{ ll numerator, denominator; num(){numerator = 0; denominator = 1;} num(int n) {numerator = n; denominator = 1;} num(int n,int d) {numerator = n; denominator = d;} void operator = (num x) { numerator = x.numerator; denominator = x.denominator; } }; #define maxl 1005 char nifix[maxl], post[maxl]; char ans[maxl]; int cnt_right, cnt_wrong; bool error; num res, rst; //****分数类***// class fraction { private: int above; //分子 int below; //分母 void reduction(); //约分 fraction makeCommond(fraction); //通分 public: fraction() { //构造函数 } fraction add(fraction); //两分数相加 fraction sub(fraction); //两分数相减 fraction mul(fraction); //两分数相乘 fraction div(fraction); //两分数相除 int display(int,int); //显示分数 void setvalue(int ,int); //存储分数 }; //***********分数的约分*********// void fraction::reduction() { int i,comdiv,small,max; if(above<below) { small=above; max=below; } else { small=below; max=above; } for(i=small;i>1;i--) { if((small%i==0 )&(max%i==0) ) break; } comdiv=i; //最大公约数 if(i!=0) { above/=i; below/=i; } } //*************分数的通分*************// fraction fraction::makeCommond(fraction frac) { int b1=below,b2=frac.below, m,s; if(b1>b2) { m=b1%b2; s=b2; } else { m=b2%b1; s=b1; } while(m>0) { int res=s%m; s=m,m=res; } int small=(b1*b2)/s; above=above*(small/below); frac.above=frac.above*(small/frac.below); below=small; frac.below=small; return frac; } //***************分数的相加*************// fraction fraction::add(fraction fr) { fraction myFraction; myFraction.above=above*fr.below+fr.above*below; myFraction.below=below*fr.below; myFraction.reduction(); return myFraction; } //*********************分数的相减***************// fraction fraction::sub(fraction fr) { fraction myFraction; myFraction.above=above*fr.below-fr.above*below; myFraction.below=below*fr.below; myFraction.reduction(); return myFraction; } //*******************分数的相乘****************// fraction fraction::mul(fraction fr) { fraction myFraction; myFraction.above=above*fr.above; myFraction.below=below*fr.below; myFraction.reduction(); return myFraction; } //******************分数的相除***********// fraction fraction::div(fraction fr) { fraction myFraction; myFraction.above=above*fr.below; myFraction.below=below*fr.above; myFraction.reduction(); return myFraction; } //*********************分数答案的输入判断*************// int fraction::display(int a,int b) { if((a==above)&&(b==below)) { cout<<"正确"<<endl; rights=rights+1; } else { cout<<"错误"<<endl; wrong=wrong+1; } return rights,wrong; } //*******************分数的赋值****************// void fraction::setvalue(int sj1,int sj3) { above=sj1; below=sj3; } //*************无分数,无余数答案判断****************// int answer(int a[],int i) { int ans; cout<<"请输入答案:"<<endl; cin>>ans; if(ans==a[i]) { cout<<"正确"<<endl; rights=rights+1; } else { cout<<"错误"<<endl; wrong=wrong+1; } return rights,wrong; } //*************无分数,有余数答案判断****************// int answer_1(int a[],int i,int b[]) { int ans,yushu; cout<<"请输入商:"<<endl; cin>>ans; cout<<"输入余数"<<endl; cin>>yushu; if((ans==a[i])&&(yushu=b[i])) { cout<<"正确"<<endl; rights=rights+1; } else { cout<<"错误"<<endl; wrong=wrong+1; } return rights,wrong; } //*************产生带括号式子****************// void create(int maxn) { if(!fout) //如果打开失败,outfile返回值 { cerr << "open error!" << endl; exit(1); } //首先随机生成算式中操作数的个数,其数量必须大于1 int lengt;//式子长度 do{ lengt = rand()%8; }while(lengt < 2); bool div = false; //用来防止出现除0错误 int brack_cnt = 0; //记录未匹配的左括号个数 ll num, op; for (int i = 1; i < lengt; i++) //循环生成算式 { if (div) //若此时需要生成的数字前的负号是'/',则需要特判此次生成的数字不能为0 { div = false; do{ num = rand()%maxn; }while(num == 0); cout<< num; fout<< num; } else { num= rand()%maxn; fout <<num; cout<<num; }//否则直接生成数字输出 int tmpcnt = brack_cnt; for (int j = 0; j < tmpcnt; j++) //若当前有未匹配的左括号,则对每一个未匹配的左括号,都有一定概率生成相应右括号。 { if ((rand()%5) > 2) //生成右括号概率为0.6 { brack_cnt--; fout << ")"; cout<<")"; } } op = rand()%4; //生成运算符 fout << Op[op]; cout << Op[op]; if (op == 3) //若生成了除号,则需要置相应标志位 div = true; if (!(rand()%3)) //以一定概率生成左括号,概率为1/3 { fout << "("; cout<<"("; brack_cnt++; num= rand()%maxn;//生成左括号后必须生成一个数字和运算符,不然可能出现(15)这样的错误 fout <<num; cout<<num; op = rand()%4; fout << Op[op]; cout<<Op[op]; if (op == 3) div = true; } } if (div) //生成最后一个数字,该数字后不需要跟运算符 { div = false; do{ num = rand()%maxn; }while(num == 0); fout << num; cout<< num; } else { num=rand()%maxn; fout << num; cout<<num; } while(brack_cnt--) //补全右括号 { fout << ")"; cout << ")"; } cout<<"="; fout<< endl; cout<<endl; } bool isNum(char x) //判断是否是数字 { return (x >= '0' && x <= '9'); } bool isOp(char x) //判断是否是操作符 { return (x == '+' || x == '-' || x == '*' || x == '/' || x == '(' || x == ')'); } int priority(char x) //返回一个操作符的优先级 { if (x == '-' || x == '+') return 1; else if (x == '*' || x == '/') return 2; else if (x == '(') return 0; else return -1; } bool nifix_to_post() { memset(post, 0, sizeof(post)); stack<char> s; //操作符栈,用来压操作符 /* ************************************************************************************************ # 由于操作数是多位的,所以我们逐位做一个累加暂存的工作,当遇到操作符时,代表此操作符前的数暂存完毕, # 需要将其入栈,但也不是所有操作符前都有操作数,如'*('等,所以我们需要一个标志位来表示某个操作符前 # 是否进行过暂存操作数的处理。 *///************************************************************************************************** bool havenum = false; int tmp = 0, pos = 0; //tmp为暂存多位数的变量,pos为后缀数组存储位置 for (int i = 0; nifix[i] != '\0'; i++) //循环遍历中缀数组 { if (isNum(nifix[i])) //若当前字符是数字,则进行暂存操作。并标识已有操作数 { havenum = true; tmp = tmp*10 + (nifix[i]-'0'); } else if (isOp(nifix[i])) //若当前字符是操作符则进行如下操作 { //中缀表达式合法性判断,判断是否有连续两个字符相连,若有则代表出错 if (isOp(nifix[i-1]) && nifix[i-1] != ')' && nifix[i] != '(') return true; //如果操作符前有操作数,则将得到的操作数输出至后缀表达式 if (havenum) { havenum = false; post[pos++] = tmp + '0'; tmp = 0; } //如果操作符为右括号,则按规则进行出栈如后缀表达式等操作 if (nifix[i] == ')') { if (s.empty()) //中缀表达式合法性判断,判断括号匹配 return true; while(s.top() != '(') { post[pos++] = s.top(); s.pop(); if (s.empty()) //中缀表达式合法性判断,判断括号匹配 return true; } s.pop(); } else if (nifix[i] == '(') //如果是左括号则直接入栈 s.push(nifix[i]); else //如果是+-*/则按规则操作 { while (!s.empty() && priority(nifix[i]) <= priority(s.top())) { post[pos++] = s.top(); s.pop(); } s.push(nifix[i]); } } else //中缀表达式合法性判断,判断是否含非法符号 return true; } //若有操作数,则将其输出至后缀表达式 if (havenum) { havenum = false; post[pos++] = tmp + '0'; tmp = 0; } //将栈中操作符一次输出到后缀表达式中 while(!s.empty()) { if (s.top() == '(') //中缀表达式合法性判断,判断括号匹配 return true; post[pos++] = s.top(); s.pop(); } return false; } ll gcd(ll m, ll n) { ll tmp; tmp = m % n; while(tmp) { m = n; n = tmp; tmp = m % n; } return n; } bool cal_result() { stack<num> s; //用来存放操作数的栈 for (int i = 0; i < (strlen(post)); i++) //循环遍历后缀表达式 { if (!isOp(post[i])) //如果是数字则直接入栈 { num tmp(post[i]-'0', 1); s.push(tmp); } else { //取出两个操作数,如果操作数数量不够,则出错,返回 if (s.empty()) return true; num b = s.top(); s.pop(); if (s.empty()) return true; num a = s.top(); s.pop(); num c; if (post[i] == '+') //操作符是'+',进行上述规则运算 { c.numerator = a.numerator * b.denominator + b.numerator * a.denominator; c.denominator = a.denominator * b.denominator; } else if (post[i] == '-') //操作符是'-',进行上述规则运算 { c.numerator = a.numerator * b.denominator - b.numerator * a.denominator; c.denominator = a.denominator * b.denominator; } else if (post[i] == '*') //操作符是'*',进行上述规则运算 { c.numerator = a.numerator * b.numerator; c.denominator = a.denominator * b.denominator; } else if (post[i] == '/') //操作符是'/',进行上述规则运算 { if (b.numerator == 0) //如果除的是0,则出现除法错误,返回 return true; c.numerator = a.numerator * b.denominator; c.denominator = a.denominator * b.numerator; } else //其他情况则出错 return true; if (c.numerator != 0) //若结果不为0,则对分子分母约分 { ll div = gcd(c.denominator, c.numerator); c.denominator /= div; c.numerator /= div; } s.push(c); //将约分后结果入栈 } } if (s.size() > 1) //如果所有操作符都进行相应操作数后栈内还有多个元素,则说明出错,返回 return true; res = s.top(); //保存结果 s.pop(); if (res.denominator < 0) //化简结果,将分母的负号移到分子上 { res.numerator = -res.numerator; res.denominator = -res.denominator; } return false; } bool trans_ans() { int i = 0; ll tmp = 0; //两个标志位分别标志用户输入的分子分母是否为负数 bool num_flag = false, deno_flag = false; //先判断分子是否为负 if (ans[i] == '-') { num_flag = true; i++; } //接收第一个数字 while(isNum(ans[i])) { tmp = tmp * 10 + (ans[i] - '0'); i++; } //若第一个数为负数,则将转化过来的整数取负 if (num_flag) tmp = -tmp; //保存分子 rst.numerator = tmp; //分母赋初值为1 rst.denominator = 1; tmp = 0; //判断是否是分数 if (ans[i] == '/') { //判断分母是否为负数 if (ans[++i] == '-') { deno_flag = true; i++; } //接收第二个数 while(isNum(ans[i])) { tmp = tmp * 10 + (ans[i] - '0'); i++; } //若第二个数为负数,则将转化过来的整数取负 if (deno_flag) tmp = -tmp; //保存分母 rst.denominator = tmp; } //若分母为0,则用户输入的结果是非法的 if (rst.denominator == 0) return true; //若此时没有遍历所有的字符,则说明输入是非法的 if (i != strlen(ans)) return true; //化简分母的负号,将其移至分子 if (rst.denominator < 0) { rst.numerator = -rst.numerator; rst.denominator = -rst.denominator; } //若用户输入的分子分母都是负数,则用户输入不符合规范,我们取分母为0(因为计算结果不可能出现分母为0的状况)标志这种情况的发生 if (num_flag && deno_flag) rst.denominator = 0; return false; } int main() { fraction frac,frac2; int sj1,sj2,sf=1,sj3,sj4,r,j=1;//定义随机数及算符 int above,below; int Num;//题的数量 int numchose;//运算数选择 char sfchose;//算符选择 char yschose;//余数选择 char jf;//减法结果选择 int qznum;//取值范围 int ans_2[length];//存放正确的答案 int ans_4[length]={0};//存放正确的余数 srand((unsigned)time(NULL)); //srand函数是以现在系统时间作为时间种子产生随机数 cout<<"*****欢迎使用自动出题系统*****"<<endl; cout<<"请对以下内容进行初始化设置:"<<endl; cout<<"请选择运算数(1.二个运算数 2.多个运算数)"<<endl; cin>>numchose; if(numchose==1) { cout<<"请输入打印题数:"<<endl; cin>>Num; cout<<"请选择是否有乘除法?(y/n)"<<endl; cin>>sfchose; cout<<"请输入一个值确定算式中数值取值范围:"<<endl; cin>>qznum; cout<<"减法结果中出现负数吗?(y/n)"; cin>>jf; if(sfchose=='y') { cout<<"请选择除法有无余数?(y/n)"<<endl; cin>>yschose; } cout<<"********************************"<<endl; for(int i=0;i<Num;i++) { sj1=rand()%qznum; sj2=rand()%qznum; sj3=rand()%qznum; sj4=rand()%qznum; if(sfchose=='n')//无乘除法 { sf=rand()%4; } if(sfchose=='y')//有乘除法 { sf=rand()%8; } switch(sf) { case 0: cout<<sj1<<"+"<<sj2<<"= "<<endl; ans_2[i]=sj1+sj2; answer(ans_2,i); break; case 1: if(jf=='n')//无负数 { if(sj1<sj2) { cout<<sj2<<"-"<<sj1<<"= "<<endl; ans_2[i]=sj2-sj1; answer(ans_2,i); } else { cout<<sj1<<"-"<<sj2<<"= "<<endl; ans_2[i]=sj1-sj2; answer(ans_2,i); } } else { cout<<sj1<<"-"<<sj2<<"= "<<endl; ans_2[i]=sj1-sj2; answer(ans_2,i); } break; case 2: if(sj1>sj3) { r=sj1; sj1=sj3; sj3=r; } if(sj2>sj4) { r=sj2; sj2=sj4; sj4=r; } cout<<"("<<sj1<<"/"<<sj3<<")"<<"+"<<"("<<sj2<<"/"<<sj4<<")= "<<endl; frac.setvalue(sj1,sj3); frac2.setvalue(sj2,sj4); cout<<"输入分子和分母(用空格隔开,结果化到最简)"; cin>>above>>below; frac.add(frac2).display(above,below); break; case 3: if(sj1>sj3) { r=sj1; sj1=sj3; sj3=r; } if(sj2>sj4) { r=sj2; sj2=sj4; sj4=r; } if(jf=='n')//减法结果无负数 { if((sj1/sj3)<(sj2/sj4)) { cout<<"("<<sj2<<"/"<<sj4<<")"<<"-"<<"("<<sj1<<"/"<<sj3<<")= "<<endl; frac.setvalue(sj2,sj4); frac2.setvalue(sj1,sj3); cout<<"输入分子和分母(用空格隔开,结果化到最简)"; cin>>above>>below; frac.sub(frac2).display(above,below); } else { cout<<"("<<sj1<<"/"<<sj3<<")"<<"-"<<"("<<sj2<<"/"<<sj4<<")= "<<endl; frac.setvalue(sj1,sj3); frac2.setvalue(sj2,sj4); cout<<"输入分子和分母(用空格隔开,结果化到最简)"; cin>>above>>below; frac.sub(frac2).display(above,below); } } else { cout<<"("<<sj1<<"/"<<sj3<<")"<<"-"<<"("<<sj2<<"/"<<sj4<<")= "<<endl; frac.setvalue(sj1,sj3); frac2.setvalue(sj2,sj4); cout<<"输入分子和分母(用空格隔开,结果化到最简)"; cin>>above>>below; frac.sub(frac2).display(above,below); } break; case 4: cout<<sj1<<"*"<<sj2<<"="<<endl; ans_2[i]=sj1*sj2; answer(ans_2,i); break; case 5: if(sj2==0)//分母为零则不计入总数 { i=i-1; } else if(yschose=='n')//除法没有余数 { if(sj1%sj2==0) { cout<<sj1<<"/"<<sj2<<"="<<endl; ans_2[i]=sj1/sj2; answer(ans_2,i); } else { i=i-1; } } else if(yschose=='y')//除法有余数 { if(sj1%sj2!=0) { cout<<sj1<<"/"<<sj2<<"="<<endl; ans_2[i]=sj1/sj2; ans_4[i]=sj1-sj2*ans_2[i]; answer_1(ans_2,i,ans_4); } else { i=i-1; } } break; case 6: if(sj1>sj3) { r=sj1; sj1=sj3; sj3=r; } if(sj2>sj4) { r=sj2; sj2=sj4; sj4=r; } cout<<"("<<sj1<<"/"<<sj3<<")"<<"*"<<"("<<sj2<<"/"<<sj4<<")= "<<endl; frac.setvalue(sj1,sj3); frac2.setvalue(sj2,sj4); cout<<"输入分子和分母(用空格隔开,结果化到最简)"; cin>>above>>below; frac.mul(frac2).display(above,below); break; case 7: if(sj1>sj3) { r=sj1; sj1=sj3; sj3=r; } if(sj2>sj4) { r=sj2; sj2=sj4; sj4=r; } cout<<"("<<sj1<<"/"<<sj3<<")"<<"/"<<"("<<sj2<<"/"<<sj4<<")= "<<endl; frac.setvalue(sj1,sj3); frac2.setvalue(sj2,sj4); cout<<"输入分子和分母(用空格隔开,结果化到最简)"; cin>>above>>below; frac.div(frac2).display(above,below); break; default: break; } } cout<<"共做对 "<<rights<<" 道题"<<endl; cout<<"共做错 "<<wrong<<" 道题"<<endl; } else { cout<<"请输入出题数目:"; cin >>Num ; cout<<"请输入一个值确定算式中数值取值范围:"<<endl; cin>>qznum; cout<<"******************************************"<<endl; while(Num--) { create(qznum); } //计数器请0 cnt_right = cnt_wrong = 0; ifstream infile; infile.open("equation.txt", ios::in); if(infile.is_open() == false) { cerr << "open error!" << endl; exit(1); } while( infile >> nifix) { error = nifix_to_post(); //中缀转后缀,并接受错误信息 if (error) //若过程出错,则输出错误信息 { cout << "读取式子出错!" << endl; return 0; } //test_post(); error = cal_result(); //后缀表达式计算,并接受错误信息 if (error) //若过程出错,则输出错误信息 { cout << "计算中出错..." << endl; return 0; } cout << "请输入答案: "; cin >> ans; //接受用户答案 error = trans_ans(); //答案翻译, 并接受错误信息 if (error) //若过程出错,则输出错误信息 { cout << "输入不合法答案!" << endl; return 0; } //若用户输入和城西计算答案的分子分母相等或分子均为0,则说明用户输入答案正确 if ((rst.denominator == res.denominator && rst.numerator == res.numerator) || (rst.numerator == res.numerator && rst.numerator == 0)) { cnt_right++; cout << "正确!" << endl; } //否则答案错误,程序输出正确答案 else { cnt_wrong++; cout << "错误. 答案是 "; if (res.denominator == 1) cout << res.numerator << "." << endl; else cout << res.numerator << "/" << res.denominator << "." << endl; } } cout << "统计结果..." << endl; //输出统计结果 cout << "你答了" << cnt_right+cnt_wrong << " 道题 "; cout << "正确数:" << cnt_right << " 错误数: " << cnt_wrong << " 。" << endl; infile.close(); } return 0; }
同组的小伙伴:http://www.cnblogs.com/a1397240667/p/5294473.html