在给出github地址之前,我想先介绍一下关于结对项目—四则运算生成这个小项目的使用规则,四个控制台参数:
(在你想利用GUI页面进行操作时,选择以下任一语句输入)
1.结对项目.exe normal + GUI
2.结对项目.exe normal - GUI
3.结对项目.exe normal * GUI
4.结对项目.exe difficult ** GUI
5.结对项目.exe difficult ^^ GUI
输入完以上语句之后,你需要再输入:python gui.py
利用以上语句的组合,你可以得到 简单模式下,单一运算符的算式,或者困难模式下多运算符、选择一个乘方运算符的算式。
如果你不想利用界面,你可以输入下面式子:
1.结对项目.exe normal +
2.结对项目.exe normal -
3.结对项目.exe normal *
4.结对项目.exe difficult **
5.结对项目.exe difficult ^^
以上语句不需要再进行输入,直接在控制台进行输入,判断对错等都在控制台给出提示。
由于考虑到老师的检查时间,我们让题目的数量为一个随机数,随机数是%1000+1的,由于题目的难度有点大,我们调整了时间为30s。
以上是对在控制台输入的解释
这里给出有github地址:
结对项目–四则运算生成:
给出psp表格,这里只写我个人所用时间:
这次的项目时间和我的预期差距很大,我没想到我思考在这个问题竟然需要这么长时间,反倒是编码,我一个白天就写完了,所以大部分的时间都花在设计上了。
主体思路
这个四则运算的题目:生成四则运算,括号要随便加,不能重复,支持真分数,可以判断对错,支持乘方,统计答对了多少次。
我得思路,对于单操作符的算式,算式里面的符号由用户自己来选择,我会随机生成小于等于五个运算数的式子,括号也是随机进行加上的,是先生成式子,然后在式子里面加括号,多运算符的式子我的想法是运算符,运算数的个数以及大小都随机胜场,括号同样是最后才加,然后我利用逆波兰表达式及逆行求解,这里需要注意几个点,但运算符是不支持除法的,一位内难度太大,同样的,不能除0,乘方后面的数字不能过大,我都默认的设定为2,这样方便运算,我把每个结果都存起来,然后你可以通过页面或者控制台进行答题。
对于支持真分数的运算,我选择建立一个分数类,由于只有在多运算符的式子里才会有分数的出现,所以我把多运算符式子中的数字都用这个类的一个对象来表示,同样的答案也是如此,我把所有的答案都放到一个vector里,当想利用界面时候,我就把界面所需要的式子以及答案都输入到文件中,这样就方便了跨语言的协作。
面向对象设计
将uml面向对象技术应用到在这个项目,功能模型要画用例图
静态模型要画类图:
plus这个类包括单符号的所有情况
功能模型要画顺序图,由于这里交互不是很明显,所以不再给出顺序图。
代码
在这里给大家看一下重要的代码:
这是我分数的大体框架:
这里重点实现了分数的加减乘除,重点在于约分,这里我利用辗转相除法,给大家看一下具体的实现:
int yuefen(int a, int b)
{
if (a == 0 || b == 0)
return 1;
int c;
if (a < b)
{
a = a + b;
b = a - b;
a = a - b;
}
c = a % b;
while (a%b != 0)
{
a = b;
b = c;
c = a % b;
}
return b;
}
下面是我的normal难度的运算式的类:
我抽象出来的basic类本来以为多重运算符也可以用到,但是发现他俩的出入有点大。
在这个基本的运算式类中,函数有选择运算模式(±*),打印,将生成的式子,加上括号并运算,在加括号的时候,用到的是类似树的思想,左右回溯,或者说是二分法,用数组来存。
给大家重点看一下transforstring这个函数里面的一些细节:
void transforstring(int max,FILE * file1,vector<int> &a)
{
int sum;
if (n == 2)//只有两个操作数不需要添加括号
{
int j = 0;
for (int i = 0; i < n; i++)
{
if (number[i] >= 10)
{
number1[j] = '0' + number[i] / 10;
j++;
number1[j] = '0' + number[i] % 10;
j++;
}
else
{
number1[j] = '0' + number[i];
j++;
}
if (i == 1)
{
number1[j] = '=';
j++;
}
else
{
number1[j] = model;
j++;
}
}
sum = j;
}
else//超过两个操作数的情况
{
memset(Fkhpos, 0, sizeof(Fkhpos));
memset(Bkhpos, 0, sizeof(Bkhpos));
int j = 0;
int partition = rand() % n;//随机定义断点
if (partition == 0)
partition++;
if (khnumhave <= khnum&&whekh==1)
{
lefthuisu(1, partition);//左边回溯
if (partition != n - 1)
{
righthuisu(partition+1, n);//右边回溯
}
}
for (int i = 0; i < n; i++)
{
while (Fkhpos[i]!=0)
{
number1[j] = '(';
j++;
Fkhpos[i]--;
}
if (number[i] >= 10)
{
number1[j] = '0' + number[i] / 10;
j++;
number1[j] = '0' + number[i] % 10;
j++;
}
else
{
number1[j] = '0' + number[i];
j++;
}
while (Bkhpos[i] != 0)
{
number1[j] = ')';
j++;
Bkhpos[i]--;
}
if (i != n - 1)
{
number1[j] = model;
j++;
}
else
{
number1[j] = '=';
j++;
}
}
sum = j;
}
for (int i = 0; i < sum; i++)
//cout << number1[i];
fputc(number1[i], file1);
fputc('\n', file1);//将式子输入到文件里
int maxm=0;
for (int i = 0; i < n; i++)
{
if (model == '+')
maxm += number[i];
if (model == '-')
maxm -= number[i];
if (model == '*')
maxm *= number[i];
}a.push_back(maxm);//将结果存到a里,方便后续的判对错
}
下面是多重运算符式子的类:
函数分别有:选择乘方的模式,生成表达式,得到答案,进行运算,添加括号时的左右回溯。
运算就是利用两个栈来实现逆波兰表达式的计算,计算完毕后,都存入一个vector之中。
主函数就是对控制台的输入进行分析这里详情可以看个人工程的内容。
最后是软件的性能测试辣:
这是我的程序里占用cpu比较多的,逐个点开:
这个语句占用时间较多
可以发现大部分的时间都被pushback占用,我的程序里还不光这个两个地方用到了这个库函数,这就是我程序占用时间里最长的因素。