小学四则运算练习软件项目报告
项目克隆地址:https://git.coding.net/chenf640/workhome2_2.git
目录:
一、需求分析
二、功能设计
三、设计实现
四、算法详解
五、测试运行
六、代码展示
七、psp
八、总结
——————————————————————————————————————————————————
一、需求分析
(一)功能需求
基本功能:
- 程序可接收一个输入参数n,然后随机产生n道加减乘除(分别使用符号+-*÷来表示)练习题。
- 每个数字在 0 和 100 之间,运算符在3个到5个之间;
- 每个练习题至少要包含2种运算符;
- 所出的练习题在运算过程中不得出现负数与非整数,比如3÷5+2=2.6,2-5+10=7等是不合法的;
- 练习题生成好后,将你的学号与生成的n道练习题及其对应的正确答案输出到文件“result.txt”中,不要输出额外信息,文件目录与程序目录一致;
- 当程序接收的参数为4时,以下为一个输出文件示例。
2018010203
13+17-1=29
11*15-5=160
3+10+4-16=1
15÷5+3-2=4
扩展功能:
- 支持有括号的运算式,包括出题与求解正确答案。
- 扩展程序功能支持真分数的出题与运算(只需要涵盖加减法即可),例如:1/6 + 1/8 + 2/3= 23/24。注意在实现本功能时,需支持运算时分数的自动化简,比如 1/2+1/6=2/3,而非4/6,且计算过程中与结果都须为真分数。
(二)程序需求
1、只能使用Java语言。
2、生成文件时请使用相对路径,生成的txt 文件需在项目的根目录下,可直接查看演示示例。
3、使用的JDK版本为 jdk8u161,使用的JRE版本为jre8u161。
4、不得使用除限定版本jdk与jre外的额外依赖包。
二、功能设计
(一)基本功能
1、保证输入的是数字或者特定字符,如果不符合要求,会有提示,并且可以重新输入。
2、当输入一个参数n,随机产生n道加减乘除算术题。保证生成的每个练习题至少要包含2种运算符。且保证在算数过程中,不出现负数,和小数。
3、将生成的n道练习题及其对应的正确答案输出到文件“result.txt”中,不要输出额外信息,文件目录与程序目录一致;
4、为保证随机产生的四则运算更满足用户的需求,当输入参数n决定产生四则运算的个数时,另外输入一个参数m,决定产生的n道四则运算包含运算符的个数(3、4、5)
(二)扩展功能
1、保证生成的分数运算,在运算过程中不产生假分数。并且实现自动化简。
2、产生带有括号的运算式时保证不产生没有意义的括号。
三、设计实现
该程序我的设计思想是一个类实现一个功能,提高代码的重用性,减少冗杂度。让代码更清晰。
(一)Main类
功能:是主函数,包含 public static void main(String[] args) 方法。并且包含判断 输入的参数是否为整数的 函数,如果不是函数,可提示用户重新输入。
目的:确保用户输入满足要求的整数的参数(产生四则运算数量1-1000,每个四则运算包含运算符3-5)
(二)Algorithm类
功能:产生符合要求的一个四则运算,不产生括号。该类中包含一个 带参的返回类型为字符串的函数。该函数参数为产生四则运算中包含的符号个数,可以产生包含任意多符号数量的四则运算,在该函数中,为满足用户需求,运算符数量可以为三、四、五,即该函数参数可以为3、4、5。
目的:产生满足用户需求的四则运算。(包含的参数个数、在运算过程中不能产生负数、小数)
关系:公有类,可直接调用。
(三)BracketsAlgo类
功能:产生符合要求的带有括号的四则运算,该类中包含一个 带参的返回类型为字符串的函数。该函数参数为产生四则运算中包含的符号个数,可以产生包含任意多符号数量的四则运算,在该函数中,为满足用户需求,运算符数量可以为三、四、五,即该函数参数可以为3、4、5。
目的:产生括号,增大运算难度。
(四)Fraction类
功能:生成分数运算,保证在运算过程中不产生假分数。并且实现自动化简。该函数参数为产生四则运算中包含的符号个数,可以产生包含任意多符号数量的四则运算,在该函数中,为满足用户需求,运算符数量可以为三、四、五,即该函数参数可以为3、4、5。
目的:练习分数运算。
(五)CreatFile类
功能:生成规定数量的四则运算,并将运算式与结果输出到result.txt文件中。该类中包含一个带参无返回的函数,该函数参数为要打印四则运算的数量。
目的:将符合要求的四则运算输出到txt文件中,方便保留与查看。
关系:公有类,可直接调用。
(六)Symbol类
功能:将产生的代表运算符的数字转换为对应的运算符。(0代表“+”,1代表“-”,2代表“*”,3代表“÷”)
目的:输出符合规范的运算符
(七)Test测试类
功能:测试方法。
四、算法详解(具体代码分析)
(一)重要算法
1.Algorithm类中的Algorithm算法
算法功能:产生,符合要求的四则运算(运算过程中不产生负数、小数,每个四则运算至少包括两种运算符)
算法思想:
1.1.判断产生的运算符不完全相等(至少包括两种)。
- 用while循环,如果随机产生的数字完全相等,则一直循环,直到不完全一致。
- 用数字0-3分别代表运算符的加减乘除,将随机生成的数字进行比较,如果四个数字完全相等,则重新生成,最终将满足要求的代表运算符的数字存到数组中。
1.2.运算符之间优先级比较。
- 将运算符从前到后两两比较,先计算优先级高的运算符,先乘除后加减,同级运算符从左向右依次计算。
- 通过运算符两两比较,每循环一次就将生成的结果代替原来的数字,循环结束后,最终的结果会放到数组的最后一位。在计算过程中判断是否产生负数、是否产生小数,是否除数为0等,如果产生不满足要求的运算式,则舍去,重新开始。
以一次循环为例:这是不完整的流程图,只包含其中一部分情况。
1.3 .产生包含任意多运算符的四则运算。
- 只需改变循环的次数
1.4.将生成的正确的四则运算和结果放到一个字符串中,返回该字符串给 CreatFilel类中的写入文件方法。
- 为了避免在比较优先级的循环中,改变存放数组与运算符的数组,因此,在进行循环判断前,将运算数与运算符交错放到数组中,如果通过验证证明该四则运算符合要求,则返回给 CreatFilel类中的写入文件方法,写入result.txt文件。
- 在输出时,用for进行判断,如果是运算数子部分则直接输出,如果是运算符部分,则将数字转化为对应的运算符号进行输出。
如图所示:
2.CreatFile中的creatFile将生成结果写入指定文件
- 将返回的字符串写入指定路径下的result.txt文件中,并在该文件第一行输出学号。
- 该算法比较简单,需要导入java.io这个jar包。
- 通过main方法调用,传入两个参数,生成算法的数量和每个算法包含的运算符数量。
3.Fraction类中的fractionArithmetic算法
算法功能:生成符合要求的分数运算式。
算法实现:
- 随机产生随机数字(0、1)来代表运算符(+、-)
- 随机产生两个数字,一个做分母、一个做分子(不能为0)。在运算过程中,不断判断产生的分数是否大于等于一、并不断将分数进行化简。
(二)其他算法
1.Symbol方法中的symbol算法
算法思想:输出运算符时,将数字与字符一一对应。用switch()语句即可完成。
2.验证输入的为符合要求的整数(包括生成四则运算的数量、生成的四则运算中包含运算符的数量)
算法思想:将输入的内容进行验证,先验证是否为整数,如果是整数,再验证该整数是否符合要求。若不符合要求,则对用户进行提示,要求重新输入。
在命令行结果如下:
3.BracketsAlgo类中的bracketsAlgo算法
算法功能:产生带有括号的符合要求的四则运算
算法实现:
- 将随机生成的运算符从左向右两两比较。如果前面的优先级高,则进行运算,保存结果;如果前面运算符的优先级低,则加上括号。
- 该算法一定是从左向右依次运算。
缺点:该算法可以生成带有括号的四则运算,但产生括号的数量不能确定。
五、测试运行
为了更好地实现用户的要求,在运行代码时需要输入你要打印的题目的具体数量、包含运算符的具体数量、以及是否有括号、是否是分数运算等参数。
1.进入src文件夹;
2.在命令行输入javac -encoding utf-8 Main.java;
3.回车再输入java Main 20;
4回车.输入你要打印的题目数量;
5回车.输入你要打印的题目所带符号的数量;
6.回车.输入你要打印的题目的类型(y-分数运算;n- 整数运算);
7.如果输入的是y,回车.result.txt文件生成,分数运算式写入result.txt文件(如图一,结果对应图三)。
如果输入的是n,回车选择产生的算式是否包括括号(y--包括;n--不包括,回车.result.txt文件生成,对应的运算式写入result.txt文件(如图二,结果对应图四)。
图一图二
运算结果:
图三图四
六、代码展示:
Algorithm类中的Algorithm算法:
/* 将运算符从前到后两两比较,先计算优先级高的运算符,先乘除后加减,同级运算符从左向右依次计算 在计算过程中判断是否产生负数、是否产生小数,是否除数为0等,如果产生不满足要求的运算式, 则舍去,如果符合要求,则打印出来。 */ for (int i = 0; i < m.length; i++) { if ((m1[i] < m1[i + 1]) && (m1[i + 1] == 2)) { /* 包含情况为 0<2;1<2 计算优先级高的(乘法) 区域运算,并将结果放到 存放数字的数组中,运算过的运算符也被取代*/ num[i + 2] = num[i + 1] * num[i + 2]; num[i + 1] = num[i]; m1[i + 1] = m1[i]; number++; } else if ((m1[i] < m1[i + 1]) && m1[i + 1] == 3 && m1[i] != 2) { /* 包含情况为 0<3;1<3计算优先级高的(除法) 区域运算,并将结果放到 存放数字的数组中,运算过的运算符也被取代*/ if (num[i + 2] != 0) { //首先除数不能为0 int a = num[i + 1] % num[i + 2]; //是否整除 if (a == 0) { //如果整除,进行除法运算 num[i + 2] = num[i + 1] / num[i + 2]; num[i + 1] = num[i]; m1[i + 1] = m1[i]; number++; } else { //如果不能整除,重新进行四则运算的产生 number = 0; for (int k = 0; k < m.length; k++) { m[k] = 0; } break; } } else { //除数为0,重新进行四则运算的产生 number = 0; for (int k = 0; k < m.length; k++) { m[k] = 0; } break; } } else if (m1[i] == 2 && m1[i + 1] == 3) { /* 包含情况为 2<3计算从左向右进行,并将结果放到 存放数字的数组中*/ m[i + 1] = m[i] * m[i + 1]; number++; } else if ((m1[i] < m1[i + 1]) && (m1[i + 1] == 1)) { /* 包含情况为 0<1 计算从左向右进行,并将结果放到 存放数字的数组中*/ m[i + 1] = m[i] + m[i + 1]; number++; } else if ((m1[i] > m1[i + 1]) && m1[i] == 3) { /* 包含情况为 3>2;3>1;3>0 计算从左向右进行,并将结果放到 存放数字的数组中*/ if (num[i + 1] != 0) { //被除数不能为0 int a = num[i] % num[i + 1]; if (a == 0) { //能够整除 num[i + 1] = num[i] / num[i + 1]; number++; } else { number = 0; for (int k = 0; k < m.length; k++) { m[k] = 0; } break; } } else { //被除数为0 number = 0; for (int k = 0; k < m.length; k++) { m[k] = 0; } break; } } else if ((m1[i] > m1[i + 1]) && m1[i] == 2) { /* 包含情况为 2>1;1>0 计算从左向右进行,并将结果放到 存放数字的数组中*/ num[i + 1] = num[i] * num[i + 1]; number++; } else if ((m1[i] > m1[i + 1]) && m1[i] == 1) { /* 包含情况为 1>0 计算从左向右进行,并将结果放到 存放数字的数组中*/ if (num[i] > num[i + 1]) { //是否产生负数 num[i + 1] = num[i] - num[i + 1]; number++; } else { number = 0; for (int k = 0; k < m.length; k++) { m[k] = 0; } break; } } else { /* 包括情况为0==0;1==1;2==2 3==3*/ if (m1[i] == m1[i + 1]) { if (m1[i] == 0) { //加法 num[i + 1] = num[i] + num[i + 1]; number++; } else if (m1[i] == 1) { //减法,判断是否产生负数 if (num[i] > num[i + 1]) { num[i + 1] = num[i] - num[i + 1]; } else { number = 0; for (int k = 0; k < m.length; k++) { m[k] = 0; } break; } } else if (m1[i] == 2) { //乘法 num[i + 1] = num[i] * num[i + 1]; number++; } else { //除法运算,判断除数不能为0,不能产生小数 if (num[i + 1] != 0) { int a = num[i] % num[i + 1]; if (a == 0) { num[i + 1] = num[i] / num[i + 1]; number++; } else { for (int k = 0; k < m.length; k++) { m[k] = 0; } number = 0; break; } } else { for (int k = 0; k < m.length; k++) { m[k] = 0; } number = 0; break; } } } } }
2.判断你要打印的题目数量的个数是否为整数
以打印题目数量为例:
/** * 判断 输入你要打印的题目数量的个数是否为整数 * @return */ public static int numberTest() { Scanner input = new Scanner(System.in); int number =0; while(number<1||number>1000){ System.out.print("请输入你要打印的题目数量 (1-1000):"); String s = input.next(); while (!(s != null && s.matches("^[0.0-9.0]+$"))) {// [0-9]没办法识别小数,[0.0-9.0]可以识别小数和整数 System.out.print("请输入正确的题目数量,类型为整数 (1-1000):"); s = input.next(); } number = Integer.parseInt((s)); } return number; }
七、psp:
本次编码并没有严格计算时间,刚开始也没有预测所有用到的时间,下表所填的时间是估计的,可能不是很准确,下次再做作业时会更加准确的记录时间。
PSP2.1 |
任务内容 |
计划共完成需要的时间(min) |
实际完成需要的时间(min) |
Planning |
计划 |
20 |
|
· Estimate |
· 估计这个任务需要多少时间,并规划大致工作步骤 |
20 |
|
Development |
开发 |
15*60 |
27*60 |
· Analysis |
· 需求分析 (包括学习新技术) |
60 |
|
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
30 |
|
· Design |
· 具体设计 |
60 |
|
· Coding |
· 具体编码 |
14.5*60 |
|
· Code Review |
· 代码复审 |
60 |
|
· Test |
· 测试(自我测试,修改代码,提交修改) |
9*60 |
|
Reporting |
报告 |
6.5*60 |
|
· Size Measurement |
· 计算工作量 |
10 |
|
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
20 |
|
写博客 |
6*60 |
八、总结:
(一)实现软件的'模块化'原则:
- 对功能进行分析,并分解成若干子任务,每个函数只实现一种功能,并将一类功能放到一个类中。通过函数之间的调用来实现所有功能。
- 避免代码冗杂,提高代码的重用性跟规范性。
(二)缺点:
虽然实现了作业的基本要求和附加要求,但关于附加功能的第一条完成得不是很好,虽然不会产生无意义的括号,如 (3*4)*5,但是因为符号的产生是随机的 ,所以很难保证括号的数量。而且,自己的代码还是过于冗杂,还有待提高。
(三)优点:
- 除了附加一的一些问题,本作业的其他要求都以实现。
- 代码书写规范
- 有详细地注释
-
虽是一个基本功能,但是我用了三种方法去写(从刚开始最复杂的多重循环(Arithmetic3类中的Arithmetic_test()方法到逻辑更加严谨的算法(Algorithm类中的algorithm()方法再到最后更加简洁的(BracketAlgo类中的bracketsAlgo()算法)。虽然看上去浪费了很多时间,但是这也让我认识到了算法的强大,逐渐进步。
(四)个人感受
首先,先谈谈我写这个作业的过程。
对于这次作业,刚开始我是低估了难度的。我认为只要两天左右的课余时间就可以完成。结果,这次实际花的时间远远超过我预想的。并且,由于刚开始我的不恰当的想法,导致我前面浪费了许多时间。后面又用了一种更加简便的方法去做,成功的实现了基本功能中的所有要求。于是我开始去做附加要求,对于附加要求,刚开始并没有什么想法,于是,我上网查了些资料,看了同学们的一些博客,渐渐的发现附加功能并没有自己想的那么难。可能刚开始写的时候思路比较顺畅,这次附加功能并没有占用我很长时间(敲代码时间的三分之一左右)。最后,就是,写博客啦。写博客对我而言还是挺费时间,大概花了六个小时。终于,个人项目完成啦!
这次项目,于我而言还挺能难的,也挺费时间的,主要还是自己算法不是很好,不能找到最优解,导致自己浪费了一些时间。通过这次项目,我收获最大的就是互相学习、交流的重要性。如果没有看同学们、看网上的博客,没有跟同学们讨论,我可能还会限制在自己的思路中。还有就是在敲代码前一定要有明确的思路,要不然敲了半天可能会是无用功。这做这个项目过程中,debug也花费了很多时间。
虽然这周很忙,最后几天忙到晚上两点多,电脑没电了才睡觉。但是我很喜欢这种专注于一件事的感觉,以前没事的时候会看看剧,聊聊天,但是最近这几天,有时间就是敲代码,有时走在路上也会想怎么更好的实现功能,之间的逻辑是什么。在项目最终完成时,很激动也很有满足感。我想,这就是软件工程的魅力吧。
最后,依旧一句话结尾。也依旧是 越努力、越幸运!