哈工大软件构造lab1

  1. 实验目标概述

本次实验通过求解三个问题,训练基本 Java 编程技能,能够利用 Java OO 开发基本的功能模块,能够阅读理解已有代码框架并根据功能需求补全代码,能够为所开发的代码编写基本的测试程序并完成测试,初步保证所开发代码的正确性。另一方面,利用 Git 作为代码配置管理的工具,学会 Git 的基本使用方法。

  1. 基本的 Java OO 编程
  2. 基于 Eclipse IDE 进行 Java 编程
  3. 基于 JUnit 的测试
  4. 基于 Git 的代码配置管理

2.实验环境配置

2.1配置JDK

首先进入官网https://www.oracle.com/java/technologies/javase-downloads.html下载对应版本的jdk(注意实验要求的是jdk8或jdk11),可以选择免安装版或安装版。下载完成后解压压缩包,然后开始配置。

右键此电脑点击属性,打开高级系统设置:

 

然后点击环境变量,在系统变量中新建 JAVA_HOME 变量 ,变量值填写解压后jdk文件夹所在的目录

 

随后在变量path最后添加%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;(注意别落下分号)

 

  最后在系统变量中新建变量CLASSPATH,变量值填写   .;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar(注意开头的.别忘了)

 

运行cmd 输入 java -version (java 和 -version 之间有空格),若如图所示 显示版本信息 则说明安装和配置成功。

2.2安装IDE

在官网下载安装IDEA,设置JRE以及其他preferences。新建一个Workspace,建立项目、包以及java源代码,IDEA配置成功的结果如图。

2.3安装Git

在官网下载Git并安装,设置用户名、邮箱等信息,Git配置成功的结果如图。

 

2.4安装Junit

Junit是java的一种单元测试框架,可以理解为一个库,里面放着各种用来对程序的某一模块进行测试的方法,同时还附加了一个可视化窗口和错误提示。Junit的使用方式和java编程没有太多区别,基本上就是调用各种函数去测试你的代码,看看它们在给定的输入下是否能给出正确的输出。具体的测试类及方法可以上网参考,这里就不赘述了。

IDEA 一般默认安装了插件 JUnit,如下图所示:可在 settings 中的 Plugins 选项卡中的 Installed 一栏中搜索 JUnit 查看。

 

  在项目根目录下新建 Test 文件夹(文件名可以任意),并在菜单栏文件选项下的项目结构中选择 项目设置里的模块选项, 标记为该文件夹为 Test(默认为 Source),用以保存生成的测试类。标记后,自动生成的测试类都会自动保存在此文件夹下。

 

右键点击需要测试的类名(在代码栏中的类名) -> Go To -> Test,选择 "create new test...",打开生成界面。Testing Library 中可以选择junit版本,然后在下方选择需要测试的方法,点击ok即可自动在Test文件夹下生成测试类。

在这里给出你的GitHub Lab1仓库的URL地址。

GitHub Lab1仓库的URL地址:

“”

3.实验过程

请仔细对照实验手册,针对四个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但无需把你的源代码全部粘贴过来!)。

为了条理清晰,可根据需要在各节增加三级标题。

Magic Squares

Magic Squares即幻方,n阶幻方是一个正方形n×n个数(通常是不同的整数)的排列,使得所有行、所有列和两个对角线中的n个数总和为同一常数。

isLegalMagicSquare()

本题要求在main()函数中调用五次isLegalMagicSquare()函数,将5个文本文件名分别作为参数输入进去,看其是否得到正确的输出(true,false)。首先打开文件

//打开文件
        FileReader fr = new FileReader(fileName);
        fileName = fileName.split("/")[3];
        BufferedReader br = new BufferedReader(fr);

读取数据并判断是否符合格式,在while循环中读取时检测格式

//Square二维数组存储MagicSquare
        List<int[]> Square = new ArrayList<>();
        //读取MagicSquare
        String[] nums = br.readLine().split("\t");
        int col = nums.length;
        while (true) {
            if (nums.length != col) {
                for (int i = 0; i < nums.length; i++) {
                    if (nums[i].contains(" ")) {
                        System.out.println(fileName + "没有用tab分隔");
                        return false;
                    }
                    if (nums[i].contains("-") || nums[i].contains(".") || nums[i].equals("0")) {
                        System.out.println(fileName + "输入数字中含非正整数");
                        return false;
                    }
                }
                System.out.println(fileName + "并非矩阵");
                return false;//行数不相等的情况
            }
            int[] arr = new int[col];
            for (int i = 0; i < col; i++) {
                if (nums[i].contains(" ")) {
                    System.out.println(fileName + "没有用tab分隔");
                    return false;
                }
                if (nums[i].contains("-") || nums[i].contains(".") || nums[i].equals("0")) {
                    System.out.println(fileName + "输入数字中含非正整数");
                    return false;
                }
                arr[i] = Integer.parseInt(nums[i]);
            }
            Square.add(arr);
            try {
                nums = br.readLine().split("\t");
            } catch (NullPointerException e) {
                break;
            }
        }
        //关闭reader
        fr.close();
        br.close();
        if (Square.size() != col) {
            System.out.println(fileName + "行列数不相等");
            return false;
        }

之后判断该矩阵是否是幻方

检查行列:

//检查各行的和是否相等
        int sum = Arrays.stream(Square.get(0)).sum();//获取第一行的和
        //检查行列
        for (int i = 0; i < col; i++) {
            //检查行
            if (sum != Arrays.stream(Square.get(i)).sum()) {
                System.out.println(fileName + "第" + (i + 1) + "行和不相等");
                return false;
            }
            //检查列
            int finalI = i;
            if (sum != Square.stream().mapToInt(a -> a[finalI]).sum()) {
                System.out.println(fileName + "第" + (i + 1) + "列和不相等");
                return false;
            }
        }

检查斜边:

//检查对角线
        int left = 0;
        int right = 0;
        for (int i = 0; i < col; i++) {
            left += Square.get(i)[i];
            right += Square.get(col - i - 1)[i];
        }
        if (left != sum || right != sum) {
            System.out.println(fileName + "斜行不相等");
            return false;
        }

​​​​​​​generateMagicSquare()

 首先计算初始位置(0,n/2),赋值为1,之后每次取当前位置的右上角的位置,设置的值每次加1,如果当前行是第一行,则下一行为最后一行,如果当前列是最右边的列,则下一次取左边第一列,如此重复平方次,就对整个矩阵赋完值了,且满足幻方定义。

public static boolean generateMagicSquare(int n) throws IOException {
        try{
            try {
                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;//数字 1 放在首行最中间的格子中
                    //向右上角斜行,依次填入数字
                    if (i % n == 0)
                        row++;//1~n填过一遍后换行
                    else {
                        if (row == 0)
                            row = n - 1;//如果右上方向出了上边界,就以出框后的位置为基准,将数字竖直降落至底行对应的格子中;
                        else
                            row--;//向右上角斜行,依次填入数字
                        if (col == (n - 1))
                            col = 0;//同上,向右出了边界,就以出框后的位置为基准,将数字平移至最左列对应的格子中;
                        else
                            col++;//向右上角斜行,依次填入数字
                    }
                }
                //输出生成的矩阵
                for (i = 0; i < n; i++) {
                    for (j = 0; j < n; j++)
                        System.out.print(magic[i][j] + "\t");
                    System.out.println();
                }
                FileOutputStream fo = new FileOutputStream("src/P1/txt/6.txt");
                OutputStreamWriter osw = new OutputStreamWriter(fo);
                for (int k = 0; k < magic.length; k++) {
                    for (int l = 0; l < magic.length; l++) {
                        osw.write(magic[k][l] + "");
                        if (l != magic.length - 1)
                            osw.write("\t");
                    }
                    if (k != magic.length - 1)
                        osw.write("\n");
                    osw.flush();
                }
            }
            catch (NegativeArraySizeException e){
                System.out.println("Error:输入的n不能为负数");
                return false;
            }
        }
        catch (ArrayIndexOutOfBoundsException e){
            System.out.println("Error:输入的n不能为偶数");
            return false;
        }
        return true;
    }

Turtle Graphics

该任务需要我们clone已有的程序后,利用turtle按照要求画图,其中需要利用几何知识设计一些函数简化编程,最后可以发挥想象力进行Personal Art。首先分析turtle的package组成,了解类成员。

​​​​​​​Problem 1: Clone and import

  • 打开目标存储文件夹 右键点击Git Bash
  • 入git clone

https://github.com/ComputerScienceHIT/Lab1-1183710109.git

  • 在IDE中打开即可

Problem 3: Turtle graphics and drawSquare

        该函数需要实现:已知边长,画出边长为指定数值的正方形。参数是海龟对象turtle和编程sidelength。

        首先将海龟画笔设置为黑色。然后执行4次的前进sidelength长度、转完90度,即可完成一个边长为sidelength的正方形。下图是边长为40的正方形:

 

public static void drawSquare(Turtle turtle, int sideLength) {
        for (int i = 0; i < 4; i++) {
            turtle.forward(sideLength);
            turtle.turn(90);
        }
    }

Problem 5: Drawing polygons

        该问题首先希望已知正多边形边数的情况下计算正多边形的内角度。根据几何知识可以推导得计算内角的函数:

public static double calculateRegularPolygonAngle(int sides) {
        return ((double)sides-2)*(180/(double)sides);
    }

Junit测试结果如下:

 

        该问题还希望已知正多变型得边数和边长画出一个正多边形。参照画正方形的方法,可以先前进sidelength,再使海龟旋转一个角度,执行“边数”次。其中,这个角度是正多边形内角的补角,利用calculateRegularPolygonAngle的功能计算出多边形内角,再用180°减去这个值即可。边长为20的正六边形效果如下:

 

public static void drawRegularPolygon(Turtle turtle, int sides, int sideLength) {
        double angle = calculateRegularPolygonAngle(sides);
        for (int i = 0; i < sides; i++) {
            turtle.forward(sideLength);
            turtle.turn(180-angle);
        }
    }

 

 

 

Problem 6: Calculating Bearings

        该问题首先希望解决,已知起点和当前朝向角度,想知道到终点需要转动的角度。例如,如果海龟在(0,1)朝向 30 度,并且必须到达(0,0)它必须再转动 150 度。只需要考虑currentY==targetY的情况后计算atan与目前朝向的差值即可。

代码:

public static double calculateBearingToPoint(double currentBearing, int currentX, int currentY, int targetX, int targetY) {
        if(targetY==currentY)
            return targetX>currentX?(90-currentBearing+360)%360:(270-currentBearing+360)%360;
        else {
            return (Math.atan((double) (targetX - currentX) /(double) (targetY - currentY)) * 180 / Math.PI - currentBearing + 360) % 360;
        }
    }

junit测试:

        基于上一个问题,此时有若干个点,想知道从第一个点开始到第二个点,再从第二个点到第三个点……以此类推每次转向的角度。将“起点”选为第一个点循环n-1次(n为点的个数)每次将第i+1号点设置为“终点”,通过上一个函数计算旋转角度并存储到List中将下一次的“起点”用当前“终点”更新,继续循环结束后返回list即可

代码:

public static List<Double> calculateBearings(List<Integer> xCoords, List<Integer> yCoords) {
        List<Double> bearings = new ArrayList<>();
        int x = xCoords.get(0),y = yCoords.get(0);
        double currentD = 0;
        for (int i = 0; i < xCoords.size()-1; i++) {
            int targetx = xCoords.get(i+1),targety = yCoords.get(i+1);
            Double bear = calculateBearingToPoint(currentD,x,y,targetx,targety);
            bearings.add(bear);
            currentD = (bear+currentD)%360;
        }
        return bearings;
    }

junit测试:

 

 

      1. Problem 7: Convex Hulls

        给定平面上一堆点集,输出位于凸包上的点,我们采用Gift−Wrapping算法解决。任意凸包上的点以该点建立一个极角坐标系,该点连结其它所有点的极角中,该点逆时针方向的第一凸包点到该点极角最小,例如P0,到所有点的极角中P0P1极角最小。算法中首先找到最左边的点,这个点必然在凸包上,然后计算该点连接点极角最小的,找到P1后,就可以从P1开始,接着顺次找到P2,又以P2为起点依次循环直到返回第一节点。

        因为每次的起点都是上次找到的凸包点,因此外层循环的复杂度为O(H),H为凸包上的点,内层循环每次都会全部遍历点,因此时间复杂度为 O(n) ,因此总的是间复杂度为 O(nH) ,在一般情况下 凸包上的点的期望为logn ,算法复杂度为 O(nlogn) ,极端情况下,如下所示,所有点都在类似圆弧上的话,外层循环也是n,因此会达到O(n^2)

代码实现:

public static Set<Point> convexHull(Set<Point> points) {
        if (points.size() <= 3) return points;
        Set<Point> convexHullPoints = new HashSet<Point>();
        Point a = new Point(Double.MAX_VALUE, Double.MAX_VALUE);
        for (Point i : points) {
            if (i.x() < a.x() || (i.x() == a.x() && i.y() < a.y()))
                a = i;
        }
        Point curPoint = a, minPoint = null, lastPoint = a;
        double x1 = 0.0, y1 = -1.0;
        do {
            convexHullPoints.add(curPoint);
            double minTheta = Double.MAX_VALUE, x2 = 0.0, y2 = 0.0;
            for (Point i : points) {
                if ((!convexHullPoints.contains(i) || i == a) && (i != lastPoint)) {
                    double x3 = i.x() - curPoint.x(), y3 = i.y() - curPoint.y();
                    double Theta = Math
                            .acos((x1 * x3 + y1 * y3) / Math.sqrt(x1 * x1 + y1 * y1) / Math.sqrt(x3 * x3 + y3 * y3));
                    // System.out.println(i.x() + " " + i.y() + " " + Theta);
                    if (Theta < minTheta || (Theta == minTheta && x3 * x3 + y3 * y3 > Math.pow(curPoint.x() - minPoint.x(), 2)
                            + Math.pow(curPoint.y() - minPoint.y(), 2))) {
                        minPoint = i;
                        minTheta = Theta;
                        x2 = x3;
                        y2 = y3;
                    }
                }
            }
            x1 = x2;
            y1 = y2;
            lastPoint = curPoint;
            curPoint = minPoint;
            // System.out.println(curPoint.x() + " " + curPoint.y());
        } while (curPoint != a);
        return convexHullPoints;
    }

junit测试:

 

​​​​​​​Problem 8: Personal art

运用%取模生成了富有规律的图案:

 

public static void drawPersonalArt(Turtle turtle) {
        turtle.forward(120);
        Map<Integer,String> colors = new HashMap<>();
        colors.put(1,"PINK");
        colors.put(2,"ORANGE");
        colors.put(3,"YELLOW");
        colors.put(4,"GREEN");
        colors.put(5,"CYAN");
        colors.put(6,"BLUE");
        colors.put(0,"MAGENTA");
        for (int i = 0; i < 1000; i++) {
            turtle.color(PenColor.valueOf(colors.get(i%7)));
            drawRegularPolygon(turtle,50,1);
            turtle.turn(180);
            if(i%20==0){
                turtle.forward(120);
                drawRegularPolygon(turtle,5,120);
                turtle.turn(180);
                drawRegularPolygon(turtle,5,120);
                turtle.forward(120);
                turtle.turn(180);
                for (int j = 0; j < 100; j++) {
                    drawRegularPolygon(turtle,4,10);
                    turtle.forward(20);
                    turtle.turn(3.6*6);
                }
            }
            turtle.turn(180);
            turtle.forward(i%20 + i%40);
            turtle.turn(3.6*4);
        }
    }

 

​​​​​​​Submitting

git add *   ->   git commit -m “”  ->   git push提交到Lab1仓库

​​​​​​​Social Network

利用图结构实现一张人际关系网

​​​​​​​设计/实现FriendshipGraph

Friendship中定义两个成员变量:

private Map<String,List<Person>> friendList:存储关系图的邻接表表示

private Set<String> names:存储现有的人名

成员方法增加getter和setter,其他的是给定的方法。

 

给定的方法实现:

1.getDistance:运用bfs算法搜索从一个人通过朋友到达另一个人的最近 距离,当两人中有人不存在时报错并退出,bfs算法的原理不再赘述, 代码实现如下:

public int getDistance(Person rachel, Person ross) {
        if(!friendList.containsKey(rachel.getName())||!friendList.containsKey(ross.getName())){
            System.out.println("不能计算不存在的人");
            System.exit(0);
        }
        if(rachel.getName().equals(ross.getName())) return 0;
        Set<String> visited = new HashSet<>();
        Queue<Person> q = new ArrayDeque<>();
        String target = rachel.getName();
        int distance = 0;
        q.add(rachel);
        while(!q.isEmpty()){
            Person poll = q.poll();
            visited.add(poll.getName());
            List<Person> people = friendList.get(poll.getName());
            if(poll.getName().equals(ross.getName())) return distance;
            if(target.equals(poll.getName())){
                target = people.get(people.size()-1).getName();
                distance++;
            }
            for (Person i :people) {
                if(!visited.contains(i.getName())) q.add(i);
            }
        }
        return -1;
    }

2.addEdge:运用邻接表实现关系添加,当两人中有人不存在或重复时报错并退出。代码如下:

public void addEdge(Person rachel, Person ross) {
        if(!friendList.containsKey(rachel.getName())||!friendList.containsKey(ross.getName())){
            System.out.println("不能添加不存在的人");
            System.exit(0);
        }
        List<Person> people = friendList.get(rachel.getName());
        if(people.contains(ross)){
            System.out.println("不能添加已有的朋友");
            System.exit(0);
        }
        people.add(ross);
    }

 

3.addVertex:运用List邻接表实现顶点添加,当有人重复时报错并退出。代码如下:

public void addVertex(Person rachel) {
        if(names.contains(rachel.getName())){
            System.out.println("不能添加重复的人");
            System.exit(0);
        }
        names.add(rachel.getName());
        friendList.put(rachel.getName(),new ArrayList<>());
    }

 

​​​​​​​设计/实现Person

成员变量name

给出构造函数和getter,setter方法

public class Person {
    private String name;
    //构造函数
    public Person(String name) {
        this.name = name;
    }
    //getter
    public String getName() {
        return name;
    }
    //setter
    public void setName(String name) {
        this.name = name;
    }
}

 

​​​​​​​设计/实现客户端代码main()

同给出的模板:

public static void main(String[] args){
        FriendshipGraph graph = new FriendshipGraph();
        Person rachel = new Person("Rachel");
        Person ross = new Person("Ross");
        Person ben = new Person("Ben");
        Person kramer = new Person("Kramer");
        graph.addVertex(rachel);
        graph.addVertex(ross);
        graph.addVertex(ben);
        graph.addVertex(kramer);
        graph.addEdge(rachel, ross);
        graph.addEdge(ross, rachel);
        graph.addEdge(ross, ben);
        graph.addEdge(ben, ross);
        System.out.println(graph.getDistance(rachel, ross));
        //should print 1
        System.out.println(graph.getDistance(rachel, ben));
        //should print 2
        System.out.println(graph.getDistance(rachel, rachel));
        //should print 0
        System.out.println(graph.getDistance(rachel, kramer));
        //should print ‐1
    }

 

 

​​​​​​​设计/实现测试用例

整体如下:给出了FriendshipGraph的各方法测试

测试方法:测试getDistance时写了simple和complex两个测试方法,分别测试在简单图和复杂图上的正确性

@Test
    public void testGetDistanceSimple() {
        FriendshipGraph graph = new FriendshipGraph();
        Person rachel = new Person("Rachel");
        Person ross = new Person("Ross");
        Person ben = new Person("Ben");
        Person kramer = new Person("Kramer");
        graph.addVertex(rachel);
        graph.addVertex(ross);
        graph.addVertex(ben);
        graph.addVertex(kramer);
        graph.addEdge(rachel, ross);
        graph.addEdge(ross, rachel);
        graph.addEdge(ross, ben);
        graph.addEdge(ben, ross);
        assertEquals(1,graph.getDistance(rachel, ross));
        assertEquals(2,graph.getDistance(rachel, ben));
        assertEquals(0,graph.getDistance(rachel, rachel));
        assertEquals(-1,graph.getDistance(rachel, kramer));
    }
    @Test
    public void testGetDistanceComplex(){
        FriendshipGraph fg = new FriendshipGraph();
        fg.addVertex(new Person("A"));
        fg.addVertex(new Person("B"));
        fg.addVertex(new Person("C"));
        fg.addVertex(new Person("D"));
        fg.addVertex(new Person("E"));
        fg.addVertex(new Person("F"));
        fg.addVertex(new Person("G"));
        fg.addVertex(new Person("H"));
        fg.addVertex(new Person("I"));
        fg.addVertex(new Person("J"));
        fg.addEdge(new Person("A"),new Person("B"));
        fg.addEdge(new Person("B"),new Person("A"));
        fg.addEdge(new Person("D"),new Person("B"));
        fg.addEdge(new Person("B"),new Person("D"));
        fg.addEdge(new Person("A"),new Person("D"));
        fg.addEdge(new Person("D"),new Person("A"));
        fg.addEdge(new Person("C"),new Person("D"));
        fg.addEdge(new Person("D"),new Person("C"));
        fg.addEdge(new Person("D"),new Person("E"));
        fg.addEdge(new Person("E"),new Person("D"));
        fg.addEdge(new Person("C"),new Person("F"));
        fg.addEdge(new Person("F"),new Person("C"));
        fg.addEdge(new Person("F"),new Person("G"));
        fg.addEdge(new Person("G"),new Person("F"));
        fg.addEdge(new Person("E"),new Person("G"));
        fg.addEdge(new Person("G"),new Person("E"));
        fg.addEdge(new Person("H"),new Person("I"));
        fg.addEdge(new Person("I"),new Person("H"));
        fg.addEdge(new Person("J"),new Person("I"));
        fg.addEdge(new Person("I"),new Person("J"));
        assertEquals(2,fg.getDistance(new Person("A"),new Person("E")));
        assertEquals(1,fg.getDistance(new Person("A"),new Person("D")));
        assertEquals(3,fg.getDistance(new Person("A"),new Person("G")));
        assertEquals(2,fg.getDistance(new Person("D"),new Person("F")));
        assertEquals(3,fg.getDistance(new Person("B"),new Person("F")));
        assertEquals(2,fg.getDistance(new Person("J"),new Person("H")));
        assertEquals(0,fg.getDistance(new Person("I"),new Person("I")));
        assertEquals(-1,fg.getDistance(new Person("D"),new Person("J")));
        assertEquals(-1,fg.getDistance(new Person("C"),new Person("I")));
        assertEquals(-1,fg.getDistance(new Person("F"),new Person("H")));
    }

测试另外两个方法就很正常 

@Test
    public void testAddVertex()  {
        FriendshipGraph fg =new FriendshipGraph();
        fg.addVertex(new Person("ross"));
        //fg.addVertex(new Person("ross"));
        assert(fg.getNames().contains("ross")&&fg.getFriendList().containsKey("ross"));
    }
    @Test
    public void testAddEdge(){
        FriendshipGraph fg =new FriendshipGraph();
        Person ross = new Person("ross");
        Person bob = new Person("bob");
        fg.addVertex(ross);
        fg.addVertex(bob);
        fg.addEdge(ross,bob);
        //fg.addEdge(ross,bob);
        //fg.addEdge(ross,new Person("Jack"));
        assert (fg.getFriendList().get("ross").contains(bob));
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值