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. fineUI表格控件各属性说明

    最近在新的公司后台用fineUI,所以也稍微总结一下表格的一些属性,希望对刚入手的人有些帮助: AllowPaging 允许分页,为true时表示允许分页,表格下方会出现分页工具栏: PageSize ...

  2. 【转】Jmeter Http请求界面解释

    一个HTTP请求有着许多的配置参数,下面将详细介绍: 名称:本属性用于标识一个取样器,建议使用一个有意义的名称. 注释:对于测试没有任何作用,仅用户记录用户可读的注释信息. 服务器名称或IP :HTT ...

  3. 有趣的java小项目------猜拳游戏

    package com.aaa; //总结:猜拳游戏主要掌握3个方面:1.人出的动作是从键盘输入的(System.in)2.电脑是随机出的(Random随机数)3.双方都要出(条件判断) import ...

  4. 普及组2008NOIP 排座椅(贪心+排序)

    排座椅 时间限制: 1 Sec  内存限制: 50 MB提交: 4  解决: 3[提交][状态][讨论版][命题人:外部导入] 题目描述 上课的时候总有一些同学和前后左右的人交头接耳,这是令小学班主任 ...

  5. AngularJS:Select

    ylbtech-AngularJS:Select 1.返回顶部 1. AngularJS Select(选择框) AngularJS 可以使用数组或对象创建一个下拉列表选项. 使用 ng-option ...

  6. 1024 Palindromic Number

    题意: 给出一个数N(N<=10^10),最多可操作K次(K<=100),每次操作为这个数和其反转之后的数相加,若得到的结果为回文数,则输出:若在K次迭代后仍然不是回文数,在输出第K次操作 ...

  7. zabbix 在linux上安装以及一些配置

    本文章将演示zabbix 3.2版本的安装,供有需要的伙伴们参考: 网络也有很多关于zabbix的安装文档,甚至每一步的配置都有详细的截图,我这里就不演示截图了,多配置几次自然就熟练了.多折腾. 楼主 ...

  8. 第八章 JVM内存管理

    8.1 物理内存与虚拟内存 地址总线(连接处理器和RAM或处理器和寄存器的)的宽度影响了物理地址的索引范围,决定了处理器一次可以从寄存器或内存中获取多少个bit.同时决定了处理器最大的寻址空间,32位 ...

  9. xp远程桌面连接最大用户数怎么设置?

    1.首先到网上去百度下载“补丁UniversalTermsrvPatch”,这个补丁主要目的是在于去除“单用户登陆的限制”,允许多人多用户同时并行访问登录;2.然后根据自己的系统运行对应的程序:系统是 ...

  10. classmethod VS staticmethod