Github项目地址

Arithmetic

PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 40 40
· Estimate · 估计这个任务需要多少时间 40 40
Development 开发
· Analysis · 需求分析 (包括学习新技术) 60 120
· Design Spec · 生成设计文档 39 30
· Design Review · 设计复审 (和同事审核设计文档) 20 50
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 20 20
· Design · 具体设计 60 120
· Coding · 具体编码 500 720
· Code Review · 代码复审 60 60
· Test · 测试(自我测试,修改代码,提交修改) 90 120
Reporting 报告
· Test Report · 测试报告 40 40
· Size Measurement · 计算工作量 20 20
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 60 80
合计 1000 1400

功能要求

题目

实现一个自动生成小学四则运算题目的命令行程序

功能(已全部实现)

  1. 使用 -n 参数控制生成题目的个数。
  2. 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围。
  3. 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1 − e2的子表达式,那么e1 ≥ e2。
  4. 生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数。
  5. 每道题目中出现的运算符个数不超过3个。
  6. 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。
  7. 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt。
  8. 程序应能支持一万道题目的生成。
  9. 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,统计结果输出到文件Grade.txt。

效能分析

参数如下图,n值为100000(即10W道),

观察下图的性能曲线,可以看出大概需要8秒钟时间生成,使用内存峰值大约在150MB左右。

我们再次提高n值上限(1000000 100W道),适当调高r值(1000)。

可以看出大概需要54秒钟生成,使用内存峰值大约在840MB左右。

我们觉得这个运行时间有点过长,试着增大r值,看看题目树生成碰撞导致的效率减慢占比是多少。

我们的r值增大到5000,生成时间就从54秒下降到29秒,有一个很大的改进。

设计实现过程

数值生成

题目提及了三种数字的表示形式,一种是整数(例:3),一种是分数(例:3/5),一种是带分数(例:2’3/5)。这三种数字我们统一使用一个Num类,并提供随机方式生成,然后根据不同部分的值产生不同的导出结果。Num类维护一个int类型的num[3]数组,刚开始被初始化全0。

然后生成逻辑如下:

  1. 获得整数上限r
  2. 对于分母部分获得随机[0,r]内的一个数
  3. 如果分母生成值为0,将分子部分赋给整数部分,此时我们获得了一个全为0的数,结束生成。
  4. 如果分母生成值不为0,对于分子部分获得随机[0,r*r]内的一个数,此时也保证了最后生成的结果绝对不会大于r。
  5. 将分子/分母的值赋给整数部分,剩余部分赋给分子,保证带分数中分子不会大于等于分母。
  6. 如果此时分子生成值为0,将分母也置为0,此时我们获得了一个全为0的数,结束生成。
  7. 对分子和分母进行辗转相除法,获得最简分式。结束生成。

然后在Num类也内建了一个函数,用来返回字符串类型的数值表达式:

  1. 如果分母或分子部分为0,直接返回整数部分。
  2. 如果整数部分为0,因为分子部分也不为0,直接返回一个分式形式。
  3. 否则返回一个带分数形式。

然后封包了对应的加减乘除法,每次计算返回一个新的Num对象,包含计算完毕的结果。

算式生成

生成算式简单,但是要在生成之后判重需要花费心思去思索。

我们采用了二叉树的数据结构来存储整个算式,在非叶子节点存储运算符,叶子节点存储数值。这样,我们就很容易得到一颗符合波兰表达式的二叉树。我们计算和判重只需要对整个二叉树进行一次前缀遍历即可。

结对约定了二叉树的运算符拓展必然是只拓展二叉树的左子树,这样的约定既方便了代码的生成,使得递归生成的实现十分简洁。这样也同时避免了另外一个问题,就是对于加减法或乘除法来说,有个暗含的优先计算减再算加,先计算除再算乘的细节。例如,1+2的外面套一层括号,对整个式子的结果不会有影响,而1-2的外面套一层括号,则会有影响,故要先算减再算加。

我们使用一个运算符从数字映射到字符串加减乘除的常量数组存储在CTree类中,对于每一次生成,有以下方式初始化:

  1. 获得父对象的指针,深度,和整数上限r。
  2. 记录父对象的指针,如果深度为0,对本对象管理下的Num类对象进行初始化构造,本类构造结束。
  3. 如果深度不为0,对于运算符部分获得随机[0,3]内的一个数,对于加减乘除,对左子树迭代传递构造,右子树进行深度为0的传递构造,参考2。

这样我们就可以获得一颗符合上述节点描述的二叉树了。之后是判重部分的逻辑:

维护了三个方法

  • isNumber()用左右子树是否有对象指向来确认该节点是不是数值节点。
  • isRoot()用父对象是否有对象指向来确定该节点是不是根节点。
  • equals()比较树是否相同,暴露供外部调用。

利用上面三个方法和Num的部分函数来进行判重。

  1. 如果节点类型不同,直接认为是不同的。
  2. 如果是数值节点,直接判定Num类中num三个部分是否相同(num已经是最简式)来判定是否重合。
  3. 如果是运算符节点,判定运算符类型是否相同来判定是否重合。

问题集生成

在CTree上再封装一层,我们利用Calculation类来管理二叉树,同时该类也是判重的接口,以及运算符个数的生成者。

问题集的处理,因为在Ctree部分处理重合的算法选择的好,这部分就不需要太费劲。我们直接建立一个HashSet,Calculation类重写了hashCode()函数,返回答案的字符串hashCode,来第一步去重。若答案一样,也就是hashCode一样,HashSet会调用Calculation类的equals退化成链表保存。

设计实现过程

面向对象编程,将文件按功能抽离成类,每个类暴露对应的函数。

代码说明

数值生成逻辑:

    public Num(int r) {
num = new int[3];
num[2] = (int)(Math.random() * r); if(num[2] == 0) {
num[0] = num[1];
num[1] = 0;
} else {
num[1] = (int)(Math.random() * r) * (int)(Math.random() * r) % num[2] * r; num[0] = num[1] / num[2];
num[1] = num[1] % num[2];
if(num[1] == 0) {
num[2] = 0;
} if(num[1] != 0 && num[2] != 0)
{
int gcdNumber = gcd(num[1], num[2]);
num[1] /= gcdNumber;
num[2] /= gcdNumber;
}
}
}

数值返回逻辑:

    public String getFromatNumber() {
String str = new String();
if(num[2] == 0) {
str += String.valueOf(num[0]);
} else {
if(num[1] == 0) {
str += String.valueOf(num[0]);
}
else if(num[0] != 0) {
str += String.valueOf(num[0]) + '\''
+ String.valueOf(num[1]) + '/' + String.valueOf(num[2]);
} else {
str += String.valueOf(num[1]) + '/' + String.valueOf(num[2]);
}
} return str;
}

算式生成逻辑:

    public CTree(CTree parent, int depth, int r) {
this.parent = parent;
if(depth == 0) {
lchild = null;
rchild = null;
num = new Num(r);
} else {
operationType = (int)(Math.random() * 4);
lchild = new CTree(this, depth - 1, r);
rchild = new CTree(this, 0, r);
}
}

算式判重逻辑:

    public boolean equals(CTree s) { //比较树是否相同 ,不区分左右子树位置不同
if(isNumber() != s.isNumber()) {
return false;
}
if(isNumber() == true) {
return num.equals(s.num);
}
if(operationType != s.operationType) {
return false;
} return (lchild.equals(s.lchild) && rchild.equals(s.rchild))
|| (lchild.equals(s.rchild) && rchild.equals(s.lchild));
}

测试运行

生成文件

批改

代码覆盖率

项目小结

此次的结对编程,由陈志海同学负责前期架构搭建,项目测试,而我负责前期编码和博文编写。

我们讨论项目基本构思,在每一个点都是先做好大概设计,每一个类需要有什么方法。还讨论了数值生成如何操作,才能保证在范围内;二叉树存放算式的每一部分该怎么设计,才能尽可能准确高效的生成一个算式;整个计算过程中出现负值的处理等等。

然后陈志海同学把架构交给我,两个协作着,我来实现具体细节,他来在每一步编写完毕后提出问题,解决一些明显的逻辑BUG。我来提供一些算法和结构上的优化。

在项目大体编写完毕后,由陈志海同学进行项目测试,解决一些细节和用户体验方面的问题,同时完成了批改的功能。

我从此次项目中收获到了,两个人的力量,不仅仅体现在编码的速度方面,对于提升代码质量、技术精进方面都有很大的助益。同时明白了团队约定代码规范,思路交流方面是需要多多注意的。

四则运算(Java) 陈志海 邓宇的更多相关文章

  1. 3星|《陈志武金融投资课》:金融改善社会,A股投资策略

    从历史上的金融说起,介绍金融的基本知识.理念.大事.重要人物.也有一些A股投资策略和A股政策点评. 引用了不少学术研究成果做证据.讲历史的部分,功力比专业历史学者稍逊,毕竟这不是作者的专业. 我读后认 ...

  2. 四则运算 Java 杨辉鹏,郑冠华

    四则运算 Java 杨辉鹏,郑冠华 GitHub链接:https://github.com/yanghuipeng/arithmetic 项目相关要求 使用 -n 参数控制生成题目的个数,例如 -n ...

  3. 「一入 Java 深似海 」系列课程

    第一期 「一入 Java 深似海 」系列课程 - 第一期 第一节:Java 语言基础

  4. 陈志生:德国信贷工厂风控模式对P2P的启发

    上海合盘金融信息服务股份有限公司董事长陈志生 和讯银行消息 "2014中国金融论坛"于5月14-15日在北京召开,本次论坛主题为“全面深化金融体制改革与实体经济增长”.和讯网作为指 ...

  5. 四则运算Java语言实验设计过程1

    题目要求: 像二柱子那样,花二十分钟写一个能自动生成三十道小学四则运算题目的 “软件”.要求:除了整数以外,还要支持真分数的四则运算(需要验证结果的正确性).题目避免重复.可定制出题的数量. 设计思路 ...

  6. 四则运算(Java)--温铭淇,付夏阳

    GitHub项目地址: https://github.com/fxyJAVA/Calculation 四则运算项目要求: 程序处理用户需求的模式为: Myapp.exe -n num -r size ...

  7. 四则运算 Java 实现 刘丰璨,王翠鸾

    四则运算 GitHub仓库 功能实现 [x] 使用 -n 参数控制生成题目的个数,并且根据解空间限制用户设定的范围(如 range == 2 时,用户却要求生成 10000 道题目,这明显不合理) [ ...

  8. 四则运算 Java (于泽浩,袁浩越)

    GitHub 地址 一. 项目要求 题目 实现一个自动生成小学四则运算题目的命令行程序. 需求(全部完成) 使用 -n 参数控制生成题目的个数 Myapp.exe -n 10 使用 -r 参数控制题目 ...

  9. 结对编程四则运算--JAVA实现(徐静、林文敏)

    Github项目地址 项目相关要求 -n 参数控制生成题目的个数 (√) Myapp.exe -n 10 // 将生成10个题目 -r 参数控制题目中数值(自然数.真分数和真分数分母)的范围 (√) ...

随机推荐

  1. 把OnDraw和OnPaint弄清楚(转贴)

    OnDraw()和OnPaint()兄弟 经常有朋友问雷神这样的问题:我在视图画的图象或者文字,当窗口改变后为什么不见了?OnDraw()和OnPaint()两个都是解决上面的问题,有什么不同? 雷神 ...

  2. linux基础(3)

    一 正文处理命令及tar命令 使用cat命令进行文件的纵向合并 两种文件的纵向合并方法 : tar命令的功能 :   掌握tar命令的功能:将多个文件(也可能包括目录,因为目录本身也是文件)放在一起存 ...

  3. DHCP(二)

    提供阶段:即DHCP服务器向DHCP客户端提供预分配IP地址的阶段.网络中的所有DHCP服务器接收到客户端的DHCP Discover报文后,都会根据自己地址池中IP地址分配的优先次序选出一个IP地址 ...

  4. Windows10 官方原版镜像下载途径 Label:win10解决方案

    https://www.microsoft.com/en-gb/software-download/windows10ISO 设置浏览标签为手机以避免跳转,下载即可  或者手机打开该网址,获取下载链接 ...

  5. 洛谷 P3302 [SDOI2013]森林 Lebal:主席树 + 启发式合并 + LCA

    题目描述 小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值.初始的时候,森林中有M条边. 小Z希望执行T个操作,操作有两类: Q x y k查询点x到点y路径上所有的权值中,第k小的权 ...

  6. redis实现消息发布/订阅

    redis实现简单的消息发布/订阅模式. 消息订阅者: package org.common.component; import org.slf4j.Logger; import org.slf4j. ...

  7. S3C2440开发环境搭建(Ubuntu)

    我的操作系统是 Ubuntu 1404.可以使用下面的命令查看系统的版本: cat /etc/issue 安装.配置.启动 ftp 服务 sudo apt-get install vsftpd  修改 ...

  8. springmvc防止表单重复提交demo

    原理:在去某个页面直接生成一个随机数(这里使用的是UUID)并放入session中,用户提交表单时将这个随机数传入服务端与session中的值进行比较,如果不不存在或不相等,则认为是重复提交:如果相等 ...

  9. UseSubmitBehavior="false" 防止页面重复提交bug

    OnClientClick="this.disabled=true;" UseSubmitBehavior="false" 注: 1.当设置UseSubmitB ...

  10. Three.js加载gltf模型

    效果图 demo import './index.css'; var stats; stats = new Stats(); document.body.appendChild( stats.dom ...