201971010241-王晨阳 实验三 结对项目—《{0-1}KP 实例数据集算法实验平台》项目报告
201971010241-王晨阳 实验三 结对项目—《{0-1}KP 实例数据集算法实验平台》项目报告
项目 | 内容 |
---|---|
课程班级博客链接 | 2019级卓越班 |
这个作业要求链接 | 软件工程结对项目 |
我的课程学习目标 | (1)体验软件项目开发中的两人合作,练习结对编程(Pair programming)。 (2)掌握Github协作开发程序的操作方法。 (3)使用Python编写D{0-1}KP 实例数据集算法实验平台 |
这个作业在哪些方面帮助我实现学习目标 | (1)自学《构建之法》第3-4章内容,学习本次作业相关概念。 (2)结对编程,评价对方《实验二 软件工程个人项目》并学习对方优点。 (3)合作开发设计D{0-1}KP 实例数据集算法实验平台。 (4)查阅资料设计遗传算法解决D{0-1}KP。 |
结对方学号-姓名 | 牛靖威-201971010232 |
结对方本次博客作业链接 | 牛靖威-201971010232 |
本项目Github的仓库链接地址 | 01背包问题实验平台 |
任务1:阅读《现代软件工程—构建之法》第3-4章内容,理解并掌握代码风格规范、代码设计规范、代码复审、结对编程概念;
(1)代码风格规范
a.代码风格的原则
代码风格的原则是:简明,易读,无二义性。
b.几种代码风格规范
缩进
- 在学习之前习惯性理解为:对于缩进Tab键是最好的方式,也是最简单的,不用一个空格一个空格的敲,这样的习惯主要是因为大一学习C语言和WEB前端设计养成的。对于Tab键在不同的情况下会显示不同的长度的情况其实在去年学习Linux操作系统的时候用文件编写C语言程序时,就是使用Tab键来规范缩进的,那时很明显Tab键的长度和我之前在Windows系统下写代码时的长度不一样,显示长度为8个空格长度。
是用Tab键好,还是2、4、8个空格?
结论:4个空格,在VS2005和其他的一些编辑工具中都可以定义Tab键扩展成为几个空格键。不用 Tab键的理由是Tab键在不同的情况下会显示不同的长度。4个空格的距离从可读性来说正好。
命名
- 对于命名,最初接触的时候就知道要把程序中的变量、函数等要设计的有意义,然而大多数情况下自己的命名经常图简单用A、B、C...来随意命名,从而导致很多问题,如其他人看不懂你的意义,自己时间久了也不清楚等等一系列影响后续使用修改的问题,这些感受如《构建之法》10.1.6命名所说:
阿超:我在某个同学的程序中看到有些变量叫“lili”,“yunyun”,不知道这些变量在现实生活中有没有什么意义。
下面哄笑起来。
果冻:(红着脸问)那有些变量的确想不出名字,简单的变量像i、j、k都用完了,怎么办?
阿超:当我们的程序比“Hello World”复杂10倍以上的时候,像给变量命名这样简单的事看起来也不那么简单了。我们就来谈谈如何起名字这个问题。程序中的实体、变量是程序员昼思夜想的对象,要起一个好的名字才行。大家都知道用单个字母给有复杂语义的实体命名是不好的,目前最通用的,也是经过了实践检验的方法叫“匈牙利命名法”。例如:
fFileExist,表明是一个bool值,表示文件是否存在;
szPath,表明是一个以0结束的字符串,表示一个路
- 除此之外,有一些地方不适合用“匈牙利命名法”,如文中所说:
在一些强类型的语言(如C#)中,不同类型的值是不能做运算的,对类型有严格的要求,例如C# 中,if()语句只能接受bool值的表达式,这样就大大地防止了以上问题的发生。在这样的语言中,前缀就不是很必要的,匈牙利命名法则不适用了。Microsoft .Net Framework就不主张用这样的法则。
注释
谁不会写注释?但是,需要注释什么?
- 注释问题,个人理解是对所写程序的解释说明,增加了程序可读性。好的代码是自我解释的,然而仍然有大量的设计信息不能用代码表示。例如,只能在代码中正式指定类接口的一小部分,如其方法的签名。接口的非正式方面,例如每个方法的高级描述或其结果的含义,只能在注释中进行描述。还有许多其他的例子无法在代码中描述,比如特定设计决策的基本原理,或者调用特定方法的条件。注释背后的总体思想是捕获设计者头脑中但是不能在代码中表示的信息。这些信息的范围很广,从底层的细节(比如驱动一段特别复杂的代码的硬件怪癖),到高层的概念(比如类的基本原理)。当其他开发人员稍后进行修改时,注释将允许他们更快更准确地工作。
(2)代码设计规范
a.概念
代码设计规范不光是程序书写的格式问题,而且牵涉到程序设计、模块之间的关系、设计模式等方方面面,这里有不少与具体程序设计语言息息相关的内容(如C、C++、Java、C#),但是也有通用的原则,这里主要讨论通用的原则。如果你只想为了“爽”而写程序,那么可以忽略下面的原则;如果你写的程序会被很多人使用,并且你自己会加班Debug 你的程序,那最好还是遵守下面的规定。
b.一些通用的原则
函数
现代程序设计语言中的绝大部分功能,都在程序的函数(Function, Method)中实现,关于函数最重要的原则是:只做一件事,但是要做好。
错误处理
80%的程序代码,都是对各种已经发生和可能发生的错误的处理。
——阿超
如果错误会发生,那就让程序死的地方离错误产生的地方越近越好。
——阿超
- 对于错误处理,个人认为书中“如果你认为某事可能会发生,这时就要用错误处理。”所说很对,在很多时候我们需要提前对这些问题作出处理,如:当有一个程序的运行需要几个小时的时候,我们不可能一直守在电脑前,而当你离开电脑让他自己进行的时候过来很长时间回来发现程序因为一个问题报错停了,可以想象的到是多么的绝望,所以我们一定得在可能要发生问题的地方,提前做出处理如:
……
p = AllocateNewSpace(); // could fail
if (p == NULL)
{ // error handling.
}
else
{ // use p to do something
}
(3)代码复审
a.概念
代码复审看什么?是不是把你的代码拿给别人看就行了?
(1)别人根本就不懂,给他们讲也是白讲。
(2)我是菜鸟,别的大牛能看得上我的代码么?
(3)也就是形式而已,说归说,怎么做,还是我说了算。
代码复审的正确定义:看代码是否在“代码规范”的框架内正确地解决了问题
- 复审的形式
名 称 | 形式 | 目的 |
---|---|---|
自我复审 | 自己vs自己 | 用同伴复审的标准来要求自己。不一定最有效,因为开发者对自己总是过于自信。如果能持之以恒,则对个人有很大好处 |
同伴复审 | 复审者vs开发者 | 简便易行 |
团队复审 | 团队vs开发者 | 有比较严格的规定和流程,用于关键的代码,以及复审后不再更新的代码。覆盖率高——有很多双眼睛盯着程序。但是有可能效率不高(全体人员都要到会) |
b.复审目的
(1)找出代码的错误。如:
a. 编码错误,比如一些能碰巧骗过编译器的错误。
b. 不符合项目组的代码规范的地方。
(2)发现逻辑错误,程序可以编译通过,但是代码的逻辑是错的。
(3)发现算法错误,比如使用的算法不够优化。
(4)发现潜在的错误和回归性错误——当前的修改导致以前修复的缺陷又重新出现。
(5)发现可能改进的地方。
(6)教育(互相教育)开发人员,传授经验,让更多的成员熟悉项目各部分的代码,同时熟悉和应用领域相关的实际知识。
c.为什么复审
- 首先,在代码复审中发现的问题,绝大多数都可以由开发者独立发现。从这一意义上说,复审者是在替开发者干开发者本应干的事情。即使是完美,代码复审还有“教育”和“传播知识”的作用。更重要的是,不管多么厉害的开发者都会或多或少地犯一些错误,有欠考虑的地方,如果有问题的代码已签入到产品代码中,再要把所有的问题找出来就更困难了。大家学习软件工程都知道越是项目后期发现的问题,修复的代价越大。代码复审正是要在早期发现,并修复这些问题。
- 另外,在代码复审中的提问与回应能帮助团队成员互相了解,就像练武之人互相观摩点评一样。在一个新成员加入一个团队的时候,代码复审能非常有效地帮助新成员了解团队的开发策略、编程风格及工作流程。
注:关于代码复审的步骤等其他知识将在任务2中对结对对象的实验二申评中体现所学。
(4)结对编程
a.概念
在结对编程模式下,一对程序员肩并肩地、平等地、互补地进行开发工作。两个程序员并排坐在一台电脑前,面对同一个显示器,使用同一个键盘,同一个鼠标一起工作。他们一起分析,一起设计,一起写测试用例,一起编码,一起单元测试,一起集成测试,一起写文档等。
结对编程不是程序开发者独到的发明,在现实生活中,也存在着类似的搭档关系(Partnership):
越野赛车(驾驶,领航员)
驾驶飞机(驾驶,副驾驶)
战斗机的编组(长机,僚机)
提示:这些任务都有共同点:在高速度中完成任务,任务有较高的技术要求,任务失败的代价很高。
b.为什么要结对编程
- 每人在各自独立设计、实现软件的过程中不免要犯这样那样的错误。在结对编程中,因为有随时的复审和交流,程序各方面的质量取决于一对程序员中各方面水平较高的那一位。这样,程序中的错误就会少得多,程序的初始质量会高很多,这样会省下很多以后修改、测试的时间。具体地说,结对编程有如下的好处:
(1)在开发层次,结对编程能提供更好的设计质量和代码质量,两人合作能有更强的解决问题的能力。
(2)对开发人员自身来说,结对工作能带来更多的信心,高质量的产出能带来更高的满足感。
(3)在心理上, 当有另一个人在你身边和你紧密配合, 做同样一件事情的时候, 你不好意思开小差, 也不好意思糊弄。
(4)在企业管理层次上,结对能更有效地交流,相互学习和传递经验,能更好地处理人员流动。因为一个人的知识已经被其他人共享。
总之,如果运用得当,结对编程能得到更高的投入产出比(Return of Investment)。
c.如何结对编程
- 书中以驾驶员和领航员为例有如下几点:
(1)驾驶员:写设计文档,进行编码和单元测试等XP开发流程。
(2)领航员:审阅驾驶员的文档、驾驶员对编码等开发流程的执行;考虑单元测试的覆盖程度;是否需要和如何重构;帮助驾驶员解决具体的技术问题。
(3)驾驶员和领航员不断轮换角色,不宜连续工作超过一小时。领航员要控制时间。
(4)主动参与。任何一个任务都首先是两个人的责任,也是所有人的责任。没有“我的代码”、“你的代码”或“她的代码”,只有“我们的代码”。
(5)只有水平上的差距,没有级别上的差异。尽管可能大家的级别资历不同,但不管在分析、设计或编码上,双方都拥有平等的决策权利。
注:结对编程是个渐进的过程,在学习本门可之前虽然没有听过结对编程的概念,也没有很全面的专门去了解结对编程,但在实际的学习过程中,我们不仅存在两人的结对合作,也经历多5~7人小组合作,这些都为这次的结对编程打下一定基础。真的如书中舞蹈版解释存在萌芽、磨合、规范、创造以及解体各个阶段。
d.两人的合作——如何影响对方
两人在一起合作,自然会出现不同意见,每个人都有自己的想法,在两个人平等合作的情况下,不存在领导与被领导的关系,如何能说服对方?要听对方的话语和观察对方的肢体语言,了解它们所表示的潜台词,试着从对方的角度看待问题。同时也要根据情况采取不同的方法影响别人。
任务2:两两自由结对,对结对方《实验二 软件工程个人项目》的项目成果进行评价,具体要求如下:
(1)对项目博文作业进行阅读并进行评论,评论要点包括:博文结构、博文内容、博文结构与PSP中“任务内容”列的关系、PSP中“计划共完成需要的时间”与“实际完成需要的时间”两列数据的差异化分析与原因探究,将以上评论内容发布到博客评论区。
(2)克隆结对方项目源码到本地机器,阅读并测试运行代码,参照《现代软件工程—构建之法》4.4.3节核查表复审同伴项目代码并记录。
(3)依据复审结果尝试利用github的Fork、Clone、Push、Pull request、Merge pull request等操作对同伴个人项目仓库的源码进行合作修改。
项目 | 内容 |
---|---|
结对方 | 牛靖威-201971010232 |
结对方实验二博客链接 | 牛靖威 实验二 软件工程个人项目 |
结对方实验二Github项目仓库链接 | 0 1背包问题 |
(1)博客评论
1.博文结构:
页面较为简洁 排版方式存在欠缺。
2.博文内容:
大体完成实验内容 对模块化的掌握略显不足。
3.博文结构与PSP中“任务内容”列的关系:
PSP任务内容中的大部分任务都在博文中体现出来 只有少部分还有所欠缺,博文内容关于需求分析和功能设计的部分可以更加完善一些。
4.PSP中“计划共完成需要的时间”与“实际完成需要的时间”两列数据的差异化分析与原因探究:
实际完成需要的时间比计划共完成需要的时间更长,可能是因为博主编码能力较弱 时间规划不强
(2)克隆项目,阅读并测试
-
克隆项目到本地文件夹
-
测试运行
-
代码核查表
项目 内容 概要部分 代码符合需求和规格说明么? 否,仅大致完成 代码设计是否考虑周全? 否 代码可读性如何? 较好 代码容易维护么? 容易 代码的每一行都执行并检查过了吗? 是 设计规范部分 设计是否遵从已知的设计模式或项目中常用的模式? 是 有没有硬编码或字符串/数字等存在? 有 代码有没有依赖于某一平台,是否会影响将来的移植? 影响较小 开发者新写的代码是否用已有的Library/SDK/Framework中的功能实现?在本项目中是否存在类似的功能可以通过调用而不用全部重新实现? 是,代码中使用了Matplotlib库 有没有无用的代码可以清除? 有 代码规范部分 修改的部分符合代码标准和风格么? 符合 具体代码部分 有没有对错误进行处理?对于调用的外部函数,是否检查了返回值或处理了异常? 已经处理 参数传递有无错误,字符串的长度是字节的长度还是字符的长度,是从0开始计数还是从1开始计数 无错误;字符的长度;从0开始 边界条件是如何处理的?switch语句和default分支是如何处理的?循环有没有可能出现死循环? 前提分析推导边界条件;可能 有没有使用断言来保证我们认为不变的条件真的得到满足? 否 对资源的利用,是在哪里申请,在哪里释放的?有无可能存在资源泄露?有没有优化的空间? 有对应的申请、释放语句;不存在;有 数据结构中有没有用不到的元素? 有 效能 代码的效能如何?最坏的情况是怎么样的? 效能一般;最坏情况陷入死循环 代码中,特别是循环中是否有明显可优化的部分? 有 对于系统和网络的调用是否会超时?如何处理? 否 可读性 代码可读性如何?有没有足够的注释? 没有足够的注释,但代码较为清晰,故可读性较好 可测试性 代码是否需要更新或创建新的单元测试? 是 任务3:采用两人结对编程方式,设计开发一款D{0-1}KP 实例数据集算法实验平台,使之具有以下功能:
(1)平台基础功能:实验二 任务3;
(2)D{0-1}KP 实例数据集需存储在数据库;
(3)平台可动态嵌入任何一个有效的D{0-1}KP 实例求解算法,并保存算法实验日志数据;
(4)人机交互界面要求为GUI界面(WEB页面、APP页面都可);
(5)查阅资料,设计遗传算法求解D{0-1}KP,并利用此算法测试要求(3);
(6)附加功能:除(1)-(5)外的任意有效平台功能实现。
(1)项目背景
背包问题(Knapsack Problem,KP)是NP Complete问题,也是一个经典的组合优化问题,有着广泛而重要的应用背景。{0-1}背包问题({0-1 }Knapsack Problem,{0-1}KP)是最基本的KP问题形式,它的一般描述为:从若干具有价值系数与重量系数的物品(或项)中,选择若干个装入一个具有载重限制的背包,如何选择才能使装入物品的重量系数之和在不超过背包载重前提下价值系数之和达到最大?
(2)需求分析
-
目前,求解折扣{0-1}背 包 问 题(D{0-1}KP)的主要算法是基于动态规划的具有伪多项式时间的确定性算法,当D{0-1}KP实例中各项的价值系数与重量系数在大范围内取值时缺乏实用性。折扣{0-1}背包问题(Discounted {0-1} Knapsack Problem,D{0-1}KP)是比0-1背包还要难以求解的NP-hard问题。提出了一种求解D{0-1}KP的新遗传算法GADKP。
-
遗传算法的基本运算过程
(1)初始化:设置进化代数计数器t=0,设置最大进化代数T,随机生成M个个体作为初始群体P(0)。
(2)个体评价:计算群体P(t)中各个个体的适应度
(3)选择运算:将选择算子作用于群体。选择的目的是把优化的个体直接遗传到下一代或通过配对交叉产生新的个体再遗传到下一代。选择操作是建立在群体中个体的适应度评估基础上的。
(4)交叉运算:将交叉算子作用于群体。遗传算法中起核心作用的就是交叉算子。
(5)变异运算:将变异算子作用于群体。即是对群体中的个体串的某些基因座上的基因值作变动。群体P(t)经过选择、交叉、变异运算之后得到下一代群体P(t+1)。
(6)终止条件判断:若t=T,则以进化过程中所得到的具有最大适应度个体作为最优解输出,终止计算。
(3)遗传算法的实现
# 遗传算法
start2 = time.perf_counter()
heigh = heigh+70
m = 32 # 规模
N = 500 # 迭代次数
Pc = 0.8 # 交配概率
Pm = 0.05 # 变异概率
V =profit[i]
W =weight[i]
n = len(W) # 染色体长度
w = cubage[i]
C = self.init(m, n)
S,F = self.fitness(C,m,n,W,V,w)
B ,y = self.best_x(F,S,m)
Y =[y]
for i in range(N):
p = self.rate(F)
C = self.chose(p, C, m, n)
C = self.match(C, m, n, Pc)
C = self.vari(C, m, n, Pm)
S, F = self.fitness(C, m, n, W, V, w)
B1, y1 = self.best_x(F, S, m)
if y1 > y:
y = y1
Y.append(y)
(4)数据库的建立
# 创建数据库
def CreateDataBase(self):
global i
global d
global cubage
global profit
global weight
global pw
conn = sqlite3.connect('mrsoft.db')
cursor = conn.cursor()
cursor.execute('DROP TABLE IF EXISTS user')
cursor.execute('create table if not exists user (num int(10) primary key,profit int(20), weight int(20))')
cubage[i] = max(list(map(int,re.findall(r'\d+',line[i*8+3]))))
profit[i] = list(map(int,re.findall(r'\d+',line[i*8+5])))
weight[i] = list(map(int,re.findall(r'\d+',line[i*8+7])))
pw[i] = list(map(lambda x:x[0]/x[1],zip(profit[i],weight[i])))
for j in range(0,d):
cursor.execute('insert into user (num,profit,weight) values ("%d","%d","%d")'%(j,profit[i][j],weight[i][j]))
cursor.execute('select * from user')
result=cursor.fetchall()
cursor.close()
conn.commit()
conn.close()
return result
(5)人机交互页面的建立
# 创建“确定”和“取消”按钮,并绑定事件
self.bt_confirm = wx.Button(panel, label='确定')
self.bt_confirm.Bind(wx.EVT_BUTTON,self.OnclickSubmit)
self.bt_cancel = wx.Button(panel, label='取消')
self.bt_cancel.Bind(wx.EVT_BUTTON,self.OnclickCancel)
# 创建文本,左对齐
self.title = wx.StaticText(panel, label="1、beibao.txt\n")
self.label_file = wx.StaticText(panel, label="希望读取的文件:")
self.text_file = wx.TextCtrl(panel, style=wx.TE_LEFT)
# 添加容器,容器中控件按横向并排排列
hsizer_file = wx.BoxSizer(wx.HORIZONTAL)
hsizer_file.Add(self.label_file, proportion=0, flag=wx.ALL, border=5)
hsizer_file.Add(self.text_file, proportion=1, flag=wx.ALL, border=5)
hsizer_button = wx.BoxSizer(wx.HORIZONTAL)
hsizer_button.Add(self.bt_confirm, proportion=0, flag=wx.ALIGN_CENTER, border= 5)
hsizer_button.Add(self.bt_cancel, proportion=0, flag=wx.ALIGN_CENTER, border=5)
# 添加容器,容器中控件按纵向并排排列
vsizer_all = wx.BoxSizer(wx.VERTICAL)
vsizer_all.Add(self.title, proportion=0, flag=wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER,border=30)
vsizer_all.Add(hsizer_file, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=45)
vsizer_all.Add(hsizer_button, proportion=0, flag=wx.ALIGN_CENTER | wx.TOP, border=20)
panel.SetSizer(vsizer_all)
(6)运行结果
(7)结对PSP
PSP2.1 | 任务内容 | 计划共完成需要的时间(min) | 实际完成需要的时间(min) |
---|---|---|---|
Planning | 计划 | 20 | 20 |
Estimate | 估计这个任务需要多少时间,并规划大致工作步骤 | 20 | 20 |
Development | 开发 | 1090 | 1200 |
Analysis | 需求分析 (包括学习新技术) | 120 | 155 |
Design Spec | 生成设计文档 | 30 | 20 |
Design Review | 设计复审 (和同事审核设计文档) | 20 | 20 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 30 | 35 |
Design | 具体设计 | 60 | 50 |
Coding | 具体编码 | 600 | 650 |
Code Review | 代码复审 | 30 | 30 |
Test | 测试(自我测试,修改代码,提交修改) | 200 | 240 |
Reporting | 报告 | 100 | 100 |
Test Report | 测试报告 | 30 | 40 |
Size Measurement | 计算工作量 | 40 | 30 |
Postmortem & Process Improvement Plan | 事后总结 ,并提出过程改进计划 | 30 | 30 |
任务4:完成结对项目报告博文作业
- 完成博客编写
总结:两人合作真的能够带来1+1>2的效果吗?通过这次结对合作,请谈谈你的感受和体会
两人合作是否能够带来1+1>2的效果,取决于两个人之间的沟通是否有效,是否能够朝着共同的目标去各自补充,合作完成任务。对于本次我们组的结对编程,我觉得两个人合作是能带来1+1>2的效果的。
通过这次结对合作,我觉得对于一个较复杂的任务而言,一个人的能力、掌握的知识毕竟有限,两个人合作编程,能够综合各自掌握的知识,在编码的过程中,两个人轮流编程,互相监督,能够及时发现错误,少走一些不必要的弯路,并且不会的地方,两个人也能够一起讨论对策,提升了编码效率。
两个人的合作,只要可以有效沟通,是可以很好地达到完成任务、共同进步的效果的。