软件构造LAB1的一些思考

1.1本次实验通过对幻方,海龟画图以及社交网络三个问题的求解,训练基本 Java 编程技能,能够利用 Java OO 开发基本的功能模块,能够阅读理解已有代码框架并根据功能需求补全代码,能够 为所开发的代码编写基本的测试程序并完成测试,初步保证所开发代码的正确性。

1.2利用 Git 作为代码配置管理的工具,学会 Git 的基本使用方法。

  2.实验环境配置

2.1我们首先需要做安装java,在网页搜索java,安装jdk,之后安装java的集成开发环境Eclipse,最后配置环境变量。

具体请看:

(8条消息) java环境变量 的配置与详解(全网最详细教程)_S-D-C-L-Yourn的博客-CSDN博客_java环境变量https://blog.csdn.net/qq_41436122/article/details/82620080?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165200854716781685315384%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165200854716781685315384&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-4-82620080-null-null.142%5Ev9%5Epc_search_result_cache,157%5Ev4%5Econtrol&utm_term=java%E9%85%8D%E7%BD%AE%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F&spm=1018.2226.3001.4187

2.2由于在本次实验中我们需要用到Junit,所以必须配置Junit。可以参考下文:

(8条消息) 在Eclipse中使用JUnit4进行单元测试(初级篇)_andycpp的博客-CSDN博客_junit单元测试https://blog.csdn.net/andycpp/article/details/1327147?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165201046416780357294822%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165201046416780357294822&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-3-1327147-null-null.142%5Ev9%5Epc_search_result_cache,157%5Ev4%5Econtrol&utm_term=eclipse%E4%BD%BF%E7%94%A8junit%E6%B5%8B%E8%AF%95&spm=1018.2226.3001.4187

  3.实验过程

3.1Magic Squares

     本实验首先要求我们要对文件输入的数据进行判断,判断其是否为幻方,而判断的标准是(1)数据是否以矩阵的方式存储;(2)矩阵每行,每列,还有左右对角线之和是否都相等;(3)矩阵中的数字是否都是正整数;(4)矩阵每行各元素之间是否是以制表符\t隔开。这就需要我们从文件中读取数据并用合适的结构存储,之后进行判断。

       其次我们需要对罗伯法生成幻方作以理解说明,并对通过罗伯法生成幻方时产生的异常作以处理,并利用罗伯法生成一个奇数阶幻方并存储测试。

3.1.1isLegalMagicSquare()

    这个方法需要我们从文件中读取数据并判断数据是不是一个幻方。我们可以分两步完成。

(1)首先我们要从文件中读出数据。

这点可以参考下文:

(8条消息) java从文件中读取数据的几种方法(Java io基础)_南风知易✓✓✓的博客-CSDN博客_java读取文件内容https://blog.csdn.net/tianynnb/article/details/121185052?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165200944616782248519271%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165200944616782248519271&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-2-121185052-null-null.142%5Ev9%5Epc_search_result_cache,157%5Ev4%5Econtrol&utm_term=java%E4%BB%8E%E6%96%87%E4%BB%B6%E4%B8%AD%E8%AF%BB%E5%8F%96%E6%95%B0%E6%8D%AE&spm=1018.2226.3001.4187

(2)在保存好从文件读取的数据之后,我们开始判断它是否满足我们的判定条件。

    第一步我们将line数组的每一个字符串利用\t划分成一个个小字符串并存储在String数组strings中,划分出来的每一个字符串都是一个矩阵的元素,由于strings的大小代表数据阵列的列数,line的大小代表数据阵列的行数,所以我们可以通过比较strings和line的大小判断它是否是一个矩阵。

     第二步我们利用mathches()方法对strings中的每一个字符串进行正则表达式匹配,使用matches("[0-9]+")可以判断每一元素是否都是非负整数,再通过Integer.valueOf(strings[k])!=0判断是否是正整数,在判断完第一,二步之后,我们使用Integer.valueOf(strings[k])将每一个字符串转变为整型数。

int [][]data=new int[line.length][line.length];
    for(int j=0;j<line.length;j++)
{
      String[] strings=line[j].split("\t");
      if(strings.length!=line.length)//对每个数据之间是否是\t以及是否行列数相等的判断              
        return false;
       for(int k = 0;k<strings.length;k++)
{
         if(strings[k].matches("[09]+")&&Integer.valueOf(strings[k])!=0)//对每个数据是否是正整数的判断
            data[j][k] = Integer.valueOf(strings[k]);
          else                                   
            return false;
        }
}

    第三步我们进行矩阵的每一行,每一列,左右对角线之和是否相等的判断。我们只需要两个for循环就可以计算出上述的求和结果,再经过一个for循环就可以判断是否相等。

3.1.2generateMagicSquare()

(1)罗伯法首先只能生成奇数阶幻方,不然在生成过程中数组会越界产生异常。它的具体生成过程是先在第一行的正中央放置1,再从这个格子开始,依次向该格子的右上方的格子放置下一个数,如果当前格子在第一行,那么把最后一行看作是第一行的上一行再放置数字,如果当前格子在最右列,那么把最左列看作是最右列的右列再放置数字,如果我们下一个要放置数字的格子已经放置了数字,我们在当前格子的下一行的相同列数的格子中开始放置数字,按照这种规则,我们就可以把1到N2的数字放置在N2个格子中生成一个N阶幻方。

(2)之后我们将生成的幻方存储在文件中,这里我们直接使用FileWriter类进行写文件的操作。

  可以参考下文:

(8条消息) Java将数据信息写入文件文件的几种实现方法_Running-小猛的博客-CSDN博客_java将数据写到文本https://blog.csdn.net/leying521/article/details/85234096?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165200978916781667832496%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165200978916781667832496&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-8-85234096-null-null.142%5Ev9%5Epc_search_result_cache,157%5Ev4%5Econtrol&utm_term=java%E5%90%91%E6%96%87%E4%BB%B6%E4%B8%AD%E5%86%99%E5%85%A5%E6%95%B0%E6%8D%AE&spm=1018.2226.3001.4187

3.2Turtle Graphics

3.2.1Problem : Calculating Bearings

      这个问题需要我们计算轴承,首先我们需要知道需要计算什么,即计算给出的点之间的角度偏移量。这个方法给出了一组点的x坐标和一组点的y坐标,我们首先得到坐标组大小,如果坐标组个数为0,我们直接返回一个空的list<Double>集合,如果坐标组个数大于0,我们通过计算返回一个坐标组元素个数减一个元素的list<Double>集合。

      我们在计算在返回集合的元素的时候需要借助calculateBearingToPoint方法计算两个点之间的角度偏转,我们可以用两个int类型的临时变量m,n来保存数据,使用for循环遍历list集合完成计算。其中n首先记录第一个点的偏转方向,即0,之后在循环中来暂存计算出的偏转方向,然后加入list集合,在之后一次的循环中用m来暂存n,将m带入 calculateBearingToPoint方法参与计算。

public static List<Double> calculateBearings(List<Integer> xCoords, List<Integer> yCoords) {
     //throw new RuntimeException("implement me!");
     List<Double> result = new ArrayList<>();
     int x=xCoords.size();
     double n=0;
     for(int i=0;i<x-1;i++)
     {
     double m=n;
    n=calculateBearingToPoint(m,xCoords.get(i),yCoords.get(i),xCoords.get(i+1),yCoords.get(i+1));
       result.add(n);
     }
     return result;
    }

3.2.2Problem : Convex Hulls

     在这个问题中我们需要计算给出一堆点中的凸包,即一堆点中最外侧点的集合。

     我们可以先判断给出点集的个数,如果给出的点集个数小于3,那么我们直接返回这个点集。之后我们开始寻找点集之中最左下角的点,我们使用pointMinjilu第一个点,之后遍历后面的所有点,如果有一个点在它的左边我们就用这个点替换最初的pointMin,如果有一个点在pointMin的下方,我们也替换这个点,遍历完之后我们得到了最左下方的点。

     得到最左下方的点之后,我们将它加入list集合,之后开始循环,每次循环时遍历点集,寻找下一个我们要加入的点,每一次循环找到一个点,记为pointNext,意为要加入list集合的的点,直到找到我们最开始得到的pointMin结束循环。如果要遍历的点是pointNext本身,我们直接跳过,如果不是,我们借助calculateBearingToPoint()计算pointNext和这个点的角度偏转量,再计算两点之间的距离,如果比我们之前计算的角度偏转量(初始设为360)小,我们直接将这个点视为新的pointNext,然后将它的角度偏转量记为新的角度偏转量,将它与之前的pointNext之间的距离视为新的距离,开始下一次遍历看这个点是否会被替换。如果比我们之前计算的角度偏转量(初始设为360)大,我们开始比较这两点的距离和我们之前的得到的距离(初始为最大值),如果现在两点间的距离大于之前的距离,我们重复替换操作,开始下一次遍历。

3.2.3Problem : Personal art

     在这个问题中,我尝试使用turtle画图程序画出一系列的圆,只要每次偏转固定的角度,再选择合适的长度,就可以实现画圆。我在画布的上下左右各画出两个半径不一的圆,得到组合图形。

     

3.2.4Submitting

可以参考下文下载git:

(8条消息) Git 详细安装教程(详解 Git 安装过程的每一个步骤)_mukes的博客-CSDN博客_git安装https://blog.csdn.net/mukes/article/details/115693833?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165201034516782246496758%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=165201034516782246496758&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-2-115693833-null-null.142%5Ev9%5Epc_search_result_cache,157%5Ev4%5Econtrol&utm_term=git&spm=1018.2226.3001.4187可以参考下文使用:

(8条消息) 关于Git这一篇就够了_17岁boy想当攻城狮的博客-CSDN博客https://blog.csdn.net/bjbz_cxy/article/details/116703787?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165201034516782246496758%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=165201034516782246496758&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-116703787-null-null.142%5Ev9%5Epc_search_result_cache,157%5Ev4%5Econtrol&utm_term=git&spm=1018.2226.3001.4187

3.3Social Network

      本问题类似于图的问题,要求我们将一个Person类看成一个点,点与点之间用某种关系当作边连接起来,再来寻找两点间的最短距离。由于这个题本身是无向图,但是实验要求我们必须要可以改进为有向图,所以我们在建立边的时候就要注意方向的问题。

       案例实验代码:

1. FriendshipGraph graph = new FriendshipGraph();
2. Person rachel = new Person("Rachel");
3. Person ross = new Person("Ross");
4. Person ben = new Person("Ben");
Lab Manuals for Software Construction Lab-1 Fundamental Java Programming and Testing
6
5. Person kramer = new Person("Kramer");
6. graph.addVertex(rachel);
7. graph.addVertex(ross);
8. graph.addVertex(ben);
9. graph.addVertex(kramer);
10.graph.addEdge(rachel, ross);
11.graph.addEdge(ross, rachel);
12.graph.addEdge(ross, ben);
13.graph.addEdge(ben, ross);
14.System.out.println(graph.getDistance(rachel, ross)); 
//should print 1
15.System.out.println(graph.getDistance(rachel, ben)); 
//should print 2
16.System.out.println(graph.getDistance(rachel, rachel)); 
//should print 0
17.System.out.println(graph.getDistance(rachel, kramer)); 
//should print -1

3.3.1设计/实现FriendshipGraph类

       我们首先看案例代码,这里面在FriendshipGraph类中有添加点,添加边和计算距离的操作,所以我们首先要有一个点集,我们用List<Person> VertexList来实现,之后我们用addVertex方法添加点,如下图:

public boolean addVertex(Person people) //加入点集
    {
        if(VertexList.contains(people))
        {
            System.out.println("已经存在!\n ");
            return false;
        }
        else
        {
            VertexList.add(people);
            return true;
        }       
    }

     有了点,我们再来看边,要添加边,我们首先要知道边的两个端点,我们用一个FindPosistion方法表示一个点在我们设置的点集中的位置,返回一个整型数,我们在addEdge方法中用这个整型数带入Person类中的Socialadd方法,给边的前端点增加与后端点的联系,以此来实现我们添加边的功能。如下图:

public int FindPosistion(Person people)
//寻找点在点集中的位置,若不在返回-1
    {
        if(VertexList.contains(people))
            for(int i=0;i<VertexList.size();i++)
                if(VertexList.get(i).equals(people))
                    return i;
        return -1;  
}

      最后我们来看如何计算两点间的最短距离,这里我们使用广度优先搜索来实现,我们需要首先建立一个Map来表示点集中每一个点距离起始点的距离,我们使用Map<Person, Integer> distance实现。之后我们建立一个队列方便后面的广度优先遍历,我们使用Queue<Integer> queue实现。同时我们还需要一个标志数组来标记一个点是否已经被搜索,我们使用boolean []visited实现。在准备工作做好后,我们开始第一步,使用first和last两个变量借助FindPosistion方法找到两点位置,之后将第一个点入队,改变状态以及输入Map的数据,然后开始广度优先搜索,可以参考下文:

(8条消息) 图的深度优先搜索(DFS)和广度优先搜索(BFS)及其Java实现_键盘上的钢琴师_v5的博客-CSDN博客_广度优先搜索java实现https://blog.csdn.net/daijin888888/article/details/76609895?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165201091816781685325417%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165201091816781685325417&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-1-76609895-null-null.142%5Ev9%5Epc_search_result_cache,157%5Ev4%5Econtrol&utm_term=%E5%9B%BE%E7%9A%84%E5%B9%BF%E5%BA%A6%E4%BC%98%E5%85%88%E9%81%8D%E5%8E%86java&spm=1018.2226.3001.4187      在搜索中,我们每次进入一个点的子节点都将子节点的距离值设置为父节点的距离值加一,直到我们遇到的点就是我们要找的last点,到这里我们直接结束遍历,返回结果。其中currentvertex是我们返回的队首节点,visitnode是currentvertex的子节点:

while(!queue.isEmpty())
    {
     int currentvertex=queue.remove();
     for(int i=0;i<VertexList.get(currentvertex).listsize();i++)
{
       int visitnode=VertexList.get(currentvertex).getSocial(i);
       if (!visited[visitnode])
{//每次让一条边根端点到起始点的距离+1成为它的子端点的距离值,若是子端点就是目的端点退出
         visited[visitnode]=true;
         queue.add(visitnode);
         distance.put(VertexList.get(visitnode),distance.get(VertexList.get(currentvertex))+1);
        }
       if(VertexList.get(visitnode).equals(people2))
           return distance.get(VertexList.get(visitnode));
      }
    }

3.3.2设计/实现Person类

      我们首先根据实验要求中的实验代码来看Person这个类要包含的信息。首先是必须要有一个字符串表示名字,我们用String name来实现。其次,我们可以看到FriendshipGraph类中有添加点,添加边和计算距离的操作,所以这就要求我们必须在Person类中对与它相连的点作以记录,我们用一个整形变量集合List<Integer> SocialList来表示,他里面的元素代表要建立联系的点是第几个加入FriendshipGraph类中点集的点,同时我们还要有建立这个关系表的方法,我们使用Socialadd方法来实现,如下图:

public void Socialadd(int posistion)//添加联系人;
    {
        SocialList.add(posistion);
    }

还要有返回关系表中元素的方法,我们使用getSocial方法实现,如下图:

public int getSocial(int position) //其他联系者与person的关系;

    {
        return SocialList.get(position);
    }

有了点的关系,我们再来看边的关系,我们使用edgeexist方法来判断要加入的边是否已经存在,如下图:

public boolean edgeexist(int posistion)//判断两者之间是否有边;
    {
         if(SocialList.contains(posistion))
             return true;
         else
             return false;
     }

有了这些方法,Person类就建立完成了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值