201871030119-马桂婷 实验三 结对项目—《D{0-1}KP 实例数据集算法实验平台》项目报告

项目 内容
课程班级博客 2018卓越工程师班
这个作业要求链接 实验三 软件工程结对项目
我的课程学习目标 1、体验软件项目开发中的两人合作,练习结对编程;
2、掌握Github协作开发程序的操作方法。
这个作业在哪些方面帮助我实现学习目标 1、熟悉了结对编程;
2、熟悉了GitHub的相关操作。
结对方学号-姓名 201871030117-李亚楠
结对方本次博客作业链接 李亚楠
本项目Github的仓库链接地址 实验三仓库链接

任务一:阅读《现代软件工程—构建之法》第3-4章内容,理解并掌握代码风格规范、代码设计规范、代码复审、结对编程概念

​ 在阅读了《现代软件工程—构建之法》第3-4章内容之后,我对如何成为一个合格的软件工程师有了简单的了解。

​ 第3章主要讲述了评价一个软件工程师的工作水平的主要方法、技能的反面以及TSP对个人的要求,比如,对于一个初级软件工程师如何去成长提出了5个基本要点,然后对衡量软件开发的工作量和质量有了4个考量的重点,再者还有团队对于个人的期望的7个要点、软件工程师的4个职业发展,最后还用一个模仿的例子向我们讲解了技能的反面。

​ 第4章主要就向我们讲述了代码规范、极限编程、结对编程、两人合作的不同阶段、影响他人的技巧的相关理论知识。

  • 代码风格规范:主要是文字上的规定,看似表面文章,实际上非常重要。

    • 代码风格的原则是:简明,易读,无二义性。

    • 包括了:缩进、行宽、括号、断行与空白的{}行、分行、命名、下划线、大小写、注释。

  • 代码设计规范:代码设计规范不光是程序书写的格式问题,而且牵扯到程序设计、模块之间的关系、设计模式等方方面面的通用原则。

    • 包括:函数、goto、错误处理。
  • 代码复审:代码复审就是看代码是否在“代码规范”的框架内正确地解决了问题。

    • 主要包括自我复审、同伴复审、团队复审三种。

    • 代码复审的目的:

    ​ 1、找出代码的错误。

    ​ 2、发现逻辑错误,程序可以编译通过,但是代码的逻辑是错的

    ​ 3、发现算法错误,比如使用的算法不够优化,边界条件没有处理好等

    ​ 4、发现潜在的错误和回归性错误

    ​ 5、发现可能需要改进的地方

    ​ 6、教育开发人员,传授经验,让更多的成员熟悉项目各部分的代码,同时熟悉和应用领域相 关的实际知识

    • 代码复审的步骤

      1.代码必须成功地编译,在所有要求的平台上,同时要编译Debug|Retail版本。编译要用团队规定的最严格的编译警告等级(例如C/C++中的W4)

      2.程序员必须测试过代码。

      3.程序员必须提供新的代码,以及文件差异分析工具。

      4.复审者可以选择面对面的复审、独立复审或其他方式。

      5.在面对面的复审中,一般是开发者控制流程,讲述修改的前因后果。但是复审者有权在任何时候打断叙述,提出自己的意见。

      6.复审者必须逐一提供反馈意见。注意,复审者有权提出很多看似吹毛求疵的问题,复审者不必亲自调查每一件事,开发者有义务给出详尽的回答。

      7.开发者必须负责让所有的问题都得到满意的解释或解答,或者在TFS中创建新的工作项以确保这些问题会得到处理。

      8.对于复审的结果,双方必须达成一致的意见。

  • 结对编程:结对编程是一种敏捷软件开发的方法,两个程序员在一个计算机上共同工作,是极限编程的组成部分。其中一个人输入代码,称作驾驶员,另一个人负责审查工作,称作观察员(或导航员),两人常互换角色。结对编程技术是一个非常简单和直观的概念,能达到事半功倍的工作效果。

    • 结对编程的好处:①在开发层次,结对编程能提供更好的设计质量和代码质量,两人合作解决问题的能力更强。②对开发人员自身来说,结对工作能带来更多的信心,高质量的产出能带来更高的满足感。③在企业管理层次上,结对能更有效地交流,相互学习和传递经验,分享知识,能更好地应对人员流动。

​ 总之,当我们在编写代码的时候,代码的规范对我们个人以及团队有很重要的意义,如果我们编写的代码没有一定的格式,那么当我们在回过头查看代码的时候就有可能会不知道自己当初写的是什么了,更是会让我们无从下手,而当团队中的成员审查你的代码的时候更是对整个团队造成了影响。

任务二:两两自由结对,对结对方《实验二 软件工程个人项目》的项目成果进行评价

  • 博客评论:

  • 阅读、测试运行代码并记录复审核查表:

    • 结对方Github项目仓库链接

    • 测试截图:

      提取数据测试图

      写入文件测试

    • 代码复审核查表:

      1、概要部分

      ​ (1)代码基本符合需求和规格说明;

      ​ (2)已完成的代码中代码设计考虑基本周全,但在散点图的绘制部分有一处列表 溢出;

      ​ (3)代码可读性较好;

      ​ (4)代码维护较容易;

      ​ (5)代码进行了整体执行,对每一行的代码进行了检查但没有每一行都进行执 行。

      2、设计规范部分

      ​ (1)设计基本遵从已知的设计模式;

      ​ (2)无硬编码或字符串/数字等存在;

      ​ (3)代码不依赖于某一平台,不影响将来的移植;

      ​ (4)开发者新写的代码可用于已有的功能实现;

      ​ (5)基本没有无用代码可清除。

      3、代码规范部分

      ​ 修改的部分符合代码标准和风格。

      4、具体代码部分

      ​ (1)没有对错误进行处理;没有对外部调用函数做相应检查和处理;

      ​ (2)参数传递无错误;

      ​ (3)循环没有可能出现死循环;

      ​ (4)没有使用断言来保证我们认为不变的条件真的得到满足;

      ​ (5)我没有找到可优化的空间;

      ​ (6)数据结构中的元素基本没有多余。

      5、效能

      ​ (1)代码的效能中等,最坏的情况无法预计;

      ​ (2)代码中基本没有可优化的部分;

      ​ (3)对于系统和网络的调用不会超时。

      6、可读性

      ​ 代码可读性良好。

      7、可测试性

      ​ 代码部分需要更新。

    • 对同伴个人项目仓库的源码进行合作修改:
      fork
      clone
      pull request

任务三:采用两人结对编程方式,设计开发一款D{0-1}KP 实例数据集算法实验平台

  • 需求分析

    实验二回顾:

​ 背包问题(Knapsack Problem,KP)是NP Complete问题,也是一个经典的组合优化问题,有着广泛而重要的应用背景。{0-1}背包问题({0-1 }KnapsackProblem,{0-1}KP)是最基本的KP问题形式,它的一般描述为:从若干具有价值系数与重量系数的物品(或项)中,选择若干个装入一个具有载重限制的背包,如何选择才能使装入物品的重量系数之和在不超过背包载重前提下价值系数之和达到最大?

​ D{0-1} KP 是经典{ 0-1}背包问题的一个拓展形式,用以对实际商业活动中折扣销售、捆绑销售等现象进行最优化求解,达到获利最大化。D{0-1}KP数据集由一组项集组成,每个项集有3项物品可供背包装入选择,其中第三项价值是前两项之和,第三项的重量小于其他两项之和,算法求解过程中,如果选择了某个项集,则需要确定选择项集的哪个物品,每个项集的三个项中至多有一个可以被选择装入背包,D{0-1} KP问题要求计算在不超过背包载重量 的条件下,从给定的一组项集中选择满足要求装入背包的项,使得装入背包所有项的价值系数之和达到最大;D{0-1}KP instances数据集是研究 D{0-1}背包问题时,用于评测和观察设计算法性能的标准数据集;动态规划算法、回溯算法是求解D{0-1}背包问题的经典算法。

​ 在本次项目中,基于实验二的结果需要设计开发一款D{0-1}KP 实例数据集算法实验平台,该平台首先应具备实验二中的基本功 能,其次需要实现将D{0-1}KP 实例数据集存储在数据库中,然后在此平台上可动态嵌入任何一个有效的D{0-1}KP 实例求解算法,并保 存实验结果,由于本次实验需要开发的是一款实验平台,所以还需要设计一个GUI界面来确保人机交互的便利。完成以上内容后还需要 设计遗传算法来求解D{0-1}KP问题,然后利用平台测试此算法。

  • 设计说明

    在本次实验中,我们采用了Java语言来完成各项任务,GUI界面所用到的是Java中的面板、按钮和标签等知识;数据库的连接使用了SQLsever与Java的相应语句来完成。在遗传算法这一部分中,如果要将遗传算法应用到一个问题当中,那么首先要解决5个问题:

    (1)编码问题;

    (2)适应度函数的定义;

    (3)选择算子;

    (4)交叉算子;

    (5)变异算子。

    ​ 在具体解决0-1背包问题的过程中,首先我们看编码问题,对于n个物品,可以用n个二进制位来进行编码,值为1表示该物品加到背包中,值为0表示该物品不加入到背包中,这也是我们一直在用的方法;其次,适应度函数对于优质个体,其值应该较高;而对于劣质个体,其值应该较低。这样我们就可以通过适应度函数的取值来判断个体的优劣。很显然,适应度函数可以定义为一个个体所表示加入到背包中物品的价值总和。接下来选择算子,在这里选择算子的操作流程是:首先保留当前种群中最优的10%的个体;然后在剩下的90%的个体中再添加随机生成的10%的个体;从第二步中的100%的个体中随机选择90%的个体,这样加上第一次选择的10%的个体便可构成新一代的种群。然后交叉算子,种群中的个体两两组队,相互交叉,交叉时相对应位上按照某个可能性(具体的可能性可设置)进行交换值。最后变异算子,每个新的个体,都有可能发生变异(具体的可能性可设置)。一旦这个个体发生变异,那么这个个体上的每一位按照某个可能性(具体的可能性可设置)赋一个随机值。

    计算过程

  • 软件实现及核心功能代码展示

    • 连接数据库:

      public class DBO {
       //SQLServer
       private String driverName = "com.microsoft.jdbc.sqlserver.SQLServerDriver";//加载驱动程序  
       private String url = "jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=1";//设置数据库连接串  master为数据库名
       private String user = "sa";//数据库登录用户名  
       private String password = "123456";//数据库登录密码  
       public Connection getConnection() {  
        try {  
         Class.forName(driverName);  
         return DriverManager.getConnection(url, user, password);  
        } catch (Exception e) {  
         e.printStackTrace();  
         return null;  
        }  
       }
      
    • 图形用户界面1:

      public Demo17() {
              jmb = new JMenuBar();
              
              menu1 = new JMenu("文件(F)");
              menu1.setMnemonic('F');// 设置助记符
              menu2 = new JMenu("编辑(E)");
              menu2.setMnemonic('E');
              menu3 = new JMenu("格式(O)");
              menu3.setMnemonic('O');
              menu4 = new JMenu("查看(V)");
              menu4.setMnemonic('V');
              menu5 = new JMenu("帮助(H)");
              menu5.setMnemonic('H');
      
              xinjian = new JMenu("算法选择");
              file = new JMenuItem("遗传算法");
              project = new JMenuItem("贪心法");
              project1 = new JMenuItem("动态规划算法");
      
              item2 = new JMenuItem("打开", new ImageIcon("images\1.png"));
              item3 = new JMenuItem("保存(S)");
              item3.setMnemonic('S');
              item3.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
                      InputEvent.ALT_MASK));
              item4 = new JMenu("散点图");
              a1 = new JMenuItem("idkp1-10");
              a2 = new JMenuItem("sdkp1-10");
              a3 = new JMenuItem("udkp1-10");
              a4 = new JMenuItem("wdkp1-10");
              item5 = new JMenuItem("插入数据");
              item6 = new JMenuItem("查询数据");
              itm7 = new JMenuItem("退出");
          }
      
    • 图形用户界面2:

       public Demo16() {
      
              jp1 = new JPanel();
              jp2 = new JPanel();
              jp3 = new JPanel();
              jp4 = new JPanel();
              jp5 = new JPanel();
              jlb1 = new JLabel("名称(如IDKP0)");
              jlb2 = new JLabel("维数(diemnsion)");
              jlb3 = new JLabel("容量(cubage)");
              jlb4 = new JLabel("数据个数");
              //jlb2 = new JLabel("维数(diemnsion)");
              jb1 = new JButton("确定");
              jb2 = new JButton("取消");
              jpf1 = new JPasswordField(10);
              this.setLayout(new GridLayout(3, 1));
              this.setSize(250, 150);
              this.setTitle("折扣{0-1}背包问题");
              this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              this.setVisible(true);
              }
      
    • 遗传算法:

      public void solve() {
      		readDate();
      		initPopulation();
      		for(int i = 0; i < maxgen; i++) {
      			//计算种群适应度值
      			calcFitness();
      			//记录最优个体
      			recBest(i);
      			//进行种群选择
      			select();
      			//进行交叉
      			intersect();
      			//发生变异
      			aberra();
      		}
      		
      		int totalWeight = 0; 
      		for(int i = 0; i < bestUnit.length; i++) {
      			if(bestUnit[i]){
      				totalWeight += weight[i];
      			}
      		}
      		System.out.println("total profit:" + bestFitness);
      		System.out.println("total weight:" + totalWeight);
      	}
      
    • 种群个体选择:

      private void select() {
      		SortFitness[] sortFitness = new SortFitness[scale];
      		for(int i = 0; i < scale; i++) {
      			sortFitness[i] = new SortFitness();
      			sortFitness[i].index = i;
      			sortFitness[i].fitness = fitness[i];
      		}
      		Arrays.sort(sortFitness);
       
      		boolean[][] tmpPopulation = new boolean[scale][len];
       
      		//保留前10%的个体
      		int reserve = (int)(scale * 0.1);
      		for(int i = 0; i < reserve; i++) {
      			for(int j = 0; j < len; j++) {
      				tmpPopulation[i][j] = population[sortFitness[i].index][j];
      			}
      			//将加入后的个体随机化
      			for(int j = 0; j < len; j++) {
      				population[sortFitness[i].index][j] = false;
      			}
      			float tmpc = (float)(0.5 + Math.random()) * capacity;
      			int count = 0;
      			for(int j = 0; j < tmpc;) {
      				int k = random.nextInt(len);
      				if(population[sortFitness[i].index][k]) {
      					if(count == 3) {
      						break;
      					}
      					count++;
      					continue;
      				} else {
      					population[sortFitness[i].index][k] = true;
      					j += weight[k];
      					count = 0;
      				}
      			}//
      		}
      Java
      
  • 程序运行

    界面一

    界面一

    界面二

    界面二

    散点图

    散点图
    数据库
    数据库

  • 描述结对的过程

    ​ 由于我与我的结对伙伴比较熟悉,经常在一起讨论问题和学习,所以我们结对的磨合期相对就减少了许多,结对初期我们对整个项目有了一个初步的计划(包括时间以及任务的分配),然后一起查阅相关资料然后交流,进入到中期我们便开始着手于项目的设计以及编码,但本次项目远远超出了我们预期,导致我们进入到后期阶段时比较紧张,任务完成的很粗糙。

)

  • 结对作业的PSP

    PSP2.1 任务内容 计划共完成需要的时间(min) 实际完成需要的时间(min)
    Planning 计划 30 25
    · Estimate · 估计这个任务需要多少时间,并规划大致工作步骤 30 25
    Development 开发 1120 983
    ·· Analysis 需求分析 (包括学习新技术) 310 260
    · Design Spec · 生成设计文档 90 60
    · Design Review · 设计复审 (和同事审核设计文档) 30 25
    · Coding Standard 代码规范 (为目前的开发制定合适的规范) 10 8
    · Design 具体设计 60 40
    · Coding 具体编码 500 520
    · Code Review · 代码复审 60 40
    · Test · 测试(自我测试,修改代码,提交修改) 60 30
    Reporting 报告 100 62
    ·· Test Report · 测试报告 60 30
    · Size Measurement 计算工作量 10 7
    · Postmortem & Process Improvement Plan · 事后总结 ,并提出过程改进计划 30 25
  • 小结

    ​ 本次实验采用了结对编程的思想以及做法,通过本次实验我觉得结对编程有它的好处也有不好的地方。首先,在设计以及编码的过程中两个人的思路是不同的,尤其在编码过程中,很好的避免了因为一个小问题而去钻牛角尖的问题,同时在设计方面也不再很单调,两个人的想法会多一点,对题目的理解也会有所不同,能让我们更好的理解和完成实验。但是结对编程需要两个人不断地去讨论,很多问题面对面的解决会更有效,但是两人的时间会有一定的差异,这样有些问题就需要通过社交媒体来交流,这样不利于解决问题,有时对一个问题还会产生理解上的偏差,容易在编码时出现错误。但总的来说,结对编程是利大于弊的,现在出现的问题很大程度上是所处环境造成的,但帮助是很大的,通过本次实验,我从结对伙伴身上学到了很多值得学习的品质与精神。


总结

​ 通过本次实验,我很好的理解了结对编程的过程以及优点,同时通过结对编程也使我看到了在完成一个软件项目时存在的问题,但是本次实验相较于实验二的难度对我来说是大了很多,所以在任务完成过程中我与结对伙伴花费的时间比较多但结果不是很好,尤其是对于平台的设计,之前接触的太少了,所以现在突然有新东西需要去学,学过的各种知识又需要结合在一起,各种混合在一起之后有点手足无措的感觉,所以在之后的学习中还需要更努力去尽力完成每项任务。

posted @ 2021-04-14 09:55  马桂婷  阅读(106)  评论(0编辑  收藏  举报