201871030108-冯永萍 实验二 个人项目— D{0-1}背包问题项目报告
项目 | 内容 |
---|---|
课程班级博客链接 | https://edu.cnblogs.com/campus/xbsf/2018CST |
这个作业要求链接 | https://www.cnblogs.com/nwnu-daizh/p/14552393.html |
我的课程学习目标 | 完成课程要求的基础上,对软件工程有系统的理解 |
这个作业在哪些方面帮助我实现学习目标 | 让我熟悉了PSP流程,并通过例子实践,深刻体会到软件开发不等于编写程序 |
项目GitHub的仓库链接地址 | https://github.com/fengyongping1120/D01bag |
任务1:
阅读教师博客“常用源代码管理工具与开发工具”内容要求,点评班级博客中已提交相关至少3份作业。
作业点评链接:
●https://www.cnblogs.com/chms/p/14550446.html
●https://www.cnblogs.com/baofengmei/p/14544245.html
●https://www.cnblogs.com/cxl369/p/14522828.html
任务2:
详细阅读《构建之法》第1章、第2章,掌握PSP流程。
卡内基梅隆大学(CMU)的能力成熟度模型(CMM和CMMI),是用来衡量一个团队能力的一套模型。CMU的专家们针对软件工程师也有一套模型,叫Personal Software Process (PSP)。以下是PSP2.1流程:
PSP特点:
●不局限于某一种软件技术(如编程语言),而是着眼于软件开发的流程,这样,开发不同应用的软件工程师可以互相比较。
●不依赖于考试,而主要靠工程师自己收集数据,然后分析,提高。
●在小型、初创的团队中,很难找到高质量的项目需求,这意味着给程序员的输入质量不高。在这种情况下,程序员的输出(程序/软件)往往质量也不高,然而这并不能全部由程序员负责。
●PSP依赖于数据。
●需要工程师输入数据,记录工程师的各项活动。
●即使一些数据不利于工程师本人,也要尽可能保证工程师愿意如实地记录这些数据。
●PSP的目的是记录工程师如何实现需求的效率,而不是记录顾客对产品的满意度。
任务3:
项目开发背景:背包问题(Knapsack Problem,KP)是NP Complete问题,也是一个经典的组合优化问题,有着广泛而重要的应用背景。{0-1}背包问题({0-1 }Knapsack Problem,{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}背包问题的程序,程序基本功能要求如下:
1.可正确读入实验数据文件的有效D{0-1}KP数据;
2.能够绘制任意一组D{0-1}KP数据以重量为横轴、价值为纵轴的数据散点图;
3.能够对一组D{0-1}KP数据按项集第三项的价值:重量比进行非递增排序;
4.用户能够自主选择动态规划算法、回溯算法求解指定D{0-1} KP数据的最优解和求解时间(以秒为单位);
5.任意一组D{0-1} KP数据的最优解、求解时间和解向量可保存为txt文件或导出EXCEL文件。
1、需求分析
1.可正确读入实验数据文件的有效D{0-1}KP数据:
正确读入数据并存储并存储进二维数组;
2.能够绘制任意一组D{0-1}KP数据以重量为横轴、价值为纵轴的数据散点图:
坐标的正确显示;
3.能够对一组D{0-1}KP数据按项集第三项的价值:重量比进行非递增排序;
非递增排序与递减排序:
9,8,7,6,5.:递减排列
9,8,7,7,6,5,5,2,1,.:非递增排列
4.用户能够自主选择动态规划算法、回溯算法求解指定D{0-1} KP数据的最优解和求解时间(以秒为单位);
如果选择了某个项集,则需要确定选择项集的哪个物品,每个项集的三个项中至多有一个可以被选择装入背包;
5.任意一组D{0-1} KP数据的最优解、求解时间和解向量可保存为txt文件或导出EXCEL文件。
数据的正确写入及排版。
2、功能设计
●读入实验数据文件
●数据散点图
●重量比进行非递增排序
●求解指定D{0-1} KP数据的最优解和求解时间
●将最优解、求解时间和解向量保存为txt文件或导出EXCEL文件
3、设计实现
●输入:背包容量s、D{0-1}KP数据集的项数n、从文件bag.txt中读取每个项的标号(从1到n)及其中三件物品对应的重量与价值
●输出:在不超过背包容量的前提下,背包中的项及对应物品的最大价值(项有一个或多个,项中选择的物品只可能有一个)
●读入实验数据文件
首先输入要读取数据的四个测试文件中的其中一个的名字,判断该文件是否能正确打开。成功打开后,输入要进行数据操作的第几组数据。输入后,读取该组数据的项集数和背包容量。并将该组的物品的重量和价值存入二维数组A的第三行和第四行中(第一行存储要操作的是该文件中的第几组D{0-1}KP数据中的第几组物品,第二行分别将数据从1到3编号,方便第四步输出最优解)。
●数据散点图
首先读入该组D{0-1} KP数据的重量和价值的最大值,动态调整横纵坐标的最大值。然后创建一个二维数组B,数组B的大小也分别对应重量和价值的最大值,节省内存占用。然后对A数组的二、三行遍历,将数组B对应的A的重量和价值的横纵坐标处置为1.A遍历结束后,遍历数组B,对B中值为1的位置输出小点并在右边位置输出对应坐标。
●重量价值比进行非递增排序
分别创建四个二维数组,一个存储二维数组A的第一行,一个存储二维数组A的第二行,一个存储二维数组A的第三行%二维数组A的第四行,一个存储二维数组A的第三行/二维数组A的第四行。首先根据二维数组A的第三行%二维数组A的第四行进行排序,当二维数组A的第三行%二维数组A的第四行的值出现相等情况时,再根据二维数组A的第三行/二维数组A的第四行的值进行排序。注:在每次数组排序位置调整的过程中,其他三个数组中的数也要跟着调整。
●求解指定D{0-1} KP数据的最优解和求解时间
假设D{0-1}KP数据集由n组项集组成,每个项集中有三个物品,假设物品重量用weight表示,价值用value表示,则每个项集中物品的重量和价值满足以下要求:value3=value1+value2,weight3<weight1+weight2。
在不超过背包容量的前提下,背包中可以装一个或多个项,但在该项中只能选择一个物品。故难点在于不超过背包容量的前提下,如何选择项及项中的物品使背包价值最大!
具体代码未实现。
●将最优解、求解时间和解向量保存为txt文件或导出EXCEL文件
根据C语言的语法规则,将结果写入即可。此处选择输出txt文件。
4、测试运行
●已实现部分
(1)读入实验数据文件
部分结果截图如下:
评价:基本实现要求内容。
(2)散点图
部分结果截图如下:
评价:没有实现任意数据散点图的绘制以及没有坐标轴(可以实现但并不美观)。
(3)重量价值比进行非递增排序
部分结果截图如下:
评价:没有实现任意数据重量价值比的排序。
●未实现部分
(1)折扣0/1背包问题算法实现:
错误截图:
(2)将最优解、求解时间和解向量保存为txt文件或导出EXCEL文件
错误截图:
5、代码片段
●在绘制散点图时,首先求出该组D{0-1} KP数据的重量和价值的最大值,动态调整横纵坐标的最大值。然后创建一个二维数组B,数组B的大小也分别对应重量和价值的最大值,节省内存占用。
int swap1;
for(i=0;i<3*partsum;i++)
{
for(j=i;j<3*partsum;j++)
{
if(C[i]<C[j])
{
swap1=C[i];
C[i]=C[j];
C[j]=swap1;
}
}
}
for(i=0;i<3*partsum;i++)
{
for(j=i;j<3*partsum;j++)
{
if(D[i]<D[j])
{
swap1=D[i];
D[i]=D[j];
D[j]=swap1;
}
}
}
int num1=C[0],num2=D[0];//重量和价值的最大值
●从选中的某一文件中选择某组数据时,该文件中组的范围会动态变化。
while(fgets(file_str,sizeof(file_str),fp1))//逐行循环读取文件,直到文件结束
{
linesum++;
if(strstr(file_str,"DKP")) //检查字符串是否在该行中,如果在,则输出该行
{
for(i=0;i<strlen(file_str);i++)
{
if(file_str[i]=='P')
{
location[locsum]=atoi(&file_str[i+1]);
locsum++;
line[linenum]=linesum;
linenum++;
}
}
}
}
printf("请从%d-10组中输入您要测试的数据:\n",location[0]);
●输入文件的几个组数后,该组的背包容量和项集个数会根据类似“The dimension is d=3*200, the cubage of knapsack is 103936.”的代码读出,无需输入。(代码以项集数为例)
while(fgets(file_str,sizeof(file_str),fp1))//逐行循环读取文件,直到文件结束
{
if(strstr(file_str,"3*")) //检查字符串是否在该行中,如果在,则输出该行
{
for(i=0;i<strlen(file_str);i++)
{
if(file_str[i]=='*')
{
Partsum[k]=atoi(&file_str[i+1]);
k++;
break;
}
}
}
}
partsum=Partsum[0];
printf("该组数据对应的项集数为:%d\n",partsum);
6、总结
实验结果评价:
总的来说,此次个人项目虽然实现了部分功能,但是还是有很多不完善和值得改进和反思的地方。
(1)在任务一数据提取时,有些情况下数据可以被正确提取,但是,没有成功保存进二维数组,一般表现为重量行无法正确读入。
(2)任务二散点图实在草率。虽然从网上查阅到部分资料,但是并不适用于我现在的编译环境。
(3)折扣{0,1}背包问题的算法设计还是不够合理,有待改进。
模块化原则:
单一职责原则:
类的职能要单一:
遵循单一职责原则。分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险
里氏替换原则:
子类对象可以替换父类对象。子类不要增加父类没有的约束。这样会导致父类有些方法不能用。从而不能真正的实现 : 子类对象可以替换父类对象,如果子类重写了父类已实现的方法,那么子类调用的父类的方法就完全没用了,从而不是真正意义上的继承。
依赖倒置原则:
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
接口隔离原则:
在设计接口的时候,给每一个接口设计不多不少的方法,因为,如果设计的方法多了,当某个类通过接口来依赖某个类的时候,被依赖的那个类要实现的方法太多了,会造成那个类中大量的代码冗余,不可过少的原因是,接口太多,会让设计变复杂,且不便于管理。
迪米特原则:
低耦合,高内聚,即类A与类B,如果没必要依赖吗,则代码尽量不要耦合,如果这两个类要产生通信,则创建一个中间的通信类C去与这两个类进行交互。但是这样的通信类要适量。
开闭原则:
对实现封闭,对扩展开放。即当一个一个方法需要增加其他的功能,或者代码需要重构的时候,要扩展软件的行为,尽量不要去修改已有的代码。用抽象构建框架,方法的实现来扩展细节。
本次项目设计中,我的“模块化”完成的不好。我眼中C语言的模块化应该是main主函数+多个其他函数,每个函数的功能一目了然。而在我此次的项目编码中,大部分代码全部放在了main主函数里,只有动态规划算法的那部分用到了函数,这样就会显得主函数冗长,不容易读,变量的定义和管理也会很容易就出现错误,而且测试发生错误时也不容易修改代码。总的来说,还是因为前期的设计不到位,匆忙就开始编程所致。
7、PSP展示
PSP2.1 | 任务内容 | 计划共完成需要的时间(min) | 实际完成需要的时间(min) |
---|---|---|---|
Planning | 计划 | 10 | 9 |
Estimate | 估计这个任务需要多少时间,并规划大致工作步骤 | 10 | 9 |
Development | 开发 | 632 | 759 |
Analysis | 需求分析 (包括学习新技术) | 30 | 35 |
Design Spec | 生成设计文档 | 15 | 12 |
Design Review | 设计复审 (和同事审核设计文档) | 7 | 7 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
Design | 具体设计 | 10 | 10 |
Coding | 具体编码 | 540 | 660 |
Code Review | 代码复审 | 5 | 5 |
Test | 测试(自我测试,修改代码,提交修改) | 15 | 20 |
Reporting | 报告 | 30 | 40 |
Test Report | 测试报告 | 20 | 25 |
Size Measurement | 计算工作量 | 12 | 7 |
Postmortem & Process Improvement Plan | 事后总结 ,并提出过程改进计划 | 8 | 8 |
编码耗时最多,并且估计和实践相差巨大。
原因:对于C语言语法知识的遗忘以及在具体编程时的设计不合理,譬如二维数组大小。
任务4:完成任务3的程序开发,将项目源码的完整工程文件提交到你注册Github账号的项目仓库中。
任务5:完成个人项目报告博文作业
已按照要求完成。
感悟
(1)作为一名计算机专业的学生,只熟悉一门编程语言是远远不够的,比如在此次实验中,我选择的语言是C语言,但是在散点图功能的实现上却显得不是很美观大方,而对于掌握了java或Python的同学来说可能首先散点图较为容易。要成为一名未来的计算机工作者,我还要有很长很长的一段学习历程。
(2)在编码过程中,当我选择了数据较多的txt文件中的某一组数据时,程序出现了错误。与同学讨论后才发现是因为二维数组的大小设置不合理。这在我之前的C语言编程中是从来没有注意到的,也深刻体会到了细节的重要性,同时也让我明白了合作交流的重要性,“一语惊醒梦中人”,大概就是当时的感觉吧。