这个作业属于哪个课程 | 软件工程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);
}
六、测试运行
功能一:生成题目与答案
功能二:批改答案对错
七、表格实际时间填写
八、项目小结
由于个人能力有限,程序未能完成题目要求的不能生成重复的题目,但由于该程序随机性太大,生成的题目几乎都不重复。该程序还有一定的bug,就是生成题目时有时候生成失败,由于时间有限,难于修改,生成题目的数字范围越大该bug的产生可能性比较小。生成该题目的括号时,由于括号的结合可能情况很多,而且若允许程序在随意位置生成括号,程序会可能生成无意义的括号,即多余的括号,本程序对括号的产生有一定的限制。
标签:count,题目,命令行,countnum,int,top,四则运算,ms,分母 From: https://www.cnblogs.com/galiji1157/p/18095127