码市的链接:https://coding.net/u/NianQiFeng/p/C-yunsuan/git
一、需求分析
现在家长和老师们都十分注重对小学生计算思维的培养,都希望自己的孩子可以赢在起跑线上,所以大部分老师都会给学生布置不少家庭作业。而老师也往往会利用家长的力量,嘱咐家长在家里为小孩子出题目。但是,大部分家长工作繁忙或者对小学的知识早已淡忘,所以不少家长选择为孩子找家教来做这个工作。
为了让家长可以轻轻松松为小孩子出题,不用特地破费请家教,就需要我们研发出一个操作简单、可以随机生成任意题目数的四则运算自动生成器。这款自动生成器不仅局限于整数运算,分数运算才是它的特点,由于数字是随机生成的,因此分数计算的比重远远大于整数运算,而整数运算只是分数运算的基础,因此只要能够熟练掌握分数运算,则整数运算也就不在话下。
这款运算生成器将大概率出现分数运算,提高了整个题目的难度,对学生的计算提高有很大的帮助。另外,在学生作答之后,立即就会显示出正确的答案,判断学生的答案是否正确,在最终答题结束之后,还会计算出正确率,利于家长对孩子掌握情况的了解。
二、功能设计
基础功能:
1、除了整数以外,还要支持真分数的四则运算,真分数的运算,例如:1/6 + 1/8 = 7/24;
2、运算符为 +, −, ×, ÷;
3、要求能处理用户的输入,并判断答案是否正确,并统计正确率;
4、要求能处理用户输入的真分数,用户输入的答案为分数时,能同样判断对错,如 1/2, 5/12 等;
5、可以使用 -n 参数控制生成题目的个数,例如执行下面命令将生成10个题。如:Myapp.exe -n 10;
扩展功能:
6、对于随机生成的分数,利用辗转相除法改进过来的递归算法来计算最大公约数,使之最简;
7、在答题者输入一道题的答案之后,程序会立即显示该题的正确答案,告诉答题者该题是否正确。
三、代码实现
这个实验我一开始就用的是C语言来写,所以从头到尾也都是通过C语言来完成。
int a, b, c, d, op;
a = rand() % 100;
while (1) {
b = rand() % 100;
if (b != 0) {
break;
}
}
c = rand() % 100;
while (1) {
d = rand() % 100;
if (d != 0) {
break;
}
}
op = rand() % 4;
以上这段代码是用来实现随机生成数字的,其中b和d作为除数,不能为0,所以只有当b和d不为0时,才可以break出来,进行后续的计算。
int m, n;
if(op == 0){
m = a * d + b * c;
n = b * d;
}
else if (op == 1) {
m = a * d - b * c;
n = b * d ;
}
else if (op == 2) {
m = a * c;
n = b * d;
}
else {
m = a * d;
n = b * c;
}
这一段代码是用来计算题目的正确答案的,其中op=0,1,2,3分别对应着“+”、“-”、“*”、“/”。
int gcd(int a, int b)
{
if (b == 0)
return a;
return gcd(b, a % b);
}
int tmp = abs(gcd(a, b));
a /= tmp;
b /= tmp;
tmp = abs(gcd(c, d));
c /= tmp;
d /= tmp;
这两小段代码虽然不长但却很精练,作用在于分数的化简,gcd函数用来求两个数的最大公约数。
四、测试运行
如图,可以指定自动生成十道题,在输入每道题的答案之后,程序会显示该题的正确答案,并判断该题是答对还是答错。在十道题答完之后,最后会显示正确几道题,错误几道题,正确率是多少。
五、psp展示
PSP2.1 |
Personal Software Process Stages |
Time (m) Senior Student |
Time (m) |
Planning |
计划 |
6 |
4 |
· Estimate |
估计这个任务需要多少时间 |
8 |
10 |
Development |
开发 |
60 |
88 |
· Analysis |
需求分析 (包括学习新技术) |
6 |
6 |
· Design Spec |
生成设计文档 |
5 |
6 |
· Design Review |
设计复审 |
4 |
6 |
· Coding Standard |
代码规范 |
3 |
3 |
· Design |
具体设计 |
10 |
16 |
· Coding |
具体编码 |
24 |
50 |
· Code Review |
代码复审 |
5 |
12 |
· Test |
测试(自我测试,修改代码,提交修改) |
9 |
10 |
Reporting |
报告 |
9 |
6 |
· |
测试报告 |
3 |
2 |
· |
计算工作量 |
2 |
3 |
· |
并提出过程改进计划 |
3 |
3 |
由上面的psp表可以看出,我在开发和具体编码这两项上面时间与预计差别巨大。首先,在开发上,由于我一开始审题不够认真,导致对分数运算这个概念了解不够透彻,再加上这只是第一次的作业,让我以为只是写一个整数的四则运算那样的简单程序,所以导致对开发难度预计不足,在开发过程中遇到了不少棘手的问题。
其次,在具体写代码的时候,我才深切得认识到随机的分数运算让整个程序难了好几个层次,因为随机生成的数字不能直接参与计算,而要再经过一次甚至两次的再随机,才能得出分数。另外,分数的计算过程也跟往常的不一样,不论是加减乘除,都要经过不同地变化,才能得出正确的答案。同时,分数还存在化简的问题,种种困难堆在一起,一方面消磨了我的耐心,另一方面也确实需要多花很多时间来找资料解决这些问题,所以在编写代码的过程比预计多花了相当多的时间。
六、小结
刚刚拿到这个题目的时候,由于我审题不够认真,误以为第一次作业老师就是先布置简单的,以为这次作业就是简单的整数四则运算自动生成器,所以一开始没有太在意,以为很快就能做完,所以我在距离deadline三天的时候才开始着手做,在做完整数四则运算程序之后,在于同学的交流中才发现原来自己做错了,我还自以为是地以为这么简单的题目大家怎么做的这么久。这让我深刻地认识到拿到题目之后一定要认真地审题,否则一切的努力将白费;其次就是要谦逊地与同学多交流,可能会发现很多自己一个人所意识不到的问题。
在意识到错误,重新回归正轨之后,也遇到了很多棘手的问题。首先值得我思考的是,如何去随机产生分数和整数?经过一段时间的思考,我的想法是:先只产生分数,如果可以被化简成整数,就用整数输出。在学霸的帮助下,我写了一个用辗转相除法改成递归的算法来计算最大公约数以此来化简。在之后与同学的交流中,我发现了同学们一个更好的办法,那就是先随机产生整数,再随机产生加减乘除符号,再用这些整数和符号随机搭配,就可以自动生成分数的运算。
在纠结如何检验答案正确的过程中,我的思路就是以我们编程计算的正确答案和用户输入的答案进行对比,但是如何对比呢?是用数字间相等还是用字符之间的比较,最后我选择了用字符串的方式来存储用户的输入,再以字符串之间的比较来判断用户的答案。
经过这次的学习,让我重新拾起了两年前的C语言,陌生又熟悉,感觉有趣但是又懒,这个作业真的花了超级多的时间,写出一个程序已经很累了,可是还要写博客、设置git,感觉浪费了很多时间,我也不知道这个时间花的值不值,希望老师们可以再考虑考虑我的吐槽。写到这里已经凌晨12:56分了,可是我的git还没弄成功,还卡在一个地方,如果最后我的应用程序来不及上传到码市上,还请老师手下留情!
七、附录(源程序)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <conio.h>
#include <math.h>
int cnt1, cnt2;
int gcd(int a, int b)
{
if (b == 0)
return a;
return gcd(b, a % b);
}
int main(int argc, char const *argv[])
{
int n = 0;
for (int i = 0; i < strlen(argv[2]); i++) {
n = n * 10 + (argv[2][i] - '0');
}
printf("%d
", n);
srand(time(NULL));
char input[100], ans1[100], ans2[100];
while (n--) {
memset(input, 0, sizeof(input));
memset(ans1, 0, sizeof(ans1));
memset(ans2, 0, sizeof(ans2));
fflush(stdin);
printf("输入q退出,按其他任意键继续
");
char ch = getch();
if (ch == 'q') {
break;
}
int a, b, c, d, op;
a = rand() % 100;
while (1) {
b = rand() % 100;
if (b != 0) {
break;
}
}
c = rand() % 100;
while (1) {
d = rand() % 100;
if (d != 0) {
break;
}
}
op = rand() % 4;
int tmp = abs(gcd(a, b));
a /= tmp;
b /= tmp;
tmp = abs(gcd(c, d));
c /= tmp;
d /= tmp;
printf("%d", a);
if (b != 1) {
printf("/%d", b);
}
if (op == 0) printf(" + ");
else if (op == 1) printf(" - ");
else if (op == 2) printf(" * ");
else printf(" / ");
printf("%d", c);
if (d != 1) {
printf("/%d", d);
}
printf(" = ");
int m, n;
if(op == 0){
m = a * d + b * c;
n = b * d;
}
else if (op == 1) {
m = a * d - b * c;
n = b * d ;
}
else if (op == 2) {
m = a * c;
n = b * d;
}
else {
m = a * d;
n = b * c;
}
tmp = abs(gcd(m, n));
m /= tmp;
n /= tmp;
sprintf(ans1, "%d", m);
if (n != 1) {
sprintf(ans2, "/%d", n);
}
strcat(ans1, ans2);
gets(input);
puts(ans1);
if (strcmp(ans1, input) == 0) {
printf("right
");
cnt1++;
} else {
printf("wrong
");
cnt2++;
}
}
int zql;
zql = (100 * cnt1) / (cnt1 + cnt2);
printf("共答了%d道题,答对%d道题,答错%d道题,正确率为%d%%。
", cnt1 + cnt2, cnt1, cnt2, zql);
return 0;
}