项目成员:张金生     张政

  由于新的需求,原本使用JSP的实现方式目前改为Java实现,即去除了B/S端。

  需求分析:

  1.四则运算要满足整数运算、分数运算两种;

  2.运算题目随机,并且可以打印题目和答案;

  3.可以由用户输入答案,并进行对错判断;

  4.支持括号。

  5.出现的分数要约分,并且以“m p/n”方式输出。

  程序概要:

  1.原本用JSP实现,为满足需求现改用Java实现;

  2.用户可选择题目数量、题目难度(比如简单难度为两位数加减乘除,中等难度为四位数的加减乘除并包含括号,高难度为四位分数加减乘除等);

  3.用户可以选择进入答题还是打印问题与答案;

  4.答题时输入答案并回车会立即反馈结果;

  5.整数运算中不仅运算数是整数,结果也一定是整数;

  6.分数运算结果是约分后的,因此填写的答案也需要约分,否则会判为错。

  程序运行流程图:

  

  实现过程:

    分数类

  分数类构造函数

  在构造时直接进行约分操作,其中的maxCommonFactor是分子与分母的最大公约数。

        public Fraction(int up, int down) {
if (down == 0 | up == 0) {
System.out.println("divided by zero error");
return;
}
int smaller = up > down ? up : down;
int maxCommonFactor = 1;
for (int i = 1; i <= smaller; i++) {
if (up % i == 0 && down % i == 0) {
maxCommonFactor = i;
}
} this.up = up / maxCommonFactor;
this.down = down / maxCommonFactor;
}

  约分函数

  与构造函数中的月份操作有所不同,主要用来对进行四则运算后的结果数进行约分操作。

      public Fraction gcd(Fraction f) {
int smaller = f.up > f.down ? f.up : f.down;
int maxCommonFactor = 1;
for (int i = 1; i <= smaller; i++) {
if (f.up % i == 0 && f.down % i == 0) {
maxCommonFactor = i;
}
}
f.up = f.up / maxCommonFactor;
f.down = f.down / maxCommonFactor; return f;
}

  转化函数

  用于最后打印时使分数符合规范。

      public String toString() {
if (down == 1)
return "" + up;
if(Math.abs(up)/down>0){
return up>0?up/down+" "+up%down+"/"+down:"-"+Math.abs(up)/down+" "+Math.abs(up)%down+"/"+down;
}
return up + "/" + down;
}

  四则运算函数

  因为数据结构用到了栈,所以减法有些特殊,详情后面会给出解释说明,这里的changesign函数就是用来做减法运算的一个中间函数。

       public Fraction add(Fraction f) {
Fraction a = new Fraction(up, down);
a.up = f.up * a.down + a.up * f.down;
a.down = a.down * f.down; return a.gcd(a);
} public Fraction minus(Fraction f) {
Fraction a = new Fraction(up, down);
a.up = a.up * f.down - f.up * a.down;
a.down = a.down * f.down; return a.gcd(a);
} public Fraction multiply(Fraction f) {
Fraction a = new Fraction(up, down);
a.up = a.up * f.up;
a.down = a.down * f.down;
return a.gcd(a);
} public Fraction divide(Fraction f) {
Fraction a = new Fraction(up, down);
a.up = a.up * f.down;
a.down = a.down * f.up;
return a.gcd(a);
} public Fraction changeSign(){
up = -up;
return this;
}

  随机数函数

  用来随机生成分数。

       public static Fraction getRandiom(int Max) {
return new Fraction((int) (Math.random() * Max / 2) + 1, (int) (Math.random() * Max / 2) + 1);
} public static Fraction getRandiom(int Max, boolean isInt) {
return new Fraction((int) (Math.random() * Max / 2) + 1, isInt ? 1 : (int) (Math.random() * Max / 2) + 1);
}

    题目生成

  这里包含了括号的随机插入:用ArrayList维护随机生成的括号位置列表,然后使用TreeMap计算并保存应该生成括号的位置,最后在题目生成的时候进行拼接。

        public String generateQuestion(int numOfOperand, int rangeMin, int rangMax, SupportedOperation[] operation,
boolean isFractional, boolean hasbracket) {
String question = "";
int[] ioperands = null;
ArrayList<Fraction> af = new ArrayList<Fraction>();
SupportedOperation[] so = null;
if (numOfOperand < 2) {
System.out.println("操作数数量至少为2");
return "";
}
if (rangMax > 500) {
System.out.println("操作数数最大值不能超过500");
return "";
}
getBcPrint(numOfOperand);
if (!isFractional) {
ScriptEngine se = new ScriptEngineManager().getEngineByName("JavaScript");
ioperands = new int[numOfOperand];
for (int i = 0; i < numOfOperand; i++) {
ioperands[i] = (int) (Math.random() * rangMax / 2 + 1); }
so = new SupportedOperation[numOfOperand - 1];
for (int i = 0; i < operation.length; i++) {
if (operation[i] == SupportedOperation.ALL) {
operation = new SupportedOperation[4];
operation[0] = SupportedOperation.ADD;
operation[1] = SupportedOperation.MINUS;
operation[2] = SupportedOperation.MULTIPLY;
operation[3] = SupportedOperation.DIVIDE; }
}
// 除法運算,保证整除
int value = 0;
for (int j = numOfOperand - 1; j > 0; j--) {
so[numOfOperand - 1 - j] = operation[(int) (Math.random() * operation.length)];
}
for (int j = numOfOperand - 2; j >= 0; j--) {
if (so[j] == SupportedOperation.DIVIDE) {
if (value < 1) {
ioperands[j] = ioperands[j] * ioperands[j + 1];
value++; } else {
so[j] = operation[(int) (Math.random() * (operation.length - 2))];
}
}
}
// 输出括号
for (int i = 0; i < numOfOperand - 1; i++) {
if (frequency.containsKey(i)) {
if (direction.get(i) == 0) {
for (int k = 0; k < frequency.get(i); k++) {
question += "(";
}
}
}
question += ioperands[i];
if (frequency.containsKey(i)) {
if (direction.get(i) == 1) {
for (int k = 0; k < frequency.get(i); k++) {
question += ")";
}
}
}
question += so[i];
}
if (frequency.containsKey(numOfOperand - 1)) {
if (direction.get(numOfOperand - 1) == 0) {
for (int k = 0; k < frequency.get(numOfOperand - 1); k++) {
question += "(";
}
}
}
question += ioperands[numOfOperand - 1];
if (frequency.containsKey(numOfOperand - 1)) {
if (direction.get(numOfOperand - 1) == 1) {
for (int k = 0; k < frequency.get(numOfOperand - 1); k++) {
question += ")";
}
}
} try {
Integer d = (Integer) se.eval(question);
answer = "" + d;
} catch (Exception e) {
generateQuestion(numOfOperand, rangeMin, rangMax, operation, isFractional, hasbracket);
} } else {
for (int i = 0; i < numOfOperand; i++) {
af.add(Fraction.getRandiom(rangMax));
} so = new SupportedOperation[numOfOperand - 1];
for (int i = 0; i < operation.length; i++) {
if (operation[i] == SupportedOperation.ALL) {
operation = new SupportedOperation[4];
operation[0] = SupportedOperation.ADD;
operation[1] = SupportedOperation.MINUS;
operation[2] = SupportedOperation.MULTIPLY;
operation[3] = SupportedOperation.DIVIDE; }
}
question += af.get(0);
for (int j = 0; j < numOfOperand - 1; j++) {
so[j] = operation[(int) (Math.random() * operation.length)];
question += (so[j] == SupportedOperation.DIVIDE ? "÷" : so[j].toString()) + af.get(j + 1); }
answer = getanswer(af, so).toString();
try {
} catch (Exception e) {
e.printStackTrace();
}
} return question; }
}

    括号维护

      public String answer;

      Stack<SupportedOperation> oplist = new Stack<SupportedOperation>();
Stack<Fraction> numlist = new Stack<Fraction>();
ArrayList<Integer[]> bclist;
TreeMap<Integer, Integer> frequency;
TreeMap<Integer, Integer> direction; private void getBcPrint(int numOfOperand) {
bclist = new ArrayList<Integer[]>();
if (numOfOperand > 2) {
int bcnum = (int) (Math.random() * (numOfOperand - 2));
for (int n = 0; n < bcnum; n++) {
Integer[] bracket = new Integer[2];
bracket[0] = (int) (Math.random() * (numOfOperand - 2));
bracket[1] = (int) (Math.random() * (numOfOperand - 2 - bracket[0]) + bracket[0]);
if (bracket[0] == bracket[1]) {
bracket[1]++;
}
boolean canput = true;
for (int i = 0; i < bclist.size(); i++) {
Integer[] tmp = bclist.get(i);
if (bracket[0] < tmp[0] & bracket[1] >= tmp[0] & bracket[1] < tmp[1]) {
canput = false;
break;
} else if (bracket[1] > tmp[1] & bracket[0] > tmp[0] & bracket[0] <= tmp[1]) {
canput = false;
break;
} else if (bracket[0] == tmp[0] & bracket[1] == tmp[1]) { }
}
if (canput) {
bclist.add(bracket);
}
} }
frequency = new TreeMap<Integer, Integer>();
direction = new TreeMap<Integer, Integer>();
for (int i = 0; i < bclist.size(); i++) {
Integer[] tmp = bclist.get(i);
if (frequency.containsKey(tmp[0])) {
frequency.put(tmp[0], frequency.get(tmp[0]) + 1);
} else {
frequency.put(tmp[0], 1);
direction.put(tmp[0], 0);
}
if (frequency.containsKey(tmp[1])) {
frequency.put(tmp[1], frequency.get(tmp[1]) + 1);
} else {
frequency.put(tmp[1], 1);
direction.put(tmp[1], 1);
}
}
}

    计算结果

       public Fraction getanswer(ArrayList<Fraction> frlist, SupportedOperation[] so) {

           numlist.push(frlist.get(0));
for (int n = 0; n < so.length; n++) {
switch (so[n]) {
case ADD:
oplist.push(so[n]);
numlist.push(frlist.get(n + 1));
break;
case MINUS:
oplist.push(SupportedOperation.ADD);
numlist.push(frlist.get(n + 1).changeSign());
break;
case MULTIPLY: {
Fraction r = numlist.pop().multiply(frlist.get(n + 1));
numlist.push(r);
}
break;
case DIVIDE: {
Fraction r = numlist.pop().divide(frlist.get(n + 1));
numlist.push(r);
}
break;
default:
System.out.println("不支持的运算");
break;
}
} while (!oplist.isEmpty()) {
Fraction answer = numlist.pop();
switch (oplist.pop()) {
case ADD: {
answer = answer.add(numlist.pop());
numlist.push(answer);
}
break;
case MINUS: {
answer = answer.minus(numlist.pop());
numlist.push(answer);
}
break;
default:
System.out.println("不支持的运算");
break;
} } return numlist.pop();
}

  程序运行结果:

  简单难度答题:

  普通难度出题打印:

  复杂题目出题打印:

  程序退出:

  结对编程体会

  两个人在一台电脑上进行编码,其实感觉效率未必会提高,编码速度取决于正在编程的人的打字速度。而且有时候反而会因为在同一问题上看法不同、解决方式不同而产生分歧。不过因为结对编程可以了解到他人的编程思路,也是一种学习的过程。所以本次体会觉得效率提高是其次,互相学习才是最大的收获!

  争论点:

  1.整除问题:输出结果在尽量调节为整数的情况下可能因括号的出现导致结果为小数或者分数,对于这样的情况选择直接重出题目(仅限简单和普通难度的问题);

  2.分数减法入栈出栈顺序:计算结果采用栈来解决,出栈时仅剩加减法需要运算,当出现减法,甚至连减时选择将减法换为加法运算,然后改变操作数符号,即Fraction.changSign();

  3.括号列表维护:括号生成是根据操作数数量决定的,生成括号位置列表时排除无效和冗余括号,最后将括号列表转化位置和括号方向、数量的Map,用于打印括号,即先有操作数后有括号的设计,而不是括号匹配的方式;

  4.连除问题:用计数器检测除法的出现,防止连除,但是不会妨碍一个题目出现两个或更多的除法运算(仅限简单和普通难度)。因为在整数运算中,为保证可以整除,需要改变被除数,这样做太多次连除会导致最顶层操作数过大,不利于题目平衡性。

  花费时间较长的问题:括号位置列表生成及维护。

  工程地址:https://coding.net/u/jx8zjs/p/paperOne/git

SSH: ssh://git@git.coding.net:jx8zjs/paperOne.git

结对编程——paperOne基于java的四则运算 功能改进的更多相关文章

  1. 结对编程——paperOne基于java web的简易四则运算出题网站

    项目成员:张金生     张政 需求分析: 1.要进行四则运算: 2.运算题目随机: 3.进行对错判断: 4.整数运算. 程序概要: 1.用JSP实现: 2.用户可选择题目数量: 3.答题页用表格列出 ...

  2. 结对编程1----基于java的四则运算生成器

    小组成员:王震(201421123054).王杰(201421123055) Coding地址:https://git.coding.net/a506504661/sssss.git 一.题目描述 我 ...

  3. 20165325 2017-2018-2 《Java程序设计》结对编程_第二周:四则运算

    20165325 2017-2018-2 <Java程序设计>结对编程_第二周:四则运算 一.码云链接 FAO项目的码云链接; 1.Git提交日志已经实现一个功能/一个bug修复的注释说明 ...

  4. 20165325 2017-2018-2 《Java程序设计》结对编程_第一周:四则运算

    一.码云链接 项目名称FAO 码云链接 二.需求分析 实现一个命令行程序: 自动生成小学四则运算题目(加.减.乘.除) 支持整数 支持多运算符(比如生成包含100个运算符的题目) 支持真分数 统计正确 ...

  5. paperOne基于java web的简易四则运算出题网站

    项目成员:张金生     张政 需求概要 1.运算数均为正整数 2.包含的运算符有+,-,*,/ 3.除法运算结果为整除运算 4.批量生成题目并判题 核心功能分析 1.题目生成——java后端 题目生 ...

  6. 结对编程1 —— 基于GUI和Swing的四则运算题目生成器

    合作伙伴 201421123102 王艳秋 201421123106 陈 雄 代码地址 题目描述 我们在个人作业1中,用各种语言实现了一个命令行的四则运算小程序.进一步,本次要求把这个程序做成GUI( ...

  7. 结对作业:基于GUI实现四则运算

    1)Coding.Net项目地址:https://git.coding.net/day_light/GUIszysLL.git 2)在开始实现程序之前,在下述PSP表格记录下你估计将在程序的各个模块的 ...

  8. 结对编程1---基于Flask的四则运算题目生成器

    项目代码地址 / WEB应用地址 / 合作伙伴iFurySt博文链接 需求分析 本次程序是基于原有的控制台四则运算器的基础上,改成WEB的形式,同时还增加了一些新的功能.同时因为交互方式的改变,代码也 ...

  9. 结对编程1-基于GUI的四则运算生成器

    201421123016郑怀勇     201421123017康建灿 程序代码 / 康建灿 一.需求分析 记录用户的对错总数. 程序退出再启动的时候,能把以前的对错数量保存并在此基础上增量计算. 有 ...

随机推荐

  1. spring获取jdbc链接底层原理

    获取连接池的连接二种逻辑 1.一个事务中,一个连接 (底层逻辑:threadlocal存储  里面是map: key是数据源,value:链接) map存储应该是为多数据源使用的2.没有事务的serv ...

  2. hasura graphql-engine graphql2chartjs 方便的graphql 转换chartjs 的类库

    graphql2chartjs 是hasura graphql-engine 团队开源的方便graphql 转换为chartjs 的类库,我们可以方便的 用来进行ChartJS chart 开发 一张 ...

  3. yield return:使用.NET的状态机生成器

    通过关键字词组yield return,.Net Framework(从2.0开始)会为我们生成一个状态机.状态机实际上就是一个可枚举的类型化集合 理解yield return的工作方式 关键字词组y ...

  4. opencv人脸识别代码

    opencv人脸识别C++代码 /* * Copyright (c) 2011,2012. Philipp Wagner <bytefish[at]gmx[dot]de>. * Relea ...

  5. 合并两个sorted ranges(merge和inplace_merge)

    merge //版本一:用operator <比较元素 template <class InputerIterator1,class InputerIterator2,class Outp ...

  6. java Collections.sort()

    sort()是Collections中的静态方法,用于对List容器中的元素排序. 如容器list中存储的是Integer对象 List<Integer> list =Arrays.asL ...

  7. jmeter—PerfMon Metrics Collector(附java.io.IOException: Agent is unreachable via TCP错误解决办法)

    jmeter—PerfMon Metrics Collector(附java.io.IOException: Agent is unreachable via TCP错误解决办法 转自https:// ...

  8. Hadoop HDFS的shell(命令行客户端)操作实例

    HDFS的shell(命令行客户端)操作实例 3.2 常用命令参数介绍 -help 功能:输出这个命令参数手册 -ls                  功能:显示目录信息 示例: hadoop fs ...

  9. Spring Cloud Feign Ribbon 配置

    由于 Spring Cloud Feign 的客户端负载均衡是通过 Spring Cloud Ribbon 实现的,所以我们可以直接通过配置 Ribbon 的客户端的方式来自定义各个服务客户端调用的参 ...

  10. MS DSVM、DLVM

    DSVM(Data Science Virtual Machine 数据科学虚拟机)  是专为研究数据科学生成的 Microsoft Azure 云上的自定义 VM 映像.它预装并预配了许多热门数据科 ...