元宝酱拯救地球 2018-03-25
零.coding链接
https://git.coding.net/hiawei/homework1.git
一.需求分析
程序为四则运算生成器
用户为小学生
1.功能需求:
输入参数N,随机产生N道加减乘除,数字为0到100之间,符号有3到5个
学号与生成的n道练习题及其对应的正确答案输出到文件“result.txt”中,不要输出额外信息,文件目录与程序目录一致
2.开发过程需求:
语言为JAVA,提交日期为3.25,记录PSP开发流程,使用jdk8u161版本
3.服务质量需求:
用户为小学生,每个练习题至少要包含2种运算符,运算过程中不得出现负数与小数
不能产生重复算式
除法要简易正常不应过多出现除数为1
运行耗时不应过长
二.功能设计
基本功能:生成四则运算(无括号)并计算答案
扩展功能:生成四则运算(有括号)并计算答案、分数的加减法生成与运算
三.设计实现
1.基本功能:
初始想法:
实际设计:
编程思路:
生成算式使用整数数组存操作数、字符数组存操作符,数字为4到6个,随机生成时保证除法合法化,求值过程未使用逆波兰算法,直接操作并标记负数情况,储存运算结果以防止重复,输出符合要求的表达式
函数设计:
输出生成的算式:voidprtFormula(int n) //n个算式
生成算式字符串:String creatFormula()
运算符生成:void creatOperator(int i)//在第i位字符数组储存运算符
随机生成运算符:char getOperator()
求最大公因数:int gcd(int m,int n)
表达式求值:int calculate(String ss)
2.扩展功能(暂未实现):
带括号表达式:使用中缀转后缀可得出计算结果,由于原先未考虑到扩展功能,随机生成与合法性检测未抽象出来,使得扩展功能难以实现
分数加减:我的思路为先字符处理,将分子分母分别存在两个数组,求出所有分母的最小公倍数,将两个数组“通分”,计算结果后分子分母除去最大公约数。时间不足,未完成
四.算法详解与代码展示
1.表达式求值:
由于无括号,使用两个变量res,save;res保存当前表达式的值不管后面的运算;save是为后面优先运算保存的预留过程数。
res:
当前表达式的值
save:
做完1+2更新save为2,2-1更新save为-1
做玩1*2更新save为3,6/3更新save为2,1*2*6/3更新save为1*2*6/3
代码
public static int calculate(String ss) { int res = 0, save = 0, n = 0; char s[] = ss.toCharArray(); char sign = '+';//初始符号为+ for (int i = 0; i < s.length; ++i) { if (s[i] >= '0') { n = n * 10 + s[i] - '0';//读入两位数或一位数 if (i == s.length - 1 || s[i + 1] < '0') { if (sign == '+') { if (res < 0)//此类判断标记了负数情况 ok = false; res += n; save = n; } else if (sign == '-') { if (res < 0) ok = false; res -= n; save = -1 * n; } else if (sign == '*') { res = res - save + save * n; if (res < 0) ok = false; save = save * n; } else if (sign == '/') { res = res - save + save / n; if (res < 0) ok = false; save = save / n; } } } else { n = 0; sign = s[i]; } } return res; }
2.查重:
为提高运行效率,开辟了长度100000000的数组rec记录得数,为防止溢出同时降低小学生计算难度程序避免了连乘的发生,储存方式例如得数为3,检查rec[3]是否为1,如果不为1,标记为1,如果为1就认为其极有可能重复,排除这个算式
3.保证整除:
生成算式我使用整数数组存数字字符数组存操作符,随机产生除法后,先把两个数字中大的排在前面,再将两数的最大公因数作为除数,若是除数为1,则打上标记,以便后续抛弃这个运算
代码:
if (operas[i] == '/') {// 除法处理为大数除以两数最大公约数 if (nums[i + 1] == 0) nums[i + 1] = 2;// 避免除数为零 if (nums[i] < nums[i + 1]) { int t = nums[i]; nums[i] = nums[i + 1]; nums[i + 1] = t; } nums[i + 1] = gcd(nums[i], nums[i + 1]); if (nums[i + 1] == 1) { ok = false; } }
4.运算符不重复:
使用字符数组储存的运算符直接检测一遍以达到目的
五.测试运行
测试:
六.总结
1.java知识薄弱,第一次用java写如此复杂的程序。开发过程中,java的语法例如字符串字符数组间的处理必须结合资料才能完成。其中java main的运行方式直到阅读了别人的博客才学会了处理。
2.不熟悉开发流程。感觉自己还是不能按照原先的计划来编辑程序。很多原来的构思都在编程的过程中丢掉了,一边开发,一边思考新的方法,逐渐地,原先觉得比较独立抽象的函数等到作业完成的差不多时,发现好多功能都没有独立出来,给后续的扩展功能的开发造成了阻碍,更麻烦的是,独立性较低,给debug造成了极大的困难。
3.测试真的好难,自己明明感觉逻辑是对的,可偏偏就是不按照自己所想的运行。测试函数自己只会用笨方法在另一个java文件修改这个函数再测试。
4.自己感觉自己的程序还有很多不足,没有把乘法结果限制在100以内,重复出现的数字有时很奇怪。自己直接排除了像88/88、65/65、0+23这种式子,感觉程序还有很多优化的空间
展示PSP
PSP2.1 | 任务内容 | 计划共完成需要的时间(min) | 实际完成需要的时间(min) |
Planning | 计划 | 15 | 10 |
Estimate | 估计这个任务需要多少时间,并规划大致工作步骤 | 15 | 10 |
Development | 开发 | 325 | 1150 |
Analysis | 需求分析 (包括学习新技术) | 15 | 10 |
Design Spec | 生成设计文档 | 10 | 5 |
Design Review | 设计复审(和同事审核设计文档) | ||
CodingStandard | 代码规范(为目前的开发制定合适的规范) | 10 | 5 |
Design | 具体设计 | 20 | 30 |
Coding | 具体编码 | 120 | 480 |
Code Review | 代码复审 | 30 | 60 |
Test | 测试(自我测试,修改代码,提交修改) | 120 | 560 |
Reporting | 报告 | 80 | 85 |
Test Report | 测试报告 | 5 | 5 |
Size Measurement | 计算工作量 | 5 | 5 |
Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 60 | 75 |