2020年春季学期
计算机学院《软件构造》课程
2 实验环境配置
Eclipse,JDK8,Git,Junit4
在假期未开课刚学习Java语言下载JDK时,网速特别慢,凌晨下载时速度稍有提升,才下载成功。又经过网上查询环境变量配置的方法,由于忽略了一些东西,而且电脑中又有老师发送的JDK8和自己下载的JDK13,尝试了几次之后环境变量配置成功。
3 实验过程
3.1 Magic Squares
任务理解:编写isLegalMagicSquare()函数,使用文件名为参数判断是否为幻方。将generateMagicSquare产生的magic square写入文件\src\P1\txt\6.txt中;并调用isLegalMagicSquare()函数验证生成的矩阵是否为幻方。
3.1.1 isLegalMagicSquare()
首先通过查阅资料找到合适的读取文件的方式,读取作为参数的文件名。按行读取矩阵。
使用HashMap<Integer, String[]> words = new HashMap<Integer,String[]>();
创建散列表,将int row与字符串组成的数组对应起来;
String[] word = line.split("\t");// word[]是一行的所有数组成的字符串数组
以‘\t’分割矩阵的一行,将一行的n个数放置到有n个字符串的 word数组中;
然后通过查阅资料使用遍历字符串的方法判断是否有除‘-’ ‘.’以外的非法字符,判断是否各行的数组长度相等,若不相等则输出不是矩阵。然后判断行列数是否相等,列数即为每行数组的长度,行数在分割矩阵的时候已进行计算。
int num = Integer.valueOf(words.get(j)[i]);// 把字符串强制转换为数字
将word数组中每一个字符串元素强制转换成数字,判断num是否大于0,且为整数。
int[] row_sum = new int[row];
int[] col_sum = new int[col];
int[] dia_sum = new int[2];
定义三个数组存放行,列,对角线的和,在求出每一行的和就与第一行的和进行比较,求出每一列的和就与第一列的和进行比较
以下为求正对角线之和,相邻两个对角线数的位置关系是横纵坐标均加一,求反对角线也类似
for (i = 0; i < row; i++, j++)
dia_sum[0] = dia_sum[0] + Integer.valueOf(words.get(i)[j]);
比较dia_sum[1]和dia_sum[0],最后最后比较row_sum[0],col_sum[0],dia_sum[0];
在上述计算比较之后若函数没有返回false,则说明该矩阵就是幻方。
3.1.2 generateMagicSquare()
按步骤给出你的设计和实现思路/过程/结果。
public static boolean generateMagicSquare(int n) throws IOException {
if(n<=0)//n为负数,输出信息,退出
{
System.out.println("输入的n为负数,不符合要求!");
System.exit(1);
}
if(n%2==0)//n为偶数,输出信息,退出
{System.out.println("输入的n为偶数,不符合要求!");
System.exit(1);
}
int magic[][] = new int[n][n];
int row = 0, col = n / 2, i, j, square = n * n;
for (i = 1; i <= square; i++) {
magic[row][col] = i;//赋值一个矩阵元素
if (i % n == 0)//下一个位置是已经被赋值过的,不能重复赋值
row++;//所以要开始赋值下一个斜道
else {
if (row == 0)//元素赋值到了第一行,
row = n - 1;//下一次赋值转到最后一行
else
row--;//否则行数减1
if (col == (n - 1))//假如元素赋值到最后一列,
col = 0;//下一次赋值转到第一列
else
col++;//否则列数加1
}
}
File f=new File("src/P1/txt/6.txt");
Writer print=new FileWriter(f);//第二个参数为空,所以每次写入文件时清空原有内容
for (i = 0; i < n; i++) {//输出矩阵
for (j = 0; j < n; j++)
{System.out.print(magic[i][j] + "\t");
print.write(magic[i][j]+"\t");
}
System.out.println();
print.write("\n");
}
print.close();
return true;
}
3.2 Turtle
Graphics
补全函数满足要求的功能,练习List,Set等结构,初步接触Junit测试.
3.2.1 Problem 1: Clone and import
https://github.com/rainywang/Spring2020_HITCS_SC_Lab1/tree/master/P2来fork代码到自己的github账号中,然后在自己的账号中clone代码,在浏览器中下载代码。打开Git Bash,通过网络查找命令行git init使用方式来将自己本地的一个文件夹作为本地库,通过git remote add origin https://github.com/ComputerScienceHIT/Lab1-1180300412.git命令来将本地库与远程库进行关联。
3.2.2 Problem 3: Turtle graphics and drawSquare
画正方向四个循环将四个边画出来,每次循环执行turtle.forward(sideLength);
turtle.turn(90);
3.2.3 Problem 5: Drawing polygons
通过多边形的内角公式,double angle=(double)(sides-2)*180/sides;计算出多边形的内角大小,画多边形使用180-之前算出的内角大小就得到海龟需要拐的角度了。
还有根据内角大小返回边数的函数实现,根据公式也能得到,但是由于内角大小是double,而边数是int,所以数据将会有舍入(例如6.998强制类型转换会返回6),在Junit测试时会有不通过,使用以下代码处理
double sides= (double)360.0/(180-angle);//由内角和公式得到
if(sides-(int)sides>=0.999)//防止6.9998这种类型被强制转换成6
return (int)sides+1;
return (int)sides;
3.2.4 Problem 6: Calculating Bearings
通过查找资料找到Java中的库函数Math.atan2,计算两点连线与x轴正方向的夹角的弧度值
double angle=Math.atan2(targetY-currentY, targetX-currentX);//两点连线与X轴正方向的夹角弧度值
angle=90-Math.toDegrees(angle)-currentBearing;//两点连线与Y轴的夹角再减去初始的方向角度
其次使用Math.toDegrees来将弧度值转换为角度值
对于输入x,y值的列表的,初始的角度angle设置为0,然后通过前两个点求出返回列表的第一个元素,然后使用atan2函数就可以求出从第二个点到第三个点时的初始朝向角度,循环执行就得到结果了。
Bearings.add(calculateBearingToPoint(angle,xCoords.get(i),yCoords.get(i),xCoords.get(i+1),yCoords.get(i+1)));
angle=Math.toDegrees(Math.atan2(yCoords.get(i+1)-yCoords.get(i), xCoords.get(i+1)-xCoords.get(i)));
i++;
3.2.5 Problem 7: Convex Hulls
凸包算法,可以求出纵坐标最小的点,该点一定在凸包上,然后根据以该点为原点,连接其余点,求出极角最小的点,将上述两点加入到凸点集中,然后以第二个点为原点重复操作,然而以上述思路中,我编写的凸包代码没能全部通过测试
3.2.6 Problem 8: Personal art
通过switch(i%8)语句在每次画完一个多边形后能够改变一种颜色,通过for循环,每次画出的多边形边长增加4个单位,得到蜗壳形状。
3.2.7 Submitting
通过git status 语句来查看文件的变化情况,然后使用git add *.*语句将文件添加,使用git commit语句提交,最后使用git push origin master来推送修改后的文件到远程库。完成上述操作后,可以登录github账号查看到刚刚推送过去的新版本。
3.3 Social Network
对有向图进行操作,添加点和边,查找给定的两点之间的最短距离。另外添加防止重名的功能。练习编写Junit测试用例,对代码进行测试。
3.3.1 设计/实现FriendshipGraph类
使用一个字符串的列表来存储所有人的名字,在使用addVertex时就可以通过names来看是否已经存在这个名字;然后使用散列表来将每个人来和他的朋友列表对应起来。
List names=new ArrayList();//姓名列表,增加名字时防止重名,且可以得到当前的人数
HashMap<Person,LinkedList>
people=new
HashMap<Person,LinkedList>();//每个人对应了他的朋友列表
addVertex函数:使用if(names.contains(person.getname()))来判断加入的这个人的名字是否已经有人用了,假如这个名字在名单names中不存在,那么创建一个朋友列表存储加入元素person的朋友,将person加入到他自己的朋友列表中,然后将person的名字加入到names中,然后将person和他的朋友列表put进people散列表中
LinkedList a=new LinkedList();
a.add(person);
names.add(person.getname());
people.put(person,a);
addEdge函数:if(names.contains(a.getname())&&names.contains(b.getname()))
people.get(a).add(b);
检查加入的两个人的名字是不是存在,假如存在的话在a的朋友列表中加入b。
getDistance:Queue queue=new LinkedList();
HashMap<Person,Integer> distance=new HashMap<Person,Integer>();//每个人离a的距离
创建队列和散列表,散列表将每个人与他和原点的距离对应起来
使用广度优先搜索,从原点开始进行搜索,对所有的点像层序遍历一样,第二层的点是第一层的点的朋友,到原点距离为distance.get(原点)+1;,然后将第二层的点入队,将第二层的点和他们距原点的距离置入到散列表distance中。然后将队头出队,遍历其朋友,这时已经是第三层了,第三层的点到原点距离为第二层的距离加一,重复上述操作,在遍历时检查点是否是已经遍历过的点(在distance散列表中能够查找到对应键)如果是已经检查过的点,则开始下一个点;以及检查点是不是终点,如果是的话,返回该层与原点的距离。当弹出的队头的朋友遍历完之后,再弹出一个队头元素,对其朋友列表同样进行遍历。重复操作,直到队列为空或函数已经返回。
如果上述操作全部完成,则说明终点与原点不连通,返回-1.
第二层的点和第一层的原点距离为0+1,第三层的点和第一层的距离(0+1)+1,第四层的点和第一层距离为[(0+1)+1]+1一直递推就可以求出图中任意一个元素和原点的距离。
3.3.2 设计/实现Person类
private
String name;
public Person(String name)
{
this.name=name;
}
public String getname()
{
return name;
}
老师提示在起名字的时候不会知道是否重名,只有在加入到graph.names中时才能知道,所以在person中不设置判断重名的语句。
3.3.3 设计/实现客户端代码main()
设置十个点,加入边,其中有些点之间有不止一条路,第十个点设置为与其他点不连通。分别输出连通点之间的距离,不连通点之间的距离,中间有不同长度的路的两点间的距离
3.3.4 设计/实现测试用例
addVertexTest: Person a 在执行addVertex之前,在names中判断是否有a.getname();调用addVertex同时捕捉异常,如果出现异常则assertEquals(true,false);构造一个错误出来。然后再在names里面查看是否有a的名字, assertEquals(true,graph.names.contains(a.getname()));重名检测使用Junit测试有困难,在调试时设置过两个同名的人,能够识别出来并退出程序。
addEdgeTest:
在未进行addEdge操作前查看a的朋友列表是否含有b,b的朋友列表是否有a,正确结果是都没有。assertEquals(false,graph.people.get(a).contains(b));
assertEquals(false,graph.people.get(b).contains(a));
然后进行 graph.addEdge(a,b);
assertEquals(true,graph.people.get(a).contains(b));
assertEquals(false,graph.people.get(b).contains(a));
再执行graph.addEdge(b,a);,此时b的朋友列表中应该有a了。
assertEquals(true,graph.people.get(b).contains(a));
getDistanceTest:定义了十个人,其中a10与其他点没有连通,其余有些点之间有不止一条路连接,且路的长度不同。
assertEquals(0,graph.getDistance(a1, a1));//自己和自己距离为0
assertEquals(3,graph.getDistance(a1, a8));
assertEquals(3,graph. getDistance(a6, a7));
assertEquals(2,graph.getDistance(a7, a4));
assertEquals(-1,graph.getDistance(a3, a10));//a10与a3不连通