实验一
一、实验目标概述
本次实验通过求解三个问题,训练基本 Java 编程技能,能够利用 Java OO 开发基本的功能模块,能够阅读理解已有代码框架并根据功能需求补全代码,能够为所开发的代码编写基本的测试程序并完成测试,初步保证所开发代码的正确性。
另一方面,利用 Git 作为代码配置管理的工具,学会 Git 的基本使用方法。
-
- 基本的 Java OO 编程
-
- 基于 Eclipse IDE 进行 Java 编程
-
- 基于 JUnit 的测试
-
- 基于 Git 的代码配置管理
二、实验环境配置
IDE:
IntelliJ IDEA 2020.3.1 (JDK 1.8 Junit 4.12)
Git:
Git version 2.24.1.windows.2
Lab url:
url : https://github.com/ComputerScienceHIT/HIT-Lab1-1190200708.git
三、实验过程
3.1 Magic Square
3.1.1 功能
MagicSquare 主要实现了以下功能:
-
- 读取所给的五个 txt 文件,并用数组保存;
-
- 检验所给的五个 txt 文件是否为幻方矩阵
-
- 2.1 检验文件是否为空
-
- 2.2 检验每行是否存在小数
-
- 2.3 检验每行是否存在负数
-
- 2.4 检验每行是否存在非法字符
-
- 2.5 检验行的数量和列的数量是否相同
-
- 2.6 检验每行元素个数是否相同
-
- 2.7 检验行、列、对角线元素和是否相同
-
- 若是幻方矩阵则返回 true,否则抛出并打印错误;
-
- 检验要求创建的幻方矩阵的数是否合法,考虑奇数或偶数时的异常,若不存在异常,则生成 n*n 的矩阵,并写入 6.txt 文件
3.1.2 函数结构如下:
3.1.3 MagicSquare方法设计:
isLegalMagicSquare()
- 设计思路:
首先通过 System.getProperty(“user.dir”) + “\src\P1\txt\” + fileName 和 Scanner 读取文件,每次读取按行读取,读取后,将每行元素作为参数,调用 Float_Neg_tabs()函数检验每行文本是否存在小数、负数、除\t 以外的其他字符。若读取的数都为正数,且每行元素个数相同,且行数与列数相同,则调用 MagicSquareCalculate()函数计算读取的矩阵是否为幻方矩阵。
- 所调用的函数:
public static int Float_Neg_tabs(String Number);
功能:
用于检验读取到的每行元素是否存在非法数据,若字符串合法则返回 0,若字符串中含有负数,则返回 1,若字符串中含有非’\t’的字符,则返回 2,若字符串结尾为非’\t’字符,则返回 3,若字符串中含有小数,则返回 4.
实现过程:
对读取的字符串分析,若字符串最后一个字符为非’0’-‘9’且非’\t’,则返回 3,表明,当前字符串的结尾为非’\t’字符;对字符串每个字符检查,若存在字符大于’9’或者小于’0’,则检查是否为’.’或‘\t’或’-‘,若存在’-’符号,则表明存在负数,函数返回 1;若存在’.‘符号,则表明存在小数,函数返回 4;若该符不为以上三种符号,则存在其他非法字符,返回2;对以上检查完毕后,若无异常则,返回 0;
public static boolean MagicSquareCalculate(int[][] Num, int count)
*功能:*计算每行每列对角线元素和是否相同,若相同则返回 true,否则返回 false。
*实现过程:*首先计算第一行元素和,与每行、每列、写对角线元素和比较,若相等,则表明该矩阵为幻方矩阵,否则不为幻方矩阵。
3.1.4 generateMagicSquare()设计:
设计思路:
修改后的程序:
首先判断参数是否为负数或偶数,并抛出相应异常。若检验出参数为正奇数,则此时可以通过所给代码生成幻方矩阵。并将幻方矩阵通过 PrintWriter 写入到6.txt 中,并通过函数判断是否为幻方矩阵。源程序生成幻方矩阵思路(Siamese 方法):
1.首先将 1 放置在第一行中间;
2.顺序将 2,3,…等数依次放在右上方格中;
3.当右上方格出界的时候,由另一边进入;
4.当右上方格中已经填有数,则把数填入正下放的表格中;
5.按照以上步骤直到填写完所有方格。
流程图如下:
3.2 Turtle Graphics
Turtle Graphics 主要实现以下几个功能:
-
- 画正方形
-
- 计算正多边形内角角度
-
- 通过正多边形内角,计算正多边形的边数
-
- 画正多边形
-
- 已知当前对 y 轴正半轴的偏角,求从点 current 到点 target 需要旋转的角度
-
- 当前方向为 y 轴正半轴时,求从 xCoords 的点到 yCoords 的点需要旋转的角度的 List
-
- 当前方向为 x 轴正半轴时,求从点 A 到点 B 需要旋转的角度
-
- 在给定一些点的坐标的时候,求这些点的凸包的集合
-
- 绘画出个人作品
-
- 测试以上功能
3.2.1 Problem 3: Turtle graphics and drawSquare:
设计思路:
利用 turtle 的 forward 和 turn 方法,完成转动角度,向前移动等操作,画出边长,并确定多边形的内角。
过程:
使 turtle 首先向当前方向移动 sideLength 个单位,然后使用 turn 方法,向右转 90 度,再向当前方向移动 sideLength 个单位,再使用 turn 方法,向右转 90度,再向当前方向移动 sideLength 个单位,再向右转 90 度,再向当前方向移动sideLength 个单位,即可得到一个边长为 sideLength 的正方形。
运行结果:
该图形的运行结果如下,成功绘制出边长为 40 个单位的正方形。
3.2.2 Problem 5: Drawing polygons:
设计思路
-
calculateRegularPolygonAngle
利用公式即可求正多边形内角。Angle = 180 ∗ (sides − 2)/sides
-
drawRegularPolygon
使用已经实现的正多边形内角公式,即可计算出要绘制的正多边形的内角,
进行绘制。
过程
- calculateRegularPolygonAngle
利用公式很容易计算出所求的内角。 - drawRegularPolygon
利用已经实现的计算正多边形内角的公式,即很容易计算出内角,通过 turtle的 turn 方法,即可实现角度的转换,在每绘制一条边的时候,转动角度 180-angle。
运行结果:
3.2.3 Problem 6: Calculating Bearings
设计思路
- calculateBearingToPoint
通过斜率计算角度,即可计算出要旋转的角度 - calculateBearings
通过斜率计算角度,将所得到的角度保存到 List 中
过程
- calculateBearingToPoint
比较 currentX 与 targetX 的值
(1) 若 currentX 与 targetX 的值相等,则说明两点在直线 x=currentX 上。若 currentY>targetY,则说明此时需要将角度调整为 y 的负半轴,因此此时转动的角度为((540 - currentBearing) % 360 + 360) % 360
若 currentY < targrtY,则说明此时需要将角度调整为 y 的正半轴,因此此时转动的角度为((360 - currentBearing) % 360 + 360) % 360;由于 turtle 方法只能向右转动,通过加 360 并取余得到为正的角度。
(2) 若此时 currentX < targetX,则说明此时 current 在 target 左侧,即可通过斜率计算公式,和 Math 库函数中的 atan 函数,计算角度。与 x 轴正半轴角度为 angle = (Math.atan((double) (targetY - currentY) / (double) (targetX - currentX))) * 180 / Math.PI;如图所示,即可得到当前的与 x 轴的夹角,以靠近 y 的正半轴为正,靠近 y的负半轴为负,不难得出,实际需要转动的角度为((450 - angle - currentBearing) % 360 + 360) % 360同理,通过加 360 并取余得到为正的角度。
(3) 若此时 currentX>targetX,则说明此时 current 在 target 右侧,即可通过斜率计算公式,和 Math 库函数中的 atan 函数,计算角度。与 x 轴正半轴角度为 angle = (Math.atan((double) (targetY - currentY) / (double) (targetX - currentX))) * 180 / Math.PI;如图所示,即可得到当前的与 x 轴的夹角,以靠近 y 的正半轴为正,靠近 y的负半轴为负,不难得出,实际需要转动的角度为((630 - angle - currentBearing) % 360 + 360) % 360同理,通过加 360 并取余得到为正的角度。
- calculateBearings
新建变量 currentBearing,保存当前与 y 轴正半轴的角度,比较 xCoords.get(i)与 xCoords.get(i + 1)的值(i 从 0 开始到 n-2)
(1) 若 xCoords.get(i)=xCoords.get(i + 1)判断 yCoords.get(i) 与 yCoords.get(i + 1)的关系此时与 1 中(1)情况类似(详细分析在 1 中),若 yCoords.get(i)> yCoords.get(i + 1)element = ((540 - currentBearing) % 360 + 360) % 360;若 yCoords.get(i)< yCoords.get(i + 1)element = ((360 - currentBearing) % 360 + 360) % 360;
(2) 若 xCoords.get(i) < xCoords.get(i + 1)此时与 1 中(2)情况类似(详细分析在 1 中),element = ((450 - angle - currentBearing) % 360 + 360) % 360;
(3) 若 xCoords.get(i)>xCoords.get(i + 1)此时与 1 中(3)情况类似(详细分析在 1 中),element = ((630 - angle - currentBearing) % 360 + 360) % 360;此时 currentBearing+=element 并对 360 求余,保存旋转后的角度。并将 element元素添加到 List 中。当 xCoords 与 yCoords 中的元素遍历后,返回 List。
3.2.4 Problem 7: Convex Hulls
设计思路
利用 graham 凸包扫描算法,实现求凸包的功能。Graham 扫描的思想是先找到凸包上的一个点,然后从那个点开始按逆时针方向逐个找凸包上的点,实际上就是进行极角排序,然后对其查询使用。
过程
1.将 points 中的所有点,放在坐标系中,以最下面的点为坐标原点,对所有点进行平移;
2.求每个点与 O 点的连线与 x 轴正半轴所形成的夹角,并对其进行排序,若角度相同,则取与 O 点较近的元素,认为其较小,此时认为 O 点最小;
3.取步骤 2 中,所排列的集合中,取出最小的 3 个点,假设当前的方向一致为 X 正半轴,分别计算 point1 到 point2、point2 到 point3,需要转动的角度 angle1、angle2。由于第一次取出的是 O 点与最下角的点,因此第一次一定 有angle1 < angle2。此时将 point1、point2、point3 也存入栈内。
4.继续从排列好的集合中取出新的元素 point3,取出栈顶两个元素作为 point2和 point1,再将这两个元素压栈,若满足 angle1 < angle2,则将 point3 存在栈中。若 angle1 >= angle2,则进入步骤 5 中。
5.若 angle1 >= angle2,则先弹一次栈,该点不满足凸包的定义,再弹两次栈分别作为 point2 和 point1,此时计算 angle1 和 angle2,若满足 angle1 < angle2,则将point3 压栈,并进入步骤 4,否则继续进行步骤 5.
6.重复以上操作,直到 points 中所有元素都被遍历后,栈中的元素即为所求的凸包。
以下为具体实例:
3.2.4 Problem 8: Personal art
设计思路
利用循环,foreach 等操作,变换颜色,旋转角的度数,画出的图形的个数,完成一副具有个人色彩的绘画。
过程
首先利用 foreach 语句,取出 pencolor 中的所有颜色,用每个颜色的笔画一些团,利用循环取余等操作,完成在循环数不同时,绘画出不同图形的操作。
运行结果
3.2.5 Problem : Submitting
通过 git 命令
git add . 添加文件到本地库
git commit -m “TurtleSoup” 提交文件到本地库
git remote add origin git@github.com:ComputerScienceHIT/HIT-Lab1-1190200708.git 关联到远程库
git push origin master 推送到 github
3.3 Social Network
Social Network 主要实现了以下功能:
-
- 建立 java 版的数据结构,有向图。
-
- 并完成增加边、节点的操作。
-
- 广度搜索计算两个人之间最短的距离。
-
- 对输入进行检查,若不符合条件抛出相应的异常。
设计/实现 FriendshipGraph 类
设计思路
用一个 HashSet 保存有向图的节点,在每一个 Person 的对象中,设私有变量,保存每个人认识的人。通过 HashSet 实现类似邻接表的存储结构,并且能够避免添加重复等问题,查找效率较高。人与人之间距离应该用广度搜索,方便记录距离。
过程
设置两个私有变量,private HashSet<Person>personArrayList 保存有向图的每一个节点,即 Person;count 保存当前已经存入的人数。
设置一个构造函数,对 FriendshipGraph 初始化。
addVertex 函数用于添加顶点,将图中的每个人都保存到 personArrayList 中。
addEdge 函数用来添加边,若需要添加一条边,则调用 person1 的方法,将person2 的信息保存到 person1 的私有变量中。
getDistance 函数通过广度搜索获得 person1 与 person2 的之间的距离。
广度搜索步骤:
-
- 将 person1 保存到 HashSet<Person>Search 中;
-
- 将 person1 认识的人保存到 HashSet<Person>Search_Next 中;
-
- 将 Search 和 Search_Next 都保存到新的 HashSet<Person>Search_assist 中;
-
- 初始化 distance = 1;
-
- 若 Search_Next 中含有 person2,返回 distance,即为所求距离。否则将Search 清空,将 Search_Next 保存到 Search 中,在将 Search 中所有的人认识的人的 HashSet 中所有的元素添加到 Search_Next,再将 Search_Next中所有在 Search_assist 中保存的元素消除掉,再将所有 Search_Next 元素添加到 Search_assist 中。此时 distance++;
-
- 重复步骤 5,若 Search_Next 最终为空,则不存在距离,返回值为-1;否则,person1与person2之间存在路径,且 person1与person2距离为distance。
设计/实现 Person 类
设计思路
Person 中存在私有变量 HashSet<Person> friend,保存当前 Person 的朋友,并提供一个方法,用于添加当前 Person 的 friend。
过程
用一个私有变量 HashSet<Person> friend,保存当前 Person 的朋友;再用一个私有变量保存当前 Person 的姓名。设置一个构造函数,用所给字符串命名当前 Person。设置一个方法,用于给当前 Person 添加 friend。即可完成对每个人,及其好友的设定。
四、实验感受
针对以下方面的感受:
(1) Java 编程语言是否对你的口味?
(2) 关于 Eclipse IDE
(3) 关于 Git 和 GitHub
(4) 关于 CMU 和 MIT 的作业
(5) 关于本实验的工作量、难度、deadline
(6) 关于初接触“软件构造”课程
(1) Java 语言的语法与 C 语言和 C++语言很接近,使得学习起来比较容易。并且对 C++来说进行了简化和一定的提高,提供了丰富的类库和 API 文档,以及第三方开发包工具包,因此实际体验非常好。
(2) Eclipse 在外观设计方面较 IDEA 略有差距,但 eclipse 总体体验还不错。实际体验上可能 IDEA 的自动修复与智能提示等功能更加强大。
(3) GitHub 让我可以将自己的代码保存到网上,使得在任意终端都可以查看自己的代码,Git 使本地回退版本更加容易,总体体验很棒。
(4) CMU 与 MIT 的作业设计非常精妙,略有难度,但总体不错。
(5) 工作量略大,但是感觉报告内容过多,希望可以略微削减报告内容。Deadline 提前规划后还好。
(6) 了解了软件构造一些基本概念,对我写程序有很大益处
文档,以及第三方开发包工具包,因此实际体验非常好。
(2) Eclipse 在外观设计方面较 IDEA 略有差距,但 eclipse 总体体验还不错。实际体验上可能 IDEA 的自动修复与智能提示等功能更加强大。
(3) GitHub 让我可以将自己的代码保存到网上,使得在任意终端都可以查看自己的代码,Git 使本地回退版本更加容易,总体体验很棒。
(4) CMU 与 MIT 的作业设计非常精妙,略有难度,但总体不错。
(5) 工作量略大,但是感觉报告内容过多,希望可以略微削减报告内容。Deadline 提前规划后还好。
(6) 了解了软件构造一些基本概念,对我写程序有很大益处