java编程四则运算_四则运算结对编程(JAVA)

本文详述了一次Java编程项目,旨在实现四则运算题目的生成、查重和答案核对。项目成员通过PSP表格记录了开发过程,分析了效能,发现查重过程中的时间复杂度问题。主要功能包括生成不重复的四则表达式,设计实现包括图形界面和多个类与函数,如随机生成题目、生成答案、查重等。通过测试运行确保了功能的正确性。项目小结强调了结对编程在提高代码质量、发现漏洞方面的优势,同时也指出了可能存在的沟通挑战。
摘要由CSDN通过智能技术生成

一、Github项目地址

项目成员:刘昱君,潘蓓文

二、PSP表格

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

30

30

· Estimate

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

30

30

Development

开发

2340

2565

· Analysis

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

210

180

· Design Spec

· 生成设计文档

90

120

· Design Review

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

60

30

· Coding Standard

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

60

45

· Design

· 具体设计

500

400

· Coding

· 具体编码

1000

1200

· Code Review

· 代码复审

120

90

· Test

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

300

500

Reporting

报告

275

240

· Test Report

· 测试报告

240

200

· Size Measurement

· 计算工作量

15

15

· Postmortem & Process Improvement Plan

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

20

25

合计

2610

2835

三、效能分析

最初我们实现了随机生成题目并同时产生答案的功能,此时所花费的时间并不多,因为我们的答案使用后缀表达式生成的,所以我们考虑到如果两个式子后缀表达式相同则他的每一步运算过程都相同,通过HashSet存储后缀表达式并每生成题目就使用它的add方法若add不成功则重新生成,此时虽然所耗时间不多,但我们忘记考虑后缀表达式计算过程中+和×左右交换相同的问题,所以我们重新优化了原先的代码,在之前的基础上将后缀表达式用他的计算过程生成一种表达式来查重,只要先辨别该表达式是否一样若不同就将+或×左右交换后继续比较,这样就得到了比较准确的结果,但是这样同时将时间复杂度提升了许多倍,未来我们将进行进一步的优化

01ef0785c227253c4a7b97dc7b61ff0b.png

程序中消耗最大的功能:

生成10000道不重复的100内数值的四则表达式

fcf402dbc41bfddd8fdb42c3b572e777.png

66106f4fe54def52e7ef476a4552782e.png

四、设计实现过程

主类Ari_main类中调用Jfra类初始化图形界面,在图形界面中包括四个按钮以及三个文本区域:按钮对应功能分别为“生成题目”“保存文件”“上传文件”和“核对答案”,文本区域从左到右的功能分别为显示生成的题目,输入答案或显示上传的答案文件内的内容,显示答案的正确与否、题目的正答和答题情况。点击“生成题目”按钮先调用Creater类生成题目,再调用DupCheck类对生成的题目进行查重,最后调用formJudge类计算出表达式的答案;点击“保存文件”按钮将生成的题目存入一个文件,并保存于本地,同时生成对应的答案文件,存放于同一路径;点击“上传答案”按钮读取选择的文件,并将文件内的内容打印在中间的文本区域;点击“核对答案”按钮将第二个文本区域内的内容一行行读取出来,存入List当中,并将它与正确答案进行核对。

4.1该程序包含的类和函数如下图所示。

c518e59102956c9775267d289488db18.png

4.2以下为关键函数流程图

4.2.1生成题目(包含产生括号)

d6e2f75b493028330a9bb82cbd340469.png

4.2.2生成答案

794994ec028525f3567eb5216bdcaf11.png

4.2.3查重

02feb10519c115a68899ce89108062c9.png

五、代码说明

5.1随机生成题目

//创建一个新题目

/*创建思路:首先通过随机数来确定生成的题目中有几个数字,然后通过for循环

* 循环生成2*n个随机数,这时每两个随机数可以组成一个分数,并且根据要求的

* 形式化简,接着通过随机数来判断生成的运算符从而随机生成n-1个运算符,然后将

* 这些按顺序连接成字符串返回*/

public numItem pro_creater(intrange) {//创建rc,rn,ro来存储随机数从而随即得到

intrc,rn,ro;

numItem str= newnumItem();

String [] ch= new String[] {"+","-","×","÷"};

rc= (int)(Math.random()*3+2);//随机生成2~4的数来判断生成几个数

int [] a = new int[2*rc];for(int i=0; i < 2*rc; i++) {

a[i]= 1;

}

String [] num_str= newString[rc];

String [] num_str1= newString[rc];

String [] b= new String[rc-1];for(int j = 0; j < rc; j++) {

rn= (int)(Math.random()*(2-0));//随机生成0和1判断生成真分数或整数和带分数

if(rn == 1) {

a[2*j+1] = (int)(Math.random()*range+1);

a[2*j] = (int)(Math.random()*(a[2*j+1]-1)+1);

num_str[j]= fj.proSimple(a[2*j],a[2*j+1]);

}else{

a[2*j] = (int)(Math.random()*range+1);

a[2*j+1] = (int)(Math.random()*(a[2*j]-1)+1);

num_str[j]= fj.improSimple(a[2*j],a[2*j+1]);

}

num_str1[j]= a[2*j] + "/" + a[2*j+1];

}for(int k =0; k < rc-1; k++) {

ro= (int)(Math.random()*(4-0));//生成0到3的随机数来判断生成什么运算符

b[k] =ch[ro];

}//加括号

str =parent_creater(rc,num_str,num_str1,b);returnstr;

}

5.1.1创建题目中有生成括号的功能,以下为生成括号的代码

//添加括号

/*思路:生成一个随机数来判断生成多少个括号,左括号和右括号必须成对出现,构建一个

* 最终字符串数组,一个左括号位置数组和一个右括号位置数组,如果是两个数字则不用生

* 成括号,如果是两个以上则随机生成最多n/2+1个括号,生成该范围内的随机数来判断生

* 成多少括号,先生成随机左括号的位置,然后根据左括号位置随机生成右括号位置,并将

* 两个同时放入数组,若右括号位置找出范围则停止,最后将他们和已有字符连接生成最终式子*/

public numItem parent_creater(intrc, String [] num_str, String [] num_str1, String [] b) {

numItem str= newnumItem();

String [] spl= newString[rc];

String [] spr= newString[rc];for(int i = 0; i < rc; i++) {

spl[i]= "";

spr[i]= "";

}int rp = (int)(Math.random()*(rc/2+1-0));if(rc == 2) {

str.newstr= num_str[0] + " " + b[0] + " " + num_str[1];

str.oldstr= num_str1[0] + " " + b[0] + " " + num_str1[1];

}else{int lmin = 0;for(int i = 0; i < rp; i++) {int rpl = (int)(Math.random()*(rc-1-lmin)+lmin);//生成左括号位置

int rpr = (int)(Math.random()*(rc-rpl-1)+rpl+1);//根据左括号位置生成右括号位置

if(rpl == 0 && rpr ==rc-1) {

i--;continue;

}if(rpr > rc-1) break;

spl[rpl]= "(";

spr[rpr]= ")";

lmin= rpr+1;

}if(spl[0] == "") {

str.newstr= num_str[0] + " " + spr[0];

str.oldstr= num_str1[0] + " " + spr[0];

}else if(spr[0] == "") {

str.newstr= spl[0] + " " + num_str[0];

str.oldstr= spl[0] + " " + num_str1[0];

}else{

str.newstr= spl[0] + " " + num_str[0] + " " + spr[0];

str.oldstr= spl[0] + " " + num_str1[0] + " " + spr[0];

}for( int i = 1; i < rc; i++) {

str.newstr= str.newstr + " " + b[i-1] + " " + spl[i] + " " + num_str[i] + " " +spr[i];

str.oldstr= str.oldstr + " " + b[i-1] + " " + spl[i] + " " + num_str1[i] + " " +spr[i];

}

}returnstr;

}

5.2 生成答案

5.2.1中缀表达式转后缀表达式

//通过后缀表达式计算数值

/*1. 从左到右遍历表达式的每个数字和符号

* 1.1 若是数字则进栈

* 1.2 若是运算符则将栈顶两个元素出栈,进行运算并将运算结果进栈

* 2. 遍历完后缀表达式,此时栈中剩余的数字就是运算结果*/

private String calculateByPostfix(Listpostfixes) {

Stack stack = new Stack();for (Item item : postfixes) {//遍历后缀表达式

if (item.isNumber()) {//若为数则直接入栈

stack.push(item.value);

}else {//若为操作符则将栈顶两个元素取出并进行相应运算//将得到的两个字符串分数转化成Number型

String num_1,num_2;

Number num1= newNumber();

Number num2= newNumber();

Number result= newNumber();

num_1=stack.pop();

num_2=stack.pop();

num1=returnNum(num_1);

num2=returnNum(num_2);if (item.isAdd()) {//计算加法

result.a = num2.a * num1.b + num1.a *num2.b;

result.b= num2.b *num1.b;

}else if (item.isSub()) {//计算减法

result.a = num2.a * num1.b - num1.a *num2.b;

result.b= num2.b *num1.b;if(result.a < 0) return null;//若为负数则不生成答案

} else if (item.isMul()) {//计算乘法

result.a = num2.a *num1.a;

result.b= num2.b *num1.b;

}else if (item.isDiv()) {//计算除法

result.a = num2.a *num1.b;

result.b= num2.b *num1.a;

}else{throw new IllegalArgumentException("Operator invalid : " +item.value);

}

stack.push(result.a+ "/" +result.b);

}

}

5.2.2后缀表达式计算结果

//通过后缀表达式计算数值

/*1. 从左到右遍历表达式的每个数字和符号

* 1.1 若是数字则进栈

* 1.2 若是运算符则将栈顶两个元素出栈,进行运算并将运算结果进栈

* 2. 遍历完后缀表达式,此时栈中剩余的数字就是运算结果*/

private String calculateByPostfix(Listpostfixes) {

Stack stack = new Stack();for (Item item : postfixes) {//遍历后缀表达式

if (item.isNumber()) {//若为数则直接入栈

stack.push(item.value);

}else {//若为操作符则将栈顶两个元素取出并进行相应运算//将得到的两个字符串分数转化成Number型

String num_1,num_2;

Number num1= newNumber();

Number num2= newNumber();

Number result= newNumber();

num_1=stack.pop();

num_2=stack.pop();

num1=returnNum(num_1);

num2=returnNum(num_2);if (item.isAdd()) {//计算加法

result.a = num2.a * num1.b + num1.a *num2.b;

result.b= num2.b *num1.b;

}else if (item.isSub()) {//计算减法

result.a = num2.a * num1.b - num1.a *num2.b;

result.b= num2.b *num1.b;if(result.a < 0) return null;//若为负数则不生成答案

} else if (item.isMul()) {//计算乘法

result.a = num2.a *num1.a;

result.b= num2.b *num1.b;

}else if (item.isDiv()) {//计算除法

result.a = num2.a *num1.b;

result.b= num2.b *num1.a;

}else{throw new IllegalArgumentException("Operator invalid : " +item.value);

}

stack.push(result.a+ "/" +result.b);

}

}//返回化简成分数或整数或带分数的结果

Number num = newNumber();

String ans=stack.pop();

num=returnNum(ans);if(num.a < num.b) returnproSimple(num.a,num.b);else returnimproSimple(num.a,num.b);

}

5.3生成答案

5.3.1后缀表达式转化成通过计算过程产生的查重表达式

//将后缀表达式转化为可以查重的表达式的形式,即按照计算过程排列的表达式

/*由于运算结果时会用到后缀表达式,在这里将后缀表达式转换成可以查重的表达式

* (过程运用堆栈且基本和计算后缀表达式一的做法).思路主要是以后缀表达式的

* 长度循环,如果遇到数字就压栈,遇到运算符就连续出栈两个数字字符,出栈后将“#”

* 压入数字栈,这个字符就类似计算后缀表达式时的结果,完成循环后可得到查重的表达式*/

public List getDupExpression(Listpostfixes) {

List dup = new ArrayList();

Stack stack = new Stack();for(Item item : postfixes) {if(item.isNumber()) {

stack.push(item.value);

}else{

String result= "#",num1,num2;

dup.add(item.value);

num1=stack.pop();

num2=stack.pop();if(num1 != "#") {

dup.add(num1);

}else{

dup.add("null");

}if(num2 != "#") {

dup.add(num2);

}else{

dup.add("null");

}

stack.push(result);

}

}returndup;

}

5.3.2通过查重表达式查重

//判断两个字符串是否重复

public booleandupCheck(String str1,String str2) {

formJudge jd= newformJudge();

List s1 = getDupExpression(jd.infix2postfix(jd.parse(str1)));//将字符串s1转化成字符列表

List s2 = getDupExpression(jd.infix2postfix(jd.parse(str2)));//将字符串s2转化成字符列表

if(s1.equals(s2)) return true;//若两列表内容相同返回true

for(int j = 0; j < s2.size()-1; j++) {//若不同进行循环将+或×后的两个字符串交换看能否相同

if(s2.get(j).equals("+")||s2.get(j).equals("×")) {

String temp= s2.get(j+1);

s2.set(j+1,s2.get(j+2));

s2.set(j+2,temp);

}

j*=3;if(s1.equals(s2)) return true;//若交换后相同则返回true

}return false;

}

5.4核对答案

//点击核对答案的事件

b4.addActionListener(newActionListener() {

@Overridepublic voidactionPerformed(ActionEvent e) {//TODO Auto-generated method stub

a3.setText("");

String [] ansline= a2.getText().split("\n");int correct = 0,error = 0;for(int i = 0; i < ansline.length ; i++) {if(anslist.get(i).equals(ansline[i])){

a3.append("√" + " " + "正答:" + " " + anslist.get(i) + '\n');

correct++;

}else{

a3.append("×" + " " +"正答:" + " " + anslist.get(i)+ '\n');

error++;

}

}

a3.append("正确数目为:" + correct + '\n');

a3.append("错误数目为:" + error + '\n');

}

});

六、测试运行

我们能够确定生成的答案的正确性是通过多次测试得到的,我们计算过每个式子的答案是否真正正确,也填过错误的答案来判断核对答案功能是否成功,同理,其他功能的正确性也是如此

6.1打开后进入图形界面

52ad8dc9f99ec3f4d04d553d86af75d5.png

6.2点击生成题目

dd9df618e4851f1798749b6caa3c9f38.png

45dca5eda772de0c4b19c62352b4a2ae.png

f4d13ff925e25d2cd8a9fe12555cbf47.png

6.3点击保存文件

1ebc12601f6fa6417b6a36b874f4a2a7.png

0afd61a676f2377bba707f4f414c2a4d.png

199c8f0dbc0d621ba43d19335a8b9195.png

e503d9806483290afac6ed3a116f0a9e.png

6.4点击上传答案

07158ccd902ceec5f9b86d9a6db8d7b8.png

7ebaf3820dd7361db468102a9f67f9f8.png

6.5点击核对答案

033f5779f40c51f6269685942702fe79.png

6.6 测试查重

076e10dd7d1c8a790b975e70878455e7.png

1f4a408a8c8afc3be1d33b2e3817cb91.png

dc9a1c54ce21a881d6b6c51989e61404.png

f419b843d35b4cd51b89350fd336dde5.png

七、项目小结

通过这次项目,我们对结对编程有了一个更深的了解。通过结对,在代码设计的过程中,在编程过程能够更快地找到代码中的漏洞,并且比较容易找到解决之法。当然因为双方的思考方式是不一样的,因此在讨论的过程中会出现思想的碰撞,从而拖延了项目的进度。我们了解到在合作的过程中,要多从对方的角度考虑,不要固执己见。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值