一、题目及题目要求
编程随机生成四则运算,算数包括整数和真分数
1.题目避免重复
2.可定制(数量/打印方式)
3.可以控制下列参数:
是否有乘除法;
是否有括号(最多可以支持十个数参与运算);
数值范围;
加减有无负数;
除法有无余数。
4.输入结果并判断正确
二、设计思路
在上次程序的基础上进行修改,
1.题目避免重复:为避免随机数每次相同用了srand函数。
2.可定制出题数量:通过键盘输入数字,在for循环中控制循环次数。
3. 是否有乘除法:把算符分为两大类,利用case语句选择前四种加和减,后四种乘除。
4. 数值范围:在产生随机数时通过输入控制rand函数的参数,从而使运算数不超过此范围。
5. 加减有无负号:结果无负数,判断两操作数,第一操作数若比第二操作数小,则第二个减第一个。
6.除法有无余数:除法无余数,判断第一操作数模第二操作数的结果,若为0即可输出,否则再循环一次。
7.是否有括号:先随机生成一个数字,代表着生成表达式中操作数的个数。再循环生成一个数字,将其输出,然后等概率生成‘+’‘-’‘*’‘/’中的一个跟在该数字后面,输出。以一定概率生成左括号,若生成了左括号则输出,并进行计数标志当前共有多少个未完成匹配的左括号。若当前有未完成匹配的左括号,则在生成一个数字后,生成一个操作符前,以一定的概率生成右括号。在生成完毕后,生成最后一个数并将为匹配的左括号予以匹配。把产生的式子存入文件。
8.输入结果并判断正确:(1)对于两个运算数的式子,把结果存入数组,调用函数与数组中的值比较。
(2)对于多个运算数的式子,从文件中读取内容,利用中缀表达式转换为后缀表达式,然后就是后缀表达式的计算,最后与用户输入结果对比。
三、代码
1 #include<iostream> 2 #include<ctime> 3 #include<stack> 4 #include<fstream> 5 #define length 10000//存放答案数组长度 6 using namespace std; 7 typedef long long ll; 8 ofstream fout("equation.txt"); 9 char Op[] = {'+', '-', '*', '/'}; 10 int rights;//对题数目 11 int wrong;//错题数目 12 13 struct num{ 14 ll numerator, denominator; 15 num(){numerator = 0; denominator = 1;} 16 num(int n) {numerator = n; denominator = 1;} 17 num(int n,int d) {numerator = n; denominator = d;} 18 19 void operator = (num x) 20 { 21 numerator = x.numerator; 22 denominator = x.denominator; 23 } 24 }; 25 #define maxl 1005 26 char nifix[maxl], post[maxl]; 27 char ans[maxl]; 28 int cnt_right, cnt_wrong; 29 bool error; 30 num res, rst; 31 32 //****分数类***// 33 class fraction 34 { 35 private: 36 int above; //分子 37 int below; //分母 38 void reduction(); //约分 39 fraction makeCommond(fraction); //通分 40 41 public: 42 fraction() 43 { //构造函数 44 } 45 fraction add(fraction); //两分数相加 46 fraction sub(fraction); //两分数相减 47 fraction mul(fraction); //两分数相乘 48 fraction div(fraction); //两分数相除 49 int display(int,int); //显示分数 50 void setvalue(int ,int); //存储分数 51 }; 52 53 //***********分数的约分*********// 54 55 void fraction::reduction() 56 { 57 int i,comdiv,small,max; 58 if(above<below) 59 { 60 small=above; 61 max=below; 62 } 63 else 64 { 65 small=below; 66 max=above; 67 } 68 for(i=small;i>1;i--) 69 { 70 if((small%i==0 )&(max%i==0) ) 71 break; 72 } 73 comdiv=i; //最大公约数 74 75 if(i!=0) 76 { 77 above/=i; 78 below/=i; 79 } 80 } 81 82 //*************分数的通分*************// 83 84 fraction fraction::makeCommond(fraction frac) 85 { 86 int b1=below,b2=frac.below, m,s; 87 if(b1>b2) 88 { 89 m=b1%b2; 90 s=b2; 91 } 92 else 93 { 94 m=b2%b1; 95 s=b1; 96 } 97 while(m>0) 98 { 99 int res=s%m; 100 s=m,m=res; 101 } 102 int small=(b1*b2)/s; 103 above=above*(small/below); 104 frac.above=frac.above*(small/frac.below); 105 below=small; 106 frac.below=small; 107 return frac; 108 } 109 //***************分数的相加*************// 110 111 fraction fraction::add(fraction fr) 112 { 113 fraction myFraction; 114 myFraction.above=above*fr.below+fr.above*below; 115 myFraction.below=below*fr.below; 116 myFraction.reduction(); 117 return myFraction; 118 } 119 //*********************分数的相减***************// 120 121 fraction fraction::sub(fraction fr) 122 { 123 fraction myFraction; 124 myFraction.above=above*fr.below-fr.above*below; 125 myFraction.below=below*fr.below; 126 myFraction.reduction(); 127 return myFraction; 128 } 129 130 //*******************分数的相乘****************// 131 132 fraction fraction::mul(fraction fr) 133 { 134 fraction myFraction; 135 myFraction.above=above*fr.above; 136 myFraction.below=below*fr.below; 137 myFraction.reduction(); 138 return myFraction; 139 } 140 //******************分数的相除***********// 141 142 fraction fraction::div(fraction fr) 143 { 144 fraction myFraction; 145 myFraction.above=above*fr.below; 146 myFraction.below=below*fr.above; 147 myFraction.reduction(); 148 return myFraction; 149 } 150 151 //*********************分数答案的输入判断*************// 152 153 int fraction::display(int a,int b) 154 { 155 156 if((a==above)&&(b==below)) 157 { 158 cout<<"正确"<<endl; 159 rights=rights+1; 160 } 161 else 162 { 163 cout<<"错误"<<endl; 164 wrong=wrong+1; 165 } 166 return rights,wrong; 167 } 168 169 //*******************分数的赋值****************// 170 171 void fraction::setvalue(int sj1,int sj3) 172 { 173 above=sj1; 174 below=sj3; 175 } 176 //*************无分数,无余数答案判断****************// 177 178 int answer(int a[],int i) 179 { 180 int ans; 181 182 cout<<"请输入答案:"<<endl; 183 cin>>ans; 184 if(ans==a[i]) 185 { 186 cout<<"正确"<<endl; 187 rights=rights+1; 188 } 189 else 190 { 191 cout<<"错误"<<endl; 192 wrong=wrong+1; 193 } 194 return rights,wrong; 195 196 } 197 //*************无分数,有余数答案判断****************// 198 int answer_1(int a[],int i,int b[]) 199 { 200 int ans,yushu; 201 202 cout<<"请输入商:"<<endl; 203 cin>>ans; 204 cout<<"输入余数"<<endl; 205 cin>>yushu; 206 if((ans==a[i])&&(yushu=b[i])) 207 { 208 cout<<"正确"<<endl; 209 rights=rights+1; 210 } 211 else 212 { 213 cout<<"错误"<<endl; 214 wrong=wrong+1; 215 } 216 return rights,wrong; 217 218 } 219 //*************产生带括号式子****************// 220 void create(int maxn) 221 { 222 223 if(!fout) //如果打开失败,outfile返回值 224 { 225 cerr << "open error!" << endl; 226 exit(1); 227 } 228 229 //首先随机生成算式中操作数的个数,其数量必须大于1 230 int lengt;//式子长度 231 do{ 232 lengt = rand()%8; 233 }while(lengt < 2); 234 bool div = false; //用来防止出现除0错误 235 int brack_cnt = 0; //记录未匹配的左括号个数 236 ll num, op; 237 for (int i = 1; i < lengt; i++) //循环生成算式 238 { 239 if (div) //若此时需要生成的数字前的负号是'/',则需要特判此次生成的数字不能为0 240 { 241 div = false; 242 do{ 243 num = rand()%maxn; 244 }while(num == 0); 245 cout<< num; 246 fout<< num; 247 248 } 249 else 250 { 251 num= rand()%maxn; 252 fout <<num; 253 cout<<num; 254 }//否则直接生成数字输出 255 int tmpcnt = brack_cnt; 256 for (int j = 0; j < tmpcnt; j++) //若当前有未匹配的左括号,则对每一个未匹配的左括号,都有一定概率生成相应右括号。 257 { 258 if ((rand()%5) > 2) //生成右括号概率为0.6 259 { 260 brack_cnt--; 261 262 fout << ")"; 263 cout<<")"; 264 } 265 } 266 267 op = rand()%4; //生成运算符 268 fout << Op[op]; 269 cout << Op[op]; 270 if (op == 3) //若生成了除号,则需要置相应标志位 271 div = true; 272 273 if (!(rand()%3)) //以一定概率生成左括号,概率为1/3 274 { 275 fout << "("; 276 cout<<"("; 277 brack_cnt++; 278 num= rand()%maxn;//生成左括号后必须生成一个数字和运算符,不然可能出现(15)这样的错误 279 fout <<num; 280 cout<<num; 281 op = rand()%4; 282 fout << Op[op]; 283 cout<<Op[op]; 284 if (op == 3) 285 div = true; 286 } 287 } 288 if (div) //生成最后一个数字,该数字后不需要跟运算符 289 { 290 div = false; 291 do{ 292 num = rand()%maxn; 293 }while(num == 0); 294 fout << num; 295 cout<< num; 296 } 297 else 298 { 299 num=rand()%maxn; 300 fout << num; 301 cout<<num; 302 303 } 304 while(brack_cnt--) //补全右括号 305 { 306 fout << ")"; 307 cout << ")"; 308 } 309 cout<<"="; 310 fout<< endl; 311 cout<<endl; 312 313 } 314 bool isNum(char x) //判断是否是数字 315 { 316 return (x >= '0' && x <= '9'); 317 } 318 319 bool isOp(char x) //判断是否是操作符 320 { 321 return (x == '+' || x == '-' || x == '*' || x == '/' || x == '(' || x == ')'); 322 } 323 324 int priority(char x) //返回一个操作符的优先级 325 { 326 if (x == '-' || x == '+') 327 return 1; 328 else if (x == '*' || x == '/') 329 return 2; 330 else if (x == '(') 331 return 0; 332 else 333 return -1; 334 } 335 bool nifix_to_post() 336 { 337 memset(post, 0, sizeof(post)); 338 stack<char> s; //操作符栈,用来压操作符 339 /* ************************************************************************************************ 340 # 由于操作数是多位的,所以我们逐位做一个累加暂存的工作,当遇到操作符时,代表此操作符前的数暂存完毕, 341 # 需要将其入栈,但也不是所有操作符前都有操作数,如'*('等,所以我们需要一个标志位来表示某个操作符前 342 # 是否进行过暂存操作数的处理。 343 *///************************************************************************************************** 344 bool havenum = false; 345 int tmp = 0, pos = 0; //tmp为暂存多位数的变量,pos为后缀数组存储位置 346 for (int i = 0; nifix[i] != '