二、PSP表格
PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning
计划
30
25
· Estimate
· 估计这个任务需要多少时间
30
25
Development
开发
1300
1410
· Analysis
· 需求分析
150
120
· Design Spec
· 生成设计文档
60
70
· Design Review
· 设计复审
30
50
· Coding Standard
· 代码规范
30
20
· Design
· 具体设计
120
100
· Coding
· 具体编码
800
900
· Code Review
· 代码复审
60
80
· Test
· 测试(自我测试,修改代码,提交修改)
50
70
Reporting
报告
105
140
· Test Report
· 测试报告
50
70
· Size Measurement
· 计算工作量
20
30
· Postmortem & Process Improvement Plan
· 事后总结, 并提出过程改进计划
35
40
合计
1435
1575
三、效能分析
这里能看出代码各个模块占用资源的情况,由于没有保存优化前的效能,故只有最后版本的数据,为了效能的提高,我们后来改用了多线程并发的方式进行编写。
四、设计实现过程
在阅读完题目后,我们分析题目的难点是对生成算式的运算以及查重,围绕着这两个问题,程序的实现方式成了核心的问题,我们设想了两种方案,一种是用字符串表来存储算式,再建表来对其进行解析,一种是用树的结构来存储算式。而经过讨论后我们决定用前者来完成项目的开发。
关于项目的每个模块,我们会用到不同的数据结构去处理,比如字符串用以做输入输出的中介结构,而在具体的运算过程中,我们会把字符串转换成hash map、map或者是list的结构,而在运算的过程中,还使用到了栈的结构,对算式进行分级的拆解运算。
为了使程序的效能得到提高,在算式生成的模块里,我们运用了java并发编程的思想,利用future类来进行多线程编程。
而查重功能的实现,是通过把算式的运算过程存放在过程类里(Process),再在运算结果的过程中比对各步过程,排除相同的过程,从而达到避免重复的目的。
以上为项目的结构,其中
Main为主类
Equation是等式类,它用来处理单个算式,通过调用Process类来得到答案
Formula是算式类,它用来存放生成的算式,并且包含一些对算式进行格式转换的方法
Fraction是分数类,它定义了分数这一数字形式的规则
Process是过程类,它用以计算算式的结果,记录运算的过程
CalculateService是计算大类,包含了对所有生成算式进行计算的方法,涉及到了查重的功能
Calculation是计算类,是对单个算式进行计算的具体方法
FormulaFactory是生成类,用以随机生成单个具体的算式
GenerateService是生成大类,包含按需生成所需的算式的方法
JudgeService是判断类,判断用户文本中的算式是否正确
Constants类存放常量
FileUtil类存放文件操作的方法
FractionUtil类存放分数运算的方法
StringUtil类存放对用户输入信息的处理方法
调用图示如下
五、 部分代码说明
1.CalculateService 计算大类
public classCalculateService {private FormulaFactory formulaFactory = newFormulaFactory(Constants.max);public static ExecutorService es =GenerateService.es;public List calculateOfList (Listformulas) {
List equations = new ArrayList<>(formulas.size());
List> futures = new ArrayList<>(formulas.size());for(String formula : formulas) {
Future future = es.submit(newCalculation(formula));
futures.add(future);
}
Equation equation;
Futurefuture;for (int i = 0; i < futures.size(); i++) {
future=futures.get(i);try{
equation=future.get();if (equation != null && !equations.contains(equation)) {
equations.add(equation);
}else{
Formula formula= null;while (equation == null ||equations.contains(equation)) {
formula=formulaFactory.call();
equation= newCalculation(formula.toString()).call();
}
Collections.replaceAll(formulas,formulas.get(i),formula.toString());
equations.add(equation);
}
}catch (InterruptedException |ExecutionException e) {
e.printStackTrace();
}
}
List resultList = new ArrayList<>(equations.size());for(Equation e : equations) {
List processes =e.getProcesses();
resultList.add(processes.get(processes.size()- 1).getAnswer());
}returnresultList;
}
}
该类包含了算式运算的总体方法,负责运算所有的算式,其中通过判断计算过程的比对来避免重复的算式,若有重复则重新生成,以保证没有重复。
2.Calculation 计算类
public class Calculation implements Callable{privateString formula;publicCalculation(String formula) {this.formula =formula;
}
@OverridepublicEquation call() {
String[] elements= formula.split(" ");if (formula.contains("(")) {
List processList = new ArrayList<>();
Stack calculateStack = new Stack<>();
Equation equation= newEquation(formula,processList);
Equation equation1;
String s;
List strings = new ArrayList<>();for(String element : elements) {if (element.endsWith(")")) {
strings.add(" " +element);while (!(s = calculateStack.pop()).startsWith("(")){
strings.add(" " +s);
}
strings.add(s);
StringBuilder stringBuilder= newStringBuilder();for(int i = strings.size()-1 ; i >= 0 ; i--) {
stringBuilder.append(strings.get(i));
}//去掉头尾括号
s = stringBuilder.toString().substring(1,stringBuilder.length() - 1);
equation1=simpleCalculate(s);if (equation1 == null) {return null;
}else{
equation.getProcesses().addAll(equation1.getProcesses());
strings.clear();
}
}else{
calculateStack.push(element);
}
}//处理完括号后栈中剩余的式子转换成字符串
while (!calculateStack.empty()) {
strings.add(calculateStack.pop());
}
StringBuilder stringBuilder= newStringBuilder();for (int i = strings.size() - 1; i > 0; i++) {
stringBuilder.append(strings.get(i)).append(" ");
}
stringBuilder.append(strings.get(0));
equation1=simpleCalculate(stringBuilder.toString());if (equation1 == null) {return null;
}else{
equation.getProcesses().addAll(equation1.getProcesses());returnequation;
}
}else{returnsimpleCalculate(formula);
}
}/*** 无括号的计算
*@params 无括号的式子
*@return式子和计算过程*/
privateEquation simpleCalculate(String s) {
List elementList = Arrays.asList(s.split(" "));
List processList = new ArrayList<>();
Stack stack = new Stack<>();
String symbol;
Iterator it =elementList.iterator();
Fraction a;
Fraction b;
Fraction answer;while(it.hasNext()){
symbol=it.next();if("×".equals(symbol) || "÷".equals(symbol)) {
a=Fraction.stringToFraction(stack.pop());
String param=it.next();if (param.contains("'")) {
param=FractionUtil.toFakeFraction(param);
}
b=Fraction.stringToFraction(param);switch(symbol) {case "×":
answer=a.multiply(b);break;case "÷":
answer=a.divide(b);break;default:
answer=a.multiply(b);
}
processList.add(newProcess(a,b,symbol,answer));if (it.hasNext() || !stack.empty()) {
stack.push(answer.toString());
}
}else{if (symbol.contains("'")) {
symbol=FractionUtil.toFakeFraction(symbol);
}
stack.push(symbol);
}
}
Stack stack1 = new Stack<>();while (!stack.empty()) {
stack1.push(stack.pop());
}while (!stack1.empty()) {
a=Fraction.stringToFraction(stack1.pop());
symbol=stack1.pop();
b=Fraction.stringToFraction(stack1.pop());switch(symbol) {case "+":
answer=a.add(b);break;case "-":if (!a.compare(b)) {
answer= null;
}else{
answer=a.subtract(b);
}break;default:
answer=a.add(b);
}if (answer == null) {return null;
}
processList.add(newProcess(a,b,symbol,answer));if (!stack1.empty()) {
stack1.push(answer.toString());
}
}return newEquation(s,processList);
}
}
该类是对单个具体算式进行运算的方法,包含了对算式中有无括号的情况进行分别处理,还有对处理前后的字符串转换过程。其中运用了栈的结构进行处理。
3.GenerateService 生成大类
public classGenerateService {public static ExecutorService es =Executors.newCachedThreadPool();public List getFormulaStirngList(Listformulas) {
List formulaStrings = new ArrayList<>(formulas.size());for(Formula f : formulas) {
formulaStrings.add(f.toString());
}returnformulaStrings;
}public List getFormulaList(MapparamMap) {
Integer num= paramMap.get("-n");
Integer max= paramMap.get("-r");
Constants.max=max;
List formulas = new ArrayList<>(num);
List> futures = new ArrayList<>(num);for (int i = 0; i < num ; i ++) {
Future future = es.submit(newFormulaFactory(max));
futures.add(future);
}/*es.shutdown();*/
for (Futurefuture : futures) {try{
formulas.add(future.get());
}catch (InterruptedException |ExecutionException e) {
e.printStackTrace();
}
}returnformulas;
}
}
该类与CalculateService对应,是生成环节的总过程里面包含了根据用户传递的信息进行算式生成的方法,同时与计算一样,利用了多线程的编程思想,用以提高程序的效能。
4.FormulaFactory 生成类
public class FormulaFactory implements Callable{private intmax;public FormulaFactory(intmax) {this.max =max;
}
@OverridepublicFormula call() {int random = (int) (Integer.MAX_VALUE *Math.random());int figureNum = (random % 3) + 2;
List symbolList = new ArrayList<>();
List fractionList = new ArrayList<>();
fractionList.add(getRandomFraction(max));for (int i = 1; i < figureNum ; i++) {
random= (int) (Integer.MAX_VALUE *Math.random());
Fraction b=getRandomFraction(max);
String symbol= Constants.symbols[random % 4];while ("-".equals(symbol)) {if (!fractionList.get(i-1).compare(b)) {
random= (int) (Integer.MAX_VALUE *Math.random());
symbol= Constants.symbols[random % 4];
}else{break;
}
}
symbolList.add(symbol);
fractionList.add(b);
}return newFormula(symbolList,fractionList);
}/*** 生成一个随机的数
*@parammax 限值
*@return随机的分数*/
publicFraction getRandomFraction(Integer max) {int numerator = (int) (Math.random() *max);while (numerator == 0) {
numerator= (int) (Math.random() *max);
}if (Math.random() > 0.5) {int denominator = (int) (Math.random() *max);while (denominator == 0) {
denominator= (int) (Math.random() *max);
}return newFraction(numerator,denominator);
}else{return new Fraction(numerator,1);
}
}
}
对应Calculation,这是生成的具体类,利用随机数生成的思想,再结合定义的分数类,来生成算式。
六、测试运行
1. 测试-n -r 功能:
a.10道题,10以内数字
算式:
结果:
经验证,结果符合要求
b.测试生成10000题目
2 测试验证功能-e -a
文件内容:
输出结果:
七、项目小结
本次项目最后基本完成了所有的需求,并对其进行了一定的优化。
这是结对项目,我们在项目过程中互相合作,分工合力完成了这项任务。从最初的讨论需求,设计结构,到分模块的项目开发,再到最后的磨合优化,期间体验到了不同人合作完成一项任务会产生的摩擦和交流,从队友身上也学到了很多的新东西,更从这个过程中体验到了合作开发的过程。
项目成员:梁浩然 3117004616 张妙馨 3117004643