结对项目——四则运算题目生成器

一、GitHub项目地址 :

https://github.com/OurPrograming/Exercises

结对项目成员:软三 陈纪禧(3118005043)、软三 程伟杰(3118005045)

二、PSP预计时间:


PSP2.1


Personal Software Process Stages


预估耗时(分钟)


实际耗时(分钟)


Planning


计划

 60

· Estimate


· 估计这个任务需要多少时间


60


Development


开发


1680


· Analysis


· 需求分析 (包括学习新技术)


120


· Design Spec


· 生成设计文档


30


· Design Review


· 设计复审 (和同事审核设计文档)


10


· Coding Standard


· 代码规范 (为目前的开发制定合适的规范)


20


· Design


· 具体设计


120


· Coding


· 具体编码


1200


· Code Review


· 代码复审


60


· Test


· 测试(自我测试,修改代码,提交修改)


120


Reporting


报告


150


· Test Report


· 测试报告


60


· Size Measurement


· 计算工作量


30


· Postmortem & Process Improvement Plan


· 事后总结, 并提出过程改进计划


60


合计


1890

三、效能分析:

分析:由效能分析易知,判重函数占用程序时间最多,后续优化可从这方面入手。

四、设计实现过程:

结构设计:

设计6个类,不同类实现不同功能,说明如下:

Parameter  //处理命令行中传来的参数

Generate    //生成表达式

Statistics    //统计类,对于给定题目答案的对错进行统计

BinaryTree //二叉树类

Calculator  //计算类

Fraction     //分数类,所有运算数都是此类型

类与函数的调用关系图:(注:调用→被调用)

五、部分关键代码设计与说明:

Parameter  //处理命令行中传来的参数

表达式查重:

 1 void Parameter::generateExercises(int num, int range)
 2 {
 3     if (num == 0 || range == 0)
 4         return;
 5
 6     //打开文件
 7     exercises.open("Exercises.txt", std::ios::out | std::ios::trunc);
 8     answers.open("Answers.txt", std::ios::out | std::ios::trunc);
 9     if (!exercises.is_open() || !answers.is_open())
10         return;
11
12     //每生成一个表达式都与其他元素比较,不重复就入组
13     vector<BinaryTreeNode *> exe;            //用于存放表达式树
14     exe.reserve(num);                        //预留空间避免过多扩容
15
16     vector<BinaryTreeNode *>::iterator ite;
17     Generate *generate = generate = new Generate(range);
18     BinaryTree *tree = nullptr;
19     bool flag = false;                    //标志是否重复
20
21     for (int i = 0; i < num; i++)            //生成num个
22     {
23         do
24         {
25             tree = generate->genExercise();
26             for (ite = exe.begin(); ite != exe.end(); ite++)
27             {
28                 flag = BinaryTree::compare(tree->pRoot, *ite);
29                 if (flag == true)        //匹配到重复就跳出判断,避免标志被更改
30                 {
31                     //重复就删除生成的树
32                     delete tree;
33                     tree = nullptr;
34                     break;
35                 }
36             }
37         } while (flag == true);
38
39         if (flag == false)        //未重复就push
40         {
41             exe.push_back(tree->pRoot);
42         }
43     }
44
45     for (int i = 0; i < (int)exe.size(); i++)
46     {
47         string strExercises = generate->getExercise(exe.at(i));
48         exercises << i + 1 << "." << strExercises.c_str() << endl;
49
50         //转成计算器计算结果避免出错
51         BinaryTreeNode *result = Calculator::toTree(Calculator::toReversePolish(strExercises));
52         string strAnswers = Calculator::calcResult(result).display();
53         answers << i + 1 << "." << strAnswers.c_str() << endl;
54     }
55
56     cout << "题目与答案已写入到文件" << endl;
57
58     delete generate;
59     //关闭文件
60     exercises.close();
61     answers.close();
62 }

Generate类    //生成表达式

表达式建树:

 1 BinaryTree * Generate::genExercise()
 2 {
 3     //3 --> 2 --> 2
 4     //     -> 3 --> 1
 5
 6     //运算符个数
 7     std::uniform_int_distribution<int> opeCount(1, 3);
 8     int opeNum = opeCount(mt);
 9
10     //先新建出一颗最小树
11     BinaryTreeNode *left = new BinaryTreeNode(getFraction().display());
12     BinaryTreeNode *right = new BinaryTreeNode(getFraction().display());
13     string *data = new string(1, getOperator());
14     BinaryTreeNode *root = new BinaryTreeNode(*data, left, right);
15     standardization(root);
16     opeNum--;
17
18     if (opeNum == 0)        //如果运算符只有一个直接返回
19     {
20         BinaryTree *tree = new BinaryTree(root);
21         return tree;
22     }
23
24     //用于二选一的随机数
25     std::uniform_int_distribution<int> randBool(0, 1);
26
27     //选择下一步是2或3
28     if (randBool(mt) == 0 && opeNum == 2)    //再生成一个最小树并连接
29     {
30         left = new BinaryTreeNode(getFraction().display());
31         right = new BinaryTreeNode(getFraction().display());
32         data = new string(1, getOperator());
33         BinaryTreeNode *tempRoot = new BinaryTreeNode(*data, left, right);
34         standardization(tempRoot);
35         opeNum--;
36
37         //选择左右顺序
38         if (randBool(mt) == 0)    //新树在左
39         {
40             left = tempRoot;
41             right = root;
42         }
43         else
44         {
45             left = root;
46             right = tempRoot;
47         }
48         //连接
49         data = new string(1, getOperator());
50         root = new BinaryTreeNode(*data, left, right);
51         standardization(root);
52         opeNum--;
53
54         BinaryTree *tree = new BinaryTree(root);
55         return tree;
56     }
57     else        //往上插
58     {
59         do
60         {
61             //二选一 左上or右上
62             if (randBool(mt) == 0)    //左上
63             {
64                 left = new BinaryTreeNode(getFraction().display());
65                 right = root;
66             }
67             else            //右上
68             {
69                 left = root;
70                 right = new BinaryTreeNode(getFraction().display());
71             }
72             data = new string(1, getOperator());
73             root = new BinaryTreeNode(*data, left, right);
74             standardization(root);
75             opeNum--;
76         } while (opeNum > 0);
77
78         BinaryTree *tree = new BinaryTree(root);
79         return tree;
80     }
81     return nullptr;
82 }

表达式加括号:

 1 bool Generate::toString(BinaryTreeNode * root, string &strExercise)
 2 {
 3     if (root == nullptr)
 4     {
 5         return true;
 6     }
 7
 8     bool lChildBracket = false, rChildBracket = false;            //标志括号
 9     int leftPriority = 0, curPriority = 0, rightPriority = 0;    //优先级
10
11     //中序遍历
12     if (root->leftChild != nullptr)
13     {
14         //左子树是操作符加括号
15         if (Calculator::isOperator(root->leftChild->data.at(0)))
16         {
17             //得到优先级
18             leftPriority = Calculator::getPriority(root->leftChild->data.at(0));
19             curPriority = Calculator::getPriority(root->data.at(0));
20             if (leftPriority == 0 || curPriority == 0)
21             {
22                 return false;
23             }
24             if (curPriority > leftPriority)        //优先级大加括号
25             {
26                 strExercise = strExercise +  "(";
27                 lChildBracket = true;
28             }
29         }
30         //遍历左子树
31         toString(root->leftChild, strExercise);
32         //补上括号
33         if (lChildBracket == true)
34         {
35             strExercise = strExercise + ")";
36             lChildBracket = false;
37         }
38     }
39
40     //中
41     if (Calculator::isOperator(root->data.at(0)))
42         strExercise = strExercise + " " + root->data + " ";
43     else
44         strExercise = strExercise + root->data;
45
46     //右
47     if (root->rightChild != nullptr)
48     {
49         //右子树是操作符加括号
50         if (Calculator::isOperator(root->rightChild->data.at(0)))
51         {
52             //得到优先级
53             rightPriority = Calculator::getPriority(root->rightChild->data.at(0));
54             curPriority = Calculator::getPriority(root->data.at(0));
55             if (rightPriority == 0 || curPriority == 0)
56             {
57                 return false;
58             }
59             if (curPriority > rightPriority)        //优先级大加括号
60             {
61                 strExercise = strExercise + "(";
62                 rChildBracket = true;
63             }
64         }
65         //遍历右子树
66         toString(root->rightChild, strExercise);
67         //补上括号
68         if (rChildBracket == true)
69         {
70             strExercise = strExercise + ")";
71             rChildBracket = false;
72         }
73     }
74     return true;
75 }

Calculator  //计算类

传入中缀表达式转为逆波兰式:

 1 string Calculator::toReversePolish(string expression)  //传入中缀表达式转为逆波兰式
 2 {
 3     std::stack<string> S1;  //运算符栈
 4     std::stack<string> S2;  //逆波兰式栈
 5     string *ope;     //运算符,分数
 6     string *exp = new string;  //逆波兰式
 7     string *leftbracket = new string(1, ‘(‘);    //左括号
 8
 9     for (int i = 0; i < expression.length(); i++)  //从expression中逐个取出字符
10     {
11         if (isOperator(expression.at(i)))  //若取出运算符
12         {
13             if (S1.empty() || expression.at(i) == ‘(‘)
14             {
15                 ope = new string(1, expression.at(i));
16                 S1.push(*ope);
17             }
18             else if(expression.at(i) == ‘)‘)
19             {
20                 ope = new string;
21                 *ope = S1.top();   S1.pop();
22                 while ((*ope).at(0) != ‘(‘)  //取出‘(‘后停止循环,并抛弃‘(‘
23                 {
24                     if ((*ope).at(0) != ‘(‘)
25                     {
26                         S2.push(*ope);
27                     }
28                     ope = new string;
29                     *ope = S1.top();   S1.pop();
30                 }
31                 i++;
32             }
33             else if (getPriority(expression.at(i)) > getPriority(S1.top().at(0)))  //比较优先级
34             {
35                 ope = new string(1, expression.at(i));
36                 S1.push(*ope);  //该运算符优先级比栈顶运算符优先级高,直接入栈
37             }
38             else
39             {
40                 if (!S1.empty())
41                 {
42                     while (getPriority(expression.at(i)) <= getPriority(S1.top().at(0)))
43                     {   //从运算符栈S1逐个取出运算符并压入表达式栈S2,直到该运算符优先级比栈顶运算符优先级高
44                         ope = new string;
45                         *ope = S1.top();   S1.pop();
46                         S2.push(*ope);
47                         if (S1.empty()) break;
48                     }
49                 }
50                 ope = new string(1, expression.at(i));
51                 S1.push(*ope);   //完成操作后该运算符优先级比栈顶运算符优先级高,入运算符栈S1
52             }
53         }
54         else
55         {
56             if (expression.at(i) == ‘ ‘)  i++;  //隔离第一个空格
57             string fat;
58             while (expression.at(i) != ‘ ‘ && expression.at(i) != ‘(‘ && expression.at(i) != ‘)‘)
59             {   //保存分数字符串
60                 fat.push_back(expression.at(i));
61                 i++;
62                 if (i >= expression.length()) break;
63             }
64             if (!fat.empty())
65             {
66                 S2.push(fat);    //生成分数字符串,并压入表达式栈S2
67             }
68             if (i < expression.length())
69             {
70                 if (expression.at(i) == ‘(‘ || expression.at(i) == ‘)‘)  i--;
71             }
72         }
73     }
74     while (!S2.empty())   //将表达式栈逆序
75     {
76         string op;
77         op = S2.top();   S2.pop();
78         S1.push(op);
79     }
80     exp = new string;
81     while(!S1.empty())  //组装逆波兰式
82     {
83         string str = S1.top();  S1.pop();
84         if (!S1.empty())
85         {
86             *exp = *exp + str + ‘ ‘;
87         }
88         else
89         {
90             *exp = *exp + str;
91         }
92     }
93     return *exp;  //返回逆波兰式
94 }

逆波兰式转为树:

 1 BinaryTreeNode * Calculator::toTree(string exp)  //逆波兰式转为树
 2 {
 3     std::stack<BinaryTreeNode*> num;    //数字栈
 4     BinaryTreeNode *exp_ptr = nullptr;  //结点指针
 5     BinaryTreeNode *right = nullptr;    //右孩子指针
 6     BinaryTreeNode *left = nullptr;     //左孩子指针
 7     string *data_exp;         //结点数据
 8
 9     for (int i = 0; i < exp.length(); i++)  //逐个字符读取逆波兰式
10     {
11         if (isOperator(exp.at(i)))     //读取到运算符
12         {
13             right = num.top();  num.pop();
14             left = num.top();  num.pop();
15             data_exp = new string(1,exp.at(i));     //将运算符转化为string类型,并赋给结点数据data
16
17             BinaryTreeNode *exp_root = new BinaryTreeNode(*data_exp, left, right);  //构建新树
18             num.push(exp_root);
19         }
20         else
21         {   //读取到数字,生成完整分数字符串,并压入数字栈num
22             if (exp.at(i) == ‘ ‘)   i++;   //隔离第一个空格
23             if (!isOperator(exp.at(i)))
24             {
25                 string fat;
26                 while (exp.at(i) != ‘ ‘)
27                 {   //保存分数字符串
28                     fat.push_back(exp.at(i));
29                     i++;
30                     if (i >= exp.length()) break;
31                 }
32                 exp_ptr = new BinaryTreeNode;
33                 if (!fat.empty())
34                 {
35                     exp_ptr->data = fat;
36                     num.push(exp_ptr);
37                 }
38             }
39             else
40             {
41                 i--;
42             }
43         }
44     }
45     exp_ptr = num.top();  num.pop();  //从栈顶取出建好的树的根结点
46     return exp_ptr;   //返回树的根结点
47 }

六、测试运行:

1.生成一万道题目与答案:

2.批改一万道题目:

3.修改1,4,6题的答案再次批改:

     

七、完整PSP:


PSP2.1


Personal Software Process Stages


预估耗时(分钟)


实际耗时(分钟)


Planning


计划

 60
90


· Estimate


· 估计这个任务需要多少时间


60


90


Development


开发


1680


1600


· Analysis


· 需求分析 (包括学习新技术)


120


160


· Design Spec


· 生成设计文档


30


30


· Design Review


· 设计复审 (和同事审核设计文档)


10


10


· Coding Standard


· 代码规范 (为目前的开发制定合适的规范)


20


20


· Design


· 具体设计


120


120


· Coding


· 具体编码


1200


1080


· Code Review


· 代码复审


60


60


· Test


· 测试(自我测试,修改代码,提交修改)


120


120


Reporting


报告


150


120


· Test Report


· 测试报告


60


30


· Size Measurement


· 计算工作量


30


30


· Postmortem & Process Improvement Plan


· 事后总结, 并提出过程改进计划


60


60


合计


1890


1810

八、项目小结:

开发初:

本来我们想使用VS上的插件live share进行结对项目的开发,但后面发现这个插件不符合我们的想法,后转用Github团队仓库进行开发。

遇到的一大困难是对Github团队仓库的使用,仔细研究之后才初步学会使用,了解了分支、更改、同步、拉取、推送等功能,解决难题后也算是一大收获。

项目本身:

由于我们两人编程水平相差较大,纪禧很强而伟杰只有c语言基础,开发过程比较坎坷。后来也是通过不断交流,不断翻阅博客,终于实现了这个项目。

优点:前期结构框架定得很好,所以后续实现和改动比较方便。

难点:中缀表达式转逆波兰式建树的过程中需要考虑的因素很多,对各种情况的判断及解决比较复杂,刚写好时总是出现越界问题,后用断点不断调试,终于改好。

结对感受:

合作比单人更有动力,交流更多,犯错更少,学到的东西也更多,更迅速。

原文地址:https://www.cnblogs.com/clsgghost/p/12605005.html

时间: 2024-08-05 08:18:17

结对项目——四则运算题目生成器的相关文章

个人项目---四则运算题目生成器项目记录

本次作业是设计并实现一个四则运算题目随机生成器(支持分数运算),其功能包括生成题目文件,并自动生成对应的答案文件,以及根据输入的题目文件,对输入的答案文件进行批改打分. 时间表格 PSP2.1 Personal Software Process Stages Time Planning 计划 · Estimate · 估计这个任务需要多少时间 24h Development 开发 · Analysis · 需求分析 (包括学习新技术) 2h · Design Spec · 生成设计文档 2h ·

结对项目--四则运算生成器(Java) 刘彦享+龙俊健

结对项目--四则运算生成器(Java) GitHub地址 合作者 刘彦享(3118005010) 龙俊健(3118005011) https://github.com/LYX708194/calculate 一.项目简介 说明 自然数:0, 1, 2, -. 真分数:1/2, 1/3, 2/3, 1/4, 1'1/2, -. 运算符:+, ?, ×, ÷. 括号:(, ). 等号:=. 分隔符:空格(用于四则运算符和等号前后). 算术表达式: e = n | e1 + e2 | e1 ? e2

结对编程1 —— 基于GUI和Swing的四则运算题目生成器

结对编程1 -- 基于GUI和Swing的四则运算题目生成器 合作伙伴 201421123089 周迪 201421123069 黄睿 代码地址:https://git.coding.net/H1159650478/sizeyunsuanjiaqiang.git 题目描述 我们在个人作业1中,用各种语言实现了一个命令行的四则运算小程序.进一步,本次要求把这个程序做成GUI(可以是Windows PC 上的,也可以是Mac.Linux,web,手机上的),成为一个有基本功能.一定价值的程序.在下面

结对项目:四则运算题目生成器(JAVA+GUI)

结对项目(JAVA+GUI) 结对伙伴: 胡梓泽(3118005002) 黄伟洪(3118005004) Github链接:https://github.com/Hubbard1999/operation 1 项目简介 1.1 题目描述 1.1.1 题目:实现一个自动生成小学四则运算题目的命令行程序(也可以用图像界面,具有相似功能). 1.1.2 说明: 真分数:1/2, 1/3, 2/3, 1/4, 1'1/2, -. 自然数:0, 1, 2, -. 运算符:+, ?, ×, ÷. 括号:(,

结对项目:四则运算题目生成器(Java)

目录 一.需求分析 二.开发计划 三.实现方案 3.1 项目结构 3.2 代码说明 3.2.1 出题功能代码 3.2.3 批卷功能代码 3.2.3 四则运算功能代码 四.效能分析 4.1 程序效能 4.2 性能优化 4.2.1 优化前:(执行过程消耗时间为:74s) 4.2.2 查重算法优化后:(执行过程消耗时间为:11s) 4.2.3 多线程并发优化后(执行过程消耗时间为:1s) 五.测试报告 5.1 测试项:生成题目和答案是否符合要求 5.2 测试项:批卷功能是否正确判断答案正确与否 5.3

四则运算题目生成器——项目笔记

这个程序,能够实现生成彼此不重复的题目,并且计算出相应答案答案,以及根据题目计算结果的功能. 预估时间及实际花费时间表格 设计以及调试花费了大量的时间. PSP2.1 Personal Software Process Stages Time Planning 计划   · Estimate · 估计这个任务需要多少时间 20h Development 开发   · Analysis · 需求分析 (包括学习新技术) 5h · Design Spec · 生成设计文档 1h · Design R

结对项目——四则运算(GUI)

目录 1.仓库地址 2.开始前PSP展示 3.接口的设计 4.计算模块接口的设计与实现过程 5.计算模块接口部分的性能改进 6.计算模块部分单元测试展示 7.计算模块部分异常处理说明 8.界面模块的详细设计过程 9.界面模块与计算模块的对接 10.结对过程 11.结对编程优缺点 12.实际的PSP 1. 仓库地址:https://git.coding.net/jiapwy/newfouroperation.git 队友:胡雅馨 队友的博客地址:http://www.cnblogs.com/huy

个人项目——四则运算题目的随机生成

任务:实现一个自动生成小学四则运算题目的命令行程序. 一.时间预估及实际花费时间 PSP2.1 Personal Software Process Stages Time Planning 计划 · Estimate · 估计这个任务需要多少时间 15h Development 开发 · Analysis · 需求分析 (包括学习新技术) 2h · Design Spec · 生成设计文档 0.5h · Design Review · 设计复审 (和同事审核设计文档) 0.5h · Coding

个人项目——四则运算题目生成程序

任务:实现一个自动生成小学四则运算题目的命令行程序. 1)PSP表格记录 PSP2.1 Personal Software Process Stages 估计 实际 Planning 计划 · Estimate · 估计这个任务需要多少时间 21h 29h Development 开发 · Analysis · 需求分析 (包括学习新技术) 1h 3h · Design Spec · 生成设计文档 0.5h 0.5 · Design Review · 设计复审 (和同事审核设计文档) 0 0 ·