二柱子的难题02

Posted _Just

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二柱子的难题02相关的知识,希望对你有一定的参考价值。

老师又向二柱子提出一些要求,二柱子很崩溃

技术分享

具体要求如下:

  1. 题目避免重复
  2. 可定制(数量/打印方式)

可控制参数
    1是否有乘除法
    2是否有括号
    3数值范围
    4加减法有无负数
    5除法有无余数

可怜的二柱子经过8h的奋战,终于在01版本的基础上改出了满足要求的02版本

技术分享

 

 

  1 #include <iostream>
  2 #include <iomanip>
  3 #include <fstream>
  4 #include <ctime>
  5 #include <cstdlib>
  6 #include <sstream>
  7 #include <string>
  8 #include <vector>
  9 #include <algorithm>
 10 using namespace std;
 11 
 12 bool isTrueFraction(int numerator, int denominator)    //判断产生的分数是否是真分数
 13 {
 14     if(numerator >= denominator)
 15         return false;
 16 
 17     for(int i = 2 ; i <= numerator ; i++)      //判断分数是否能够约分
 18     {
 19         if(numerator % i ==0 && denominator % i ==0)
 20             return false;
 21     }
 22 
 23     return true;
 24 }
 25 
 26 string getNum(int start = 0, int end = 100, bool isParentheses = false, int depth = 0)
 27 {
 28     int n = rand();
 29     if(isParentheses)      // 若带括号 则为 a op ( b op c) 的形式 op为运算符
 30     {
 31         int num1 = rand() % (end - start + 1) + start;
 32         stringstream ss;
 33         ss << num1;
 34 
 35         if(depth  < 9)     //控制递归层数,带括号的式子少于10个
 36         {
 37             string num2 = "(" + getNum(start,end, n % 2 == 0,depth + 1) + ")";
 38             return ss.str() +","+ num2;
 39         }
 40         else
 41         {
 42             string num2 = "(" + getNum(start,end, false) + ")";
 43             return ss.str() +","+ num2;
 44         }
 45     }else
 46     {
 47         if(n % 7 == 0)       //若随机数n是7的倍数,产生一个真分数和一个整数,否则为两个整数
 48         {
 49             int num1 = rand() % (end - start + 1) + start;
 50             int num2 = rand() % (end - start + 1) + start;
 51             int num3 = rand() % (end - start + 1) + start;
 52 
 53             if(isTrueFraction(num1,num2))
 54             {
 55                 stringstream s1,s2,s3;   //将int转为string
 56                 s1<<num1;
 57                 s2<<num2;
 58                 s3<<num3;
 59                 return s1.str()+"/"+s2.str()+","+s3.str();
 60             }else
 61             {
 62                 return getNum(start,end);
 63             }
 64         }else
 65         {
 66             int num1 = rand() % (end - start + 1) + start;
 67             int num2 = rand() % (end - start + 1) + start;
 68             stringstream s1,s2;
 69             //string ss1,ss2;
 70             s1<<num1;
 71             s2<<num2;
 72             return s1.str()+","+s2.str();
 73         }
 74     }
 75 
 76 
 77 }
 78 
 79 char getOperator(string num2 = "1", bool isMulandDiv = true)   // 默认第二个参数不为0,默认包括乘除法
 80 {
 81     char op[] = {+,-,*,/};
 82 
 83     if(isMulandDiv)
 84     {
 85         if(num2 == "0") //避免除数为0
 86             return op[rand() % 3];
 87         else
 88             return op[rand() % 4];
 89     }else
 90     {
 91         return op[rand() % 2];     //只包含加减
 92     }
 93 }
 94 
 95 bool isDup(vector<string> &items, string item)    //若重复返回true,否则返回false
 96 {
 97     if(find(items.begin(),items.end(),item) == items.end())
 98         return false;
 99     else
100         return true;
101 }
102 
103 bool isNegative(string num1, string num2, char op)      //判断两数加减的正负
104 {
105     stringstream ss1,ss2;
106     int n1,n2;
107     ss1 << num1;
108     ss1 >> n1;
109     ss2 << num2;
110     ss2 >> n2;
111     if(op == -)
112     {
113         if(n1 < n2)
114         {
115             return true;
116         }else
117         {
118             return false;
119         }
120     }else
121     {
122         if(n1 + n2 < 0)
123             return true;
124         else
125             return false;
126     }
127 
128 }
129 
130 bool isRemainder(string num1, string num2)   //判断两数相除有无余数
131 {
132     stringstream ss1,ss2;
133     int n1,n2;
134     ss1 << num1;
135     ss1 >> n1;
136     ss2 << num2;
137     ss2 >> n2;
138 
139     if(n1 % n2 == 0)
140         return false;
141     else
142         return true;
143 }
144 
145 void print(vector<string> &items, bool isCmd)
146 {
147     vector<string>::iterator it = items.begin();
148     if(isCmd)
149     {
150         for(;it != items.end(); it++)
151         {
152             cout << (*it) <<endl;
153         }
154     }else
155     {
156         ofstream of("problems.txt");
157         if(!of)
158             exit(1);
159 
160         for(;it != items.end() ; it++)
161             of<<*it <<endl;
162 
163         of.close();
164     }
165 }
166 
167 //void itemsGenerate(int itemNum, bool isCmd)
168 //{
169 
170 //}
171 
172 int main(int argc, char *argv[])
173 {
174     srand((int)time(0));    //设定时间种子
175     vector<string> items;   //将题目存在items中,用于判断是否重复和输出
176     int itemNum,tmp;  //题目数量
177     char ttmp;
178     bool isCmd;    //打印方式
179     bool isMulandDiv;     //是否有乘除法
180     bool isParentheses;  //是否带括号
181     int start,end;  //数值范围
182     bool isNeg;    //有无负数
183     bool isRem;    //有无余数
184     bool addFlag = false;  //添加标识
185 
186     cout << "请输入题目数量:" << endl;       //定制题目数量、打印方式等
187     cin >> itemNum;
188     if(itemNum < 0 )
189     {
190         cout << "非法的题目数量!" <<endl;
191         exit(1);
192     }
193 
194     cout << "请输入打印方式(0. 输出到屏幕 1.输出到文件)" <<endl;
195     cin >> tmp;
196     if(tmp == 0)
197     {
198         isCmd = true;
199     }else if(tmp == 1)
200     {
201         isCmd = false;
202     }else
203     {
204         cout << "非法的打印方式!" <<endl;
205         exit(1);
206     }
207 
208     cout << "请输入是否有乘除法(Y/N)" <<endl;
209     cin >>ttmp;
210     if(ttmp == y || ttmp == Y)
211     {
212         isMulandDiv = true;
213     }else if (ttmp == N || ttmp == n)
214     {
215         isMulandDiv = false;
216     }else
217     {
218         cout << "非法输入!"<<endl;
219         exit(1);
220     }
221 
222     cout << "请输入是否有括号?(Y/N)" <<endl;
223     cin >>ttmp;
224     if(ttmp == y || ttmp == Y)
225     {
226         isParentheses = true;
227     }else if (ttmp == N || ttmp == n)
228     {
229         isParentheses = false;
230     }else
231     {
232         cout << "非法输入!"<<endl;
233         exit(1);
234     }
235 
236     cout << "请输入数值范围:(中间由一空格分隔)" << endl;
237     cin >> start >> end ;
238     if(start > end)
239     {
240         swap(start, end);
241     }
242 
243     cout << "加减法有无负数?(Y/N)" <<endl;
244     cin >> ttmp;
245     if(ttmp == y || ttmp == Y)
246     {
247         isNeg = true;
248     }else if (ttmp == N || ttmp == n)
249     {
250         isNeg = false;
251     }else
252     {
253         cout << "非法输入!"<<endl;
254         exit(1);
255     }
256 
257     cout << "乘除法有无余数?(Y/N)" <<endl;
258     cin >> ttmp;
259     if(ttmp == y || ttmp == Y)
260     {
261         isRem = true;
262     }else if (ttmp == N || ttmp == n)
263     {
264         isRem = false;
265     }else
266     {
267         cout << "非法输入!"<<endl;
268         exit(1);
269     }
270 
271 
272     for(;items.size() != itemNum ;)    //根据条件生成问题
273     {
274         string num = getNum(start,end,isParentheses);
275         while (num.find(",") != string::npos)
276         {
277             addFlag = true;
278             if( num[num.find(",") + 1] == ()     //运算符后紧跟括号,运算符选取只和isMulandDiv有关
279             {
280                 char op = getOperator("1",isMulandDiv);
281                 stringstream ss;
282                 ss << op;
283                 num = num.replace(num.find(","),1,ss.str());
284             }else                             //运算符后是数字,运算符选取和num2和isMulandDiv有关,此时是不带括号或最右边的算式
285             {
286                 //string::iterator it= num.find(",");
287                 string num2 = num.substr( num.find(",") + 1, num.find(")",num.find(",") + 1));
288                 char op = getOperator(num2,isMulandDiv);
289                 stringstream ss;
290                 ss << op;
291                 num = num.replace(num.find(","),1,ss.str());
292                 int begin = 0;    //找到形如 a op b 的式子
293                 if(num.find("(") != string::npos)
294                     begin = num.find_last_of("(") + 1;
295                 string num1 = num.substr(begin,num.find(ss.str()));
296                 num2 = num.substr(num.find_last_of(ss.str()) + 1,num.find_first_of(")"));
297                 if(op == - || op == +)
298                 {
299 
300                     if(!isNeg && isNegative(num1,num2,op))
301                     {
302                         addFlag = false;
303                         break;
304                     }
305 
306                 }else if(op == /)
307                 {
308                     if(!isRem && isRemainder(num1,num2))
309                     {
310                         addFlag = false;
311                         break;
312                     }
313                 }
314             }
315 
316         }
317         if(!addFlag)    //满足要求,可以添加
318         {
319             continue;
320         }
321 
322         if(!isDup(items,num))    //判断是否重复,不重复则添加
323         {
324             items.push_back(num);
325         }
326 
327 
328     }
329 
330     print(items,isCmd);
331     return 0;
332 }

首先,用vector 存式子 ,1、方便检查重复 2、方便集中输出(屏幕或文件)

然后,生成式子带括号使用递归实现,通过depth控制深度,防止式子过长,getNum()获得参与运算的数,由“,”作为占位符,

等数值生成完毕后,在通过getOperator()获得运算符,此时运算符的获得由紧跟运算符后的数值和有无乘除的控制标识 共同决定,

P.S. 本程序考虑运算符,加减法有无负数,除法有无余数 仅考虑后面紧跟一个数值的情况,

如果后面是一个由“(”和“)”包围的式子,则不考虑了, 原谅我介一生不羁放纵爱自由 ~~~

 

从带括号的式子中取 子字符串 然后比较的部分在 271~314行,这部分比较绕,调试了很久。

附上一组测试数据: (把代码176~269行注释掉换成数据即可)

  /* 最常规的方式 */
    int itemNum = 10;  //题目数量
    bool isCmd = 0;    //打印方式 0.屏幕输出 1.文件输出
    bool isMulandDiv= true;     //是否有乘除法
    bool isParentheses = true;  //是否带括号
    int start = 0;    //数值范围起始
    int end = 100;  //数值范围结束
    bool isNeg = true;    //有无负数
    bool isRem = true;    //有无余数


    /*不带乘除法 数值范围为负 */
    int itemNum = 10;  //题目数量
    bool isCmd = 0;    //打印方式 0.屏幕输出 1.文件输出
    bool isMulandDiv = false;     //是否有乘除法
    bool isParentheses = true;  //是否带括号
    int start = -100;    //数值范围起始
    int end = 0;  //数值范围结束
    bool isNeg = true;    //有无负数
    bool isRem = true;    //有无余数

    /*文件输出 除法没余数 加减不为负*/
    int itemNum = 10;  //题目数量
    bool isCmd = 1;    //打印方式 0.屏幕输出 1.文件输出
    bool isMulandDiv = true;     //是否有乘除法
    bool isParentheses = true;  //是否带括号
    int start = 0;    //数值范围起始
    int end = 100;  //数值范围结束
    bool isNeg = false;    //有无负数
    bool isRem= false;    //有无余数

    /* 不带括号 */
    int itemNum = 10;  //题目数量
    bool isCmd = 0;    //打印方式 0.屏幕输出 1.文件输出
    bool isMulandDiv = true;     //是否有乘除法
    bool isParentheses = false;  //是否带括号
    int start = 0;    //数值范围起始
    int end = 100;  //数值范围结束
    bool isNeg = true;    //有无负数
    bool isRem = true;    //有无余数


    /*数值范围有正有负 */
    int itemNum = 10;  //题目数量
    bool isCmd = 0;    //打印方式 0.屏幕输出 1.文件输出
    bool isMulandDiv = true;     //是否有乘除法
    bool isParentheses = true;  //是否带括号
    int start = -100;    //数值范围起始
    int end = 100;  //数值范围结束
    bool isNeg = true;    //有无负数
    bool isRem = true;    //有无余数

    /*不生成式子 */
    int itemNum = 0;  //题目数量
    bool isCmd = 0;    //打印方式 0.屏幕输出 1.文件输出
    bool isMulandDiv = true;     //是否有乘除法
    bool isParentheses = true;  //是否带括号
    int start = 0;    //数值范围起始
    int end = 100;  //数值范围结束
    bool isNeg = true;    //有无负数
    bool isRem = true;    //有无余数

 

 

 附上运行截图3张:

技术分享

技术分享

这是输出到文本里的

技术分享

 

总之 ~~~

本部分任务算是完成了,如有问题 ,欢迎老师同学提出批评建议。

 

以上是关于二柱子的难题02的主要内容,如果未能解决你的问题,请参考以下文章

二柱子—四则运算1

2019春第二课程设计报告

二柱子四则运算在线答题系统

二柱子四则运算2.0版本

二柱子课后题加强版的加强版

二柱子四则运算3