软件构造——Lab1实验总结

一. 幻方问题

幻方:一个n×n矩阵,其所有行、所有列和两个对角线中的n个数字和为一个常数

要求1:判断给出的矩阵是否为幻方
要求2:针对给出的使用罗伯法生成幻方的代码,画出程序流程图

要求1:

首先,我需要读入文件数据,并将其存入一个二维数组中。

读取方法:使用BufferedReader类逐行读取文件,并使用line.split("\t")方法分割字符,存入一个String[ ]数组中,再通过Integer.valueOf(substring)将每个字符转化为数字,存入二维数组。

BufferedReader reader = new BufferedReader(new FileReader(fileName));
	String line;
	while ((line = reader.readLine()) != null) {
		String[] elements = line.split("\t");

存入数组后,遍历数组的每一行每一列每一个对角线,判断是否相等,若不相等,返回false,若均相等,返回true

		// 判断是否为 magic square
        int n = matrix.length;
        int sum = 0;
        // 计算第一行的和
        for (int j = 0; j < n; j++) {
            sum += matrix[0][j];
        }
        // 验证每行、每列以及对角线的和是否相等
        for (int i = 0; i < n; i++) {
            int rowSum = 0;
            int colSum = 0;
            for (int j = 0; j < n; j++) {
                rowSum += matrix[i][j];
                colSum += matrix[j][i];
            }
            if (rowSum != sum || colSum != sum) {
                return false;
            }
        }
        // 验证主对角线的和
        int diagSum1 = 0;
        for (int i = 0; i < n; i++) {
            diagSum1 += matrix[i][i];
        }
        if (diagSum1 != sum) return false;
        // 验证副对角线的和
        int diagSum2 = 0;
        for (int i = 0; i < n; i++) {
            diagSum2 += matrix[i][n - 1 - i];
        }
        return diagSum2 == sum;

错误处理:在我的程序中,将输入的数据以 \t 分隔,如果文件中的数据以空格分隔,会将带有空格字符的数据读入String[ ]数组中,因此,只需在每一次分隔后判断字符串中是否包含空格字符,即可做出未以\t分隔的错误处理。

		for (String element : elements) {
                if (element.contains(" ")) {
                    System.out.print("文件" + fileName + "错误:数字之间并非使用'\\t'分割\t");
                    return false;
                }

错误处理:在判断完字符串是否以 \t 分割后,通过正则表达式判断输入数据是否为正整数

		String regex = "\\d+";
                if (!Pattern.matches(regex, element)) {
                    System.out.print("文件" + fileName + "错误:矩阵中的数字必须为正整数\t");
                    return false;
                }

错误处理:在第一次向矩阵中写入数据中,会记录第一行的长度length,在后续读每一行时,都会比较length与element.length是否相等,且在矩阵读完后,还会判断length与row是否相等

			// 判断是否为方阵
            if (!(length == elements.length)) {
                System.out.print("文件" + fileName + "错误:不是一个n×n矩阵\t");
                return false;}
			if (!(length == row)) {
           		System.out.print("文件" + fileName + "错误:不是一个n×n矩阵\t");
            	return false;
           }

要求2:

程序流程图:
在这里插入图片描述

二. Turtle Graphics

在这个任务中,我需要用老师给出的Java海龟图形代码,来完成TurtleSoup.java类中未完成的一系列方法,在每完成一个方法后,需要用Junit调试,并通过git完成add、commit、push等操作。 这里,我选择一些难度较高的问题进行总结

Problem 6 :Calculating Bearings

calculateBearingToPoint:

设计思路:首先计算出目标方向的方位角,再用该角度减去初始方位角得到角度差,若相减为负数,则加上360°

首先,计算当前位置到目标位置的相对坐标偏移量,可表示为一个向量。

		int deltaX = targetX - currentX;
        int deltaY = targetY - currentY;

使用反正切函数计算向量相对正x轴方向的方位角,并将弧度转化为角度

		double relativeBearing = Math.atan2(deltaY, deltaX);
  		double degreesBearing = Math.toDegrees(relativeBearing);

随后,将方位角转化成以北为基准,顺时针为正方向的角度

		degreesBearing = 90.0 - degreesBearing;
		if(degreesBearing < 0) degreesBearing += 360;

最后,计算角度差

		double angleDifference = degreesBearing - currentBearing;
        if (angleDifference < 0) {
            angleDifference += 360;
        }
calculateBearings:

设计思路:遍历两个List,使用calculateBearingToPoint方法来计算相邻两点的方位角差,存入一个List数组中,并用currentBearing加上这个方位角差作为新的currentBearing,若大于360°,则减一个360°

代码示例:

		double currentBearing = 0.0;
        List<Double> bearingsList = new ArrayList<>();
        for (int i = 0; i < xCoords.size() - 1; i++) {
            Integer currentX = xCoords.get(i);
            Integer currentY = yCoords.get(i);
            Integer targetX = xCoords.get(i+1);
            Integer targetY = yCoords.get(i+1);          				     
            bearingsList.add(calculateBearingToPoint(currentBearing,currentX,currentY,targetX, targetY));
            currentBearing += calculateBearingToPoint(currentBearing,currentX,currentY,targetX, targetY);
            if(currentBearing >= 360.0) currentBearing -= 360.0;
        }

Problem 7: Convex Hulls

该任务中,我被给出一组坐标,要求计算其凸包

**设计思路:**使用礼品包装算法,取集合中左下角的点——纵坐标最小,若与其他点纵坐标相等,则取横坐标最小。该点必是凸包中的一个点,从该点开始,按逆时针的方向,逐个找凸包上的其他点,若两点均可选且在同一直线上,则取距离参考点最远的点。

**解决方案:**取一参考点,上一个参考点到它的向量方向角度即为该参考点的方位角,遍历集合,使用calculateBearingToPoint方法,找出与参考点方位角差最大的点即为凸点,将该凸点设为新的参考点,循环进行,直到新参考点为起始点,终止。对于初始点没有上一个参考点的情况,我们令其方向为0.0,找不超过90°的最大方位角。

代码展示:

首先,选择凸包起始点,findStartPoint函数实现找到左下角点

 		Point startPoint = findStartPoint(points);
        convexHull.add(startPoint);

以起始点为参考点,令初始方向为0.0

		Point currentPoint = startPoint;
        double currentBearing = 0.0;

遍历集合,找方位角差最大的点为新的参考点,并考虑初始点和两点同一直线的情况

		for (Point candidate : points) {
                if (!candidate.equals(currentPoint)) {
                    double bearing = calculateBearingToPoint(currentBearing,(int)currentPoint.x(),
                            (int)currentPoint.y(), (int)candidate.x(),(int)candidate.y());
                    if (bearing > maxBearing) {
                        // 当参考点为起始点时,需排除角度差大于90的情况
                        if(currentPoint == startPoint && bearing > 90.0)
                                continue;
                        maxBearing = bearing;
                        nextPoint = candidate;
                    } else if (bearing == maxBearing) {
                        // 方位角相等时,选距离currentPoint距离最远的点
                        if(distance(currentPoint,nextPoint) < distance(currentPoint,candidate))
                            nextPoint = candidate;
                    }
                }
            }

遍历结束,添加旧参考点至凸包,添加新参考点,并确定新方向

			// 添加变化方位角最大的点到凸包
            convexHull.add(nextPoint);
            currentPoint = nextPoint;
            // 以新方向为基准找凸点
            currentBearing += maxBearing;
            if(currentBearing >= 360.0) currentBearing -= 360.0;

三. Social Network问题

该任务要求我实现并测试一个社交关系图,并可以计算图中两个人之间的距离。还需要实现一个辅助的Person类。我还应该将社交网络构建为一个无向图,其中每个人都连接到零个或更多的人,但是底层图实现应该是有向的。

设计/实现FriendshipGraph类

使用Map存储图的结构,键为顶点,值为顶点的邻接表

			Map<Person, List<Person>> graph;

添加Person时需判断图中是否已存在

		public void addVertex(Person person) {
             if (!graph.containsKey(person)) {
                 graph.put(person, new ArrayList<>());
             }
        }

添加边时需要判断图中是否存在输入的两个顶点

		public void addEdge(Person person1, Person person2) {
                if (!graph.containsKey(person1) 	|| !graph.containsKey(person2)) {
                        throw new IllegalArgumentException("Person not in 	graph");
                }else {
                        graph.get(person1).add(person2);
                }
        }

计算两点间距离时使用广度优先算法,找起始点的邻接表,并存入队列中,循环取队头,重复操作,直到找到目标点

		public int getDistance(Person person1, Person person2) {
                // 用Set存储已访问过的顶点,避免重复访问
                Set<Person> visited = new HashSet<>();
                // 用队列存储待访问的顶点和已走过距离组成的键值对
                Queue<Pair<Person, Integer>> queue = new LinkedList<>();
                queue.add(new Pair<>(person1, 0));

                while (!queue.isEmpty()) {
                        // 取出队列中的第一个元素
                        Pair<Person, Integer> current = queue.poll();
                        // 当前顶点
                        Person currentPerson = current.getKey();
                        int dist = current.getValue();
                        visited.add(currentPerson);

                        if (currentPerson == person2) {
                                return dist;
                        }

                        // 将当前顶点的邻接表中的顶点加入队列
                        for (Person friend : graph.getOrDefault(currentPerson, Collections.emptyList())) {
                                if (!visited.contains(friend)) {
                                        queue.add(new Pair<>(friend, dist + 1));
                                }
                        }
                }

                // 如果两个人之间没有路径,则返回-1
                return -1;
        }

存入队列的数据结构为自定义的Pair内部静态类,以键值对的形式构建,键为顶点,值为已走的距离

		public Pair(K key, V value) {
                this.key = key;
                this.value = value;
        }
  • 21
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值