一. github
github 地址:https://github.com/DongDGT/ArithmeticGenerator
项目成员:吴宗东,3118005112;林涛,3118005100
二 .psp
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
60 |
|
· Estimate |
· 估计这个任务需要多少时间 |
20 |
|
Development |
开发 |
1570 |
|
· Analysis |
· 需求分析 (包括学习新技术) |
20 |
|
· Design Spec |
· 生成设计文档 |
30 |
|
· Design Review |
· 设计复审 (和同事审核设计文档) |
10 |
|
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
10 |
|
· Design |
· 具体设计 |
180 |
|
· Coding |
· 具体编码 |
1200 |
|
· Code Review |
· 代码复审 |
20 |
|
· Test |
· 测试(自我测试,修改代码,提交修改) |
100 |
|
Reporting |
报告 |
120 |
|
· Test Report |
· 测试报告 |
60 |
|
· Size Measurement |
· 计算工作量 |
10 |
|
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
30 |
|
合计 |
|
1730 |
|
三.效能分析
可以看出文件的打开和关闭占用了非常多的资源,设置生成10000道题只生成6000多道题目用了2分钟多而且还暂停了,所以对此做了一些改进
改进之后
改进之后生成10000道题目只花了0.63秒
尝试一下生成10万道题目
四.代码设计过程
1.使用语言:C语言
2.解题思路:
(1):对每个算式进行拆分,将各个操作数和操作符独立开来。各个操作数和操作符按照特定的顺序排列,再加上答案,组成算式。
(2):需求分析使用message参数获取。
(3):括号随机插入算式中,算式在输出的时候再转化为字符串。
(4):对于判重,没有做到题目中将的那样,只好将之简化,不允许生成答案一样的题目,这样就避免了生成题目重复的问题。
3.项目文件结构ArithmeticGenerrator
├── .vscode
├── Answers .txt // 存放答案文件
├── Exercises.txt // 存放题目文件
├── Debug
├── Release
├── main.cpp
├──main.h
4.函数之间的关系
五.代码说明
消息处理
1 //消息处理,topnum为题目的数量,numrange为数值的范围 2 bool MessageProcess(int *topnum ,int *numrange ,int messageCount ,char *message[]){ 3 *topnum = 10; //设题目数量的默认值为10 4 if(messageCount == 5 && strcmp(message[1], "-r")==0 && strcmp(message[3],"-n") == 0){ 5 if(StrToInt(numrange ,message[2]) == false|| (StrToInt(topnum, message[4]) == false)) return false; 6 } 7 else if(messageCount == 5 && strcmp(message[1], "-n")==0 && strcmp(message[2],"-r") == 0){ 8 if(StrToInt(topnum ,message[2]) == false|| (StrToInt(numrange, message[4]) == false)) return false; 9 } 10 else if(messageCount == 3&&strcmp(message[1], "-r")==0 ){ 11 if(StrToInt(numrange ,message[2]) == false) return false; 12 } 13 else return false; 14 return true; 15 }
算式生成
1 //随机算式生成,输出生成的算式,算式由数个算数单元组成 2 int RandomArithmetic(arithmeticAtom* arithmetic,int length,int range) { 3 int countRandom; 4 countRandom = RandomNum(3)+1; 5 // 6 7 int i = 0,n=0; 8 while (i < countRandom+1) { 9 10 //生成分数或整数 11 //1按整数生成 12 if (RandomNum(2)) { 13 arithmetic[n].arithmeticData.num = RandomNum(range+1); 14 arithmetic[n].type = dataType::NUM; 15 n++; 16 } 17 /****************************************************************/ 18 //0为按分数生成 19 else { 20 arithmetic[n].arithmeticData.fraction.denominator = RandomNum(range)+1; 21 arithmetic[n].arithmeticData.fraction.molecule = RandomNum(arithmetic[n].arithmeticData.fraction.denominator); 22 //若随机的分子为0,转化为整数 23 if (arithmetic[n].arithmeticData.fraction.molecule == 0){ 24 arithmetic[n].arithmeticData.num = 0; 25 arithmetic[n].type = dataType::NUM; 26 } 27 else { 28 arithmetic[n].type = dataType::FRACTION; 29 SimplifyFraction(&arithmetic[n]); 30 } 31 n++; 32 } 33 /**************************************************************/ 34 //生成运算符 35 if (i + 1 < countRandom+1) { 36 arithmetic[n].arithmeticData.oper = RandomOper(); 37 arithmetic[n++].type = dataType::OPER; 38 } 39 i++; 40 } 41 42 //判断算式是否结束 43 arithmetic[n].arithmeticData.oper = ‘\0‘; 44 arithmetic[n].type = dataType::OPER; 45 //插入括号 46 BracketsRandom(countRandom, arithmetic, length,&n); 47 48 return n; 49 }
1 void AtomToString(char* string,arithmeticAtom* atom,int atomLength) { 2 int a = 0, b = 0, c = 0; 3 int i = 0,j = 0; 4 /*i:atom[i] ,j:strin 12+4 atom[0].data.num=12->"12"g[j]*/ 5 /*arithmeticData*/ 6 /* denominator:分母*/ 7 /*molecule:分子*/ 8 while(i < atomLength){ 9 if(atom[i].type == NUM){//atom[i]整数 10 a = atom[i].arithmeticData.num; 11 AddString(a, string, &j);//从string[j]把整数a传入 12 } 13 else if(atom[i].type == FRACTION){ 14 a = atom[i].arithmeticData.fraction.molecule; 15 b = atom[i].arithmeticData.fraction.denominator; 16 if (a > b) { 17 c = (a / b); 18 a -= b * c; 19 AddString(c, string, &j); 20 string[j++] = ‘\‘‘; 21 } 22 AddString(a, string, &j); 23 string[j++] = ‘|‘; 24 AddString(b, string, &j); 25 } 26 else if(atom[i].type == OPER){ 27 string[j++] = atom[i].arithmeticData.oper; 28 } 29 else{ 30 printf("TO-STRING-ERROR"); 31 } 32 string[j++] = ‘ ‘; 33 i++; 34 } 35 string[j++] = ‘\0‘; 36 }
判断算式是否满足题目条件
//计算算式答案及该算式是否可用 bool Calculation(arithmetic *arith) { bool isRight = true; std::stack<arithmeticAtom> numStack, operStack; arithmeticAtom answer ,a,b; answer.type = NUM; answer.arithmeticData.num = 0; for (int i = 0; i < arith->length&&isRight; i++) { if (arith->a[i].type != dataType::OPER) { numStack.push(arith->a[i]); } else if (arith->a[i].arithmeticData.oper == ‘(‘|| operStack.empty()) { operStack.push(arith->a[i]); } else if(arith->a[i].arithmeticData.oper == ‘)‘){ while (operStack.top().arithmeticData.oper != ‘(‘ && isRight) { b = numStack.top(); numStack.pop(); a = numStack.top(); numStack.pop(); switch (operStack.top().arithmeticData.oper) { case ‘+‘: isRight = Addition(&answer, a, b); break; case ‘-‘: isRight = Subtraction(&answer, a, b); break; case ‘*‘: isRight = Multiplication(&answer, a, b); break; case ‘/‘: isRight = Division(&answer, a, b); break; default: break; }; numStack.push(answer); operStack.pop(); } operStack.pop(); } else if((arith->a[i].arithmeticData.oper==‘*‘|| arith->a[i].arithmeticData.oper== ‘/‘)&& (operStack.top().arithmeticData.oper == ‘+‘ || operStack.top().arithmeticData.oper == ‘-‘)){ operStack.push(arith->a[i]); } else { while (!operStack.empty() && (((arith->a[i].arithmeticData.oper == ‘+‘ || arith->a[i].arithmeticData.oper == ‘-‘) && operStack.top().arithmeticData.oper != ‘(‘) || ((arith->a[i].arithmeticData.oper == ‘*‘ || arith->a[i].arithmeticData.oper == ‘/‘) && (operStack.top().arithmeticData.oper == ‘/‘ || operStack.top().arithmeticData.oper == ‘/‘)) &&isRight)) { b = numStack.top(); numStack.pop(); a = numStack.top(); numStack.pop(); switch (operStack.top().arithmeticData.oper) { case ‘+‘: isRight = Addition(&answer, a, b); break; case ‘-‘: isRight = Subtraction(&answer, a, b); break; case ‘*‘: isRight = Multiplication(&answer, a, b); break; case ‘/‘: isRight = Division(&answer, a, b); break; default: break; }; numStack.push(answer); operStack.pop(); } operStack.push(arith->a[i]); } } while (!operStack.empty()&&isRight) { b = numStack.top(); numStack.pop(); a = numStack.top(); numStack.pop(); switch (operStack.top().arithmeticData.oper) { case ‘+‘: isRight = Addition(&answer, a, b); break; case ‘-‘: isRight = Subtraction(&answer, a, b); break; case ‘*‘: isRight = Multiplication(&answer, a, b); break; case ‘/‘: isRight = Division(&answer, a, b); break; default: break; }; numStack.push(answer); operStack.pop(); } arith->answer = answer; return isRight; }
判断答案是否重复
//判断是否重复**** bool IsNotRepeat(arithmetic arith, arithmeticAtom* repeat,int nownum) { int t = nownum - 1; while(t >= 0){ if(arith.answer.type == repeat[t].type){ //如果当前题目答案的类型与之前的相同 if(arith.answer.type == NUM){ //如果都为整数 if(arith.answer.arithmeticData.num == repeat[t].arithmeticData.num){ return false; } } else if(arith.answer.type == FRACTION){ //如果都为分数 if(arith.answer.arithmeticData.fraction.molecule == repeat[t].arithmeticData.fraction.molecule && arith.answer.arithmeticData.fraction.denominator == repeat[t].arithmeticData.fraction.denominator) return false; } } t--; } //如果答案不重复,则将这个答案插入到答案数组后面 repeat[nownum].type = arith.answer.type; if(repeat[nownum].type == NUM){ repeat[nownum].arithmeticData.num = arith.answer.arithmeticData.num; } else if(repeat[nownum].type == FRACTION){ repeat[nownum].arithmeticData.fraction.molecule = arith.answer.arithmeticData.fraction.molecule; repeat[nownum].arithmeticData.fraction.denominator = arith.answer.arithmeticData.fraction.denominator; } return true; }
文本输出
1 //文件输出Exercises.txt Answers.txt 2 void fileCreate(int i, arithmetic arith, FILE* q, FILE* an) { 3 char string[QUESTIONLENGTH],count[QUESTIONCOUNT],answer[ANSWERLENGTH]; 4 int j=0,k=1; 5 if (q == NULL || an == NULL) 6 return; 7 i++; 8 //形成当前题号 9 AddString(i, count, &j); 10 count[j] = ‘\0‘; 11 12 //输出题目 13 AtomToString(string, arith.a, arith.length); 14 fputs(count, q); 15 fputs(" . ", q); 16 fputs(string, q); 17 fputs("= ", q); 18 fputc(‘\n‘, q); 19 20 //输出答案 21 AtomToString(answer, &(arith.answer), k); 22 fputs(count, an); 23 fputs(" . ", an); 24 fputs(answer, an); 25 fputc(‘\n‘, an); 26 }
主函数
int main(int messageCount ,char *message[]){ srand(time(NULL)); int topnum,numrange; arithmetic a; arithmeticAtom* repeat; //消息处理 if (!MessageProcess(&topnum, &numrange, messageCount, message)) { for (int i = 0; i < messageCount+1; i++) { printf("%s\n", message[i]); } return 0; } //开辟重复判重数组 repeat = (arithmeticAtom*)malloc(sizeof(arithmeticAtom) * topnum); FILE* q, *an; fopen_s(&q, QUESTION, "a"); fopen_s(&an, ANSWER, "a"); for (int i = 0; i < topnum; ) { a.length=RandomArithmetic(a.a, ALENGTH, numrange);//随机生成一个算式 if (Calculation(&a) && IsNotRepeat(a,repeat,i)) {//当该算式可用且不重复 fileCreate(i, a, q, an);//输出算式及答案 i++; } else; } free(repeat); fclose(q); fclose(an); return 0; }
数据结构
1 enum dataType { 2 NUM,OPER,FRACTION 3 }; 4 struct arithmeticAtom { 5 dataType type;//单元类型 6 union { 7 int num;//整数 8 char oper;//运算符或括号 9 struct { 10 int denominator;//分母 11 int molecule;//分子 12 }fraction;//分数 13 }arithmeticData; 14 }; 15 struct arithmetic {//算式 16 arithmeticAtom a[ALENGTH];//算式主体 17 int length;//算式长度 18 arithmeticAtom answer;//算式答案 19 };
六 . 功能测试
生成10000道题目,数值范围为0~99
。。。
经过反复检查,生成的题目和答案都没有问题。
七 .psp
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
60 |
20 |
· Estimate |
· 估计这个任务需要多少时间 |
20 |
15 |
Development |
开发 |
1570 |
1600 |
· Analysis |
· 需求分析 (包括学习新技术) |
20 |
20 |
· Design Spec |
· 生成设计文档 |
30 |
30 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
10 |
20 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
10 |
10 |
· Design |
· 具体设计 |
180 |
20 |
· Coding |
· 具体编码 |
1200 |
1000 |
· Code Review |
· 代码复审 |
20 |
0 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
100 |
500 |
Reporting |
报告 |
100 |
80 |
· Test Report |
· 测试报告 |
60 |
50 |
· Size Measurement |
· 计算工作量 |
10 |
10 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
30 |
20 |
合计 |
|
1730 |
1700 |
本次项目为我们俩个人的首次结对项目,有很多东西都不熟悉,而且两个人熟悉的语言不相同,产生了很多的麻烦。
首先确定大家都会的语言(c语言)为本次项目的语言,然后确定了VScode为本次项目使用的IDE工具,然后再去找了VScode有
什么结对编程使用的插件,找到了live share,使用live share作为结对编程工具。
个人感想:
吴宗东:这次结对项目让我第一次有团队协作的感觉,队友的提醒和想法极大的加快了项目的完工,期待着下一次的团队项目。
林涛:本次有大佬带着非常舒服,因为我只学了c语言的皮毛,所以有很多东西都不知道,再加上live share没有找到有给加入者的错误提示,
写了很多BUG,都有大佬迅速改正。这次结对编程真的学到了很多。
原文:https://www.cnblogs.com/qiuyezhezhi/p/12616532.html