自动生成小学四则运算题目的命令行程序

这个作业属于哪个课程 软件工程2024 (广东工业大学)
这个作业要求在哪里 结对项目
这个作业的目标 学习完成项目的过程

正文

一、姓名、学号、GitHub

本次项目缺失队友,由个人完成。崔海源3122004779
github地址

二、PSP表格

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

三、效能分析

从效能分析中看,找到消耗最大的分函数为Count,该函数的主要功能计算生成题目的值。计算时,按照题目要求,对小数的数进行输出分母,所以假设每个数都有分母,若为整数则设分母为1,当进行计算时将他们的分母一同计算,最后将数再转化成字符串。计算过程中主要使用后缀表达式进行计算。

四、设计实现过程

该程序分为两种功能,一种是生成题目并计算出答案,另一种是计算给出题目的答案,打印对错结果。
第一种:

第二种:

五、代码说明

随机生成题目代码
string Numandoper(int oper, int bra,int r)//生成具体数字和运算符以及括号 oper个运算符 bra个括号 oper+1个数字
{
	string pro;
	bool mark = true;    //记录上一个运算是否为/,防止分母为0,默认上一个运算不是/
	bool divs = true;		//最多生成一个除号,多个除号太过麻烦
	if (bra == 0)			//不生成括号
	{
		pro += pronum(r,mark) + ' ';          //生成一个数字,r范围
		
		while (oper--)
		{
			pro += Prooper(mark,divs);				//生成一个运算符
			pro += pronum(r,mark) + ' ';          //生成一个数字,r范围
		}

	}
	else if (bra == 1)				//生成一个括号
	{
		int index_l = Randnum(1, oper);			//判断该左括号落在什么位置
		int index_r = Randnum(index_l + 1, oper + 1);		//右括号至少在左括号过两个数的位置
		int beginpro = 1;	//开始生成题目
		while (beginpro <= oper)
		{
			if (index_l == beginpro)		//生成左括号
			{
				pro += '(';
			}
			pro += pronum(r, mark);			//生成一个数字
			if (index_r == beginpro)		//生成右括号
			{
				pro += ") ";
			}
			else
			{
				pro += ' ';
			}
			pro += Prooper(mark, divs);				//生成一个运算符
			beginpro++;
		}
		//出循环还需要加一个数字
		if (index_l == beginpro)		//生成左括号
		{
			pro += '(';
		}
		pro += pronum(r, mark);			//生成一个数字
		if (index_r == beginpro)		//生成右括号
		{
			pro += ") ";
		}
		else
		{
			pro += ' ';
		}

	}
	else                  //生成两个括号,该种情况只有为3个运算符时所有括号才有意义  为避免出现bug,减少了很多种情况
	{
		int index_l = Randnum(1,2);			//左括号位置1
		int index_ls = Randnum(2,3);			//左括号位置2
		int index_r = index_ls==3 ? 4 : Randnum(3, 4);	//右括号位置1
		int index_rs = Randnum(index_r, 4);	//右括号位置2
		int beginpro = 1;	//开始生成题目
		while (beginpro <= oper)
		{
			if (index_l == beginpro|| index_ls == beginpro)		//生成左括号
			{
				if (index_l == index_ls)
				{
					pro += "(( ";
				}
				else
				{
					pro += '(';
				}
				
			}
			pro += pronum(r, mark);			//生成一个数字
			if (index_r == beginpro|| index_rs == beginpro)		//生成右括号
			{
				if (index_r == index_rs)
				{
					pro += ")) ";
				}
				else
				{
					pro += ") ";
				}
			}
			else
			{
				pro += ' ';
			}
			pro += Prooper(mark, divs);				//生成一个运算符
			beginpro++;
		}
		//出循环还需要加一个数字
		if (index_l == beginpro || index_ls == beginpro)		//生成左括号
		{
			if (index_l == index_ls)
			{
				pro += "(( ";
			}
			else
			{
				pro += '(';
			}
		}
		pro += pronum(r, mark);			//生成一个数字
		if (index_r == beginpro || index_rs == beginpro)		//生成右括号
		{
			if (index_r == index_rs)
			{
				pro += ")) ";
			}
			else
			{
				pro += ") ";
			}
		}
		else
		{
			pro += ' ';
		}
	}

	return pro;
}

生成的题目运算符,运算数,是否有括号全由随机数决定。

计算主要代码

void ChangeTosum2(string s, int& a, int& b)    //小数转化成整数
{

	int n1 = -1, n2 = -1, n3 = -1;
	string a1[4];
	int index_ = 0;
	for (int i = 0; i < s.length(); i++)
	{
		string a2;
		for (int j = i; j < s.length(); j++)
		{
			if (s[j] == '\'')
			{

				n1 = ChangeTosum1(a2);   //保存分数'前的数


				i = j;
				break;
			}
			else if (s[j] == '/')
			{
				n2 = ChangeTosum1(a2);			//保存分数的分子
				i = j;
				break;
			}
			else if (j == s.length() - 1)
			{
				a2 += s[j];
				n3 = ChangeTosum1(a2);			//保存分数的分母
				i = j;
				break;
			}
			a2 += s[j];
		}
		
	}
	if (n1 != -1)
	{
		n2 = n1 * n3 + n2;
		
	}
	
	a = n2;
	b = n3;

	return;
}

string ReaCount(string count,int upmul[],int mul[])
{
	stack<char> ms;
	string beh;
	int num = 0;
	for (int i = 0; i < count.length(); i++)			// 转成后缀表达式
	{
		if (count[i] <= '9' && count[i] >= '0')// 改存数字下标
		{
			beh += (num + 48);
			num++;

			for (int j = i+1; j < count.length(); j++)
			{
				if (count[j] >'9' || count[j] < '0')   //不是数
				{
					
					i = j - 1;
					break;
				}
				if (j == count.length() - 1)
				{
					i = j;
				}
			}
		}
		else if (count[i]=='(')
		{
			ms.push(count[i]);
		}
		else if (count[i] == ')')
		{
			while (ms.top() != '(')    //弹出直到左括号
			{
				beh += ms.top();
				ms.pop();
			}
			ms.pop();      //弹出左括号
		}
		else if (count[i] == '+' || count[i] == '-' || count[i] == '*' || count[i] == '/')
		{
			if (ms.empty())
			{
				ms.push(count[i]);
			}
			else if (count[i] == '+' || count[i] == '-')
			{
				while (!ms.empty()&&ms.top()!='(')
				{

					beh += ms.top();
					ms.pop();
				}
				ms.push(count[i]);
			}
			else            //'*'  '/'的情况
			{
				while (!ms.empty()&&ms.top()!='+'&&ms.top()!='-'&& ms.top() != '(')   
				{

					beh += ms.top();
					ms.pop();
				}
				ms.push(count[i]);

			}
		}
	}
	while (!ms.empty())		//栈可能非空,把剩下所有元素送给表达式
	{
		beh += ms.top();
		ms.pop();
	}

	stack<pair<int, int>> countnum;       //第一个数存数值 第二个数 若该数为小数 存分母 否则存0
	for (int i = 0; i < beh.length(); i++)     
	{
		if (beh[i] <= '9' && beh[i] >= '0')
		{
			int index = beh[i]-48;   //数的下标
			countnum.push({ upmul[index] , mul[index]});        //将数存进去
		}
		else
		{
			int b = countnum.top().first;   //第二个入栈
			int b2 = countnum.top().second;		//该数的分母
			countnum.pop();
			int a = countnum.top().first;
			int a2 = countnum.top().second;		//该数的分母
			countnum.pop();


			switch (beh[i])
			{
			case('+'):
			{	
				a = a * b2;			//互相乘分母
				b = b * a2;
				int c = a2 * b2;     //分母合为一起
				{
					countnum.push({ b + a ,c});
				}
				break;
			}
			case('-'):
			{
				a = a * b2;			//互相乘分母
				b = b * a2;
				if (a - b < 0)							//保证计算过程不出现负数
				{
					return "-1";
				}
				int c = a2 * b2;     //分母合为一起
				{
					countnum.push({ a - b ,c });
				}
				break;
			}
			case('*'):
			{
				int c = a2 * b2;     //分母合为一起
				countnum.push({ b * a ,c });
				break;
			}
			case('/'):				//需要将/外的数乘以分母
			{
				b = b * a2;
				a = a * b2;
				countnum.push({a,b});  
				break;
			}
			}
		}
	}
	string ans;
	if (countnum.top().second!=1)
	{
		if (countnum.top().first % countnum.top().second == 0)
		{
			ans = numtostring(countnum.top().first/ countnum.top().second);
		}
		else
		{
			int a = Gcd(countnum.top().first, countnum.top().second);
			ans = ChangeToString(countnum.top().first/a, countnum.top().second / a);          //小数转化为字符串
		}
		
	}
	else
	{
		ans = numtostring(countnum.top().first);       //一个整数转变为字符串
	}
	
	return ans;
}

string Count(string s)			//将表达式转变为全部为整数的运算   计算题目值
{
	string count;
	int mul[4];       //存分母
	int upmul[4];     //存数
	int index_mul = 0;		//分母个数
	int index_num = 0;		//存数的个数
	for(int i = 0; i < s.length(); i++)
	{
		if (s[i] == '(' || s[i] == ')' || s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/')
		{
			count += s[i];
		}
		else if (s[i] == ' ')  //跳过=号
		{
			continue;
		}
		else
		{
			bool flag = false;   //检查是否为小数
			string thenum;
			for (int j = i; j < s.length(); j++)
			{
				if (s[j] == ')')
				{
					i = j - 1;
					break;
				}
				if (s[j] == ' ')
				{
					i = j;
					break;
				}
				if (s[j] == '/')
				{
					flag = true;
				}
				thenum += s[j];
				
				
			}
			if (flag)					//该数为小数
			{
				int a, b;				//上 、下
				ChangeTosum2(thenum,a,b);			//小数转化为int

				upmul[index_num++] = a;        //整数加上

				mul[index_num-1] = b;			//存进该分母  用数的下标

				count += numtostring(a);       //一个整数转变为字符串 存进去
			}
			else
			{
				upmul[index_num++] = ChangeTosum1(thenum);			//整数转化为int
				mul[index_num - 1] = 1;			//分母默认为1
				count += thenum;				//存整数进去
			}
			

		}
	}
	return ReaCount(count, upmul,mul);




}
主要思路为先将题目的小数去除,记录其分母然后利用下标记录数转化成后缀表达式,这种操作可以使表达式存储的数可以发生更改,更加灵活。然后假设每一个整数的分母为0,再进行具体的+-*/,得到答案。再判断答案是否为小数,在转化成题目要求形式。

六、测试运行

功能一:生成题目与答案


功能二:批改答案对错

七、表格实际时间填写

八、项目小结

由于个人能力有限,程序未能完成题目要求的不能生成重复的题目,但由于该程序随机性太大,生成的题目几乎都不重复。该程序还有一定的bug,就是生成题目时有时候生成失败,由于时间有限,难于修改,生成题目的数字范围越大该bug的产生可能性比较小。生成该题目的括号时,由于括号的结合可能情况很多,而且若允许程序在随意位置生成括号,程序会可能生成无意义的括号,即多余的括号,本程序对括号的产生有一定的限制。

posted @ 2024-03-25 22:01  常泪  阅读(14)  评论(0编辑  收藏  举报