201871010126-王亚涛 实验二 个人项目—0-1背包问题项目报告
| 项目 | 内容 |
| ---- | ---- | ---- |
| 课程班级博客 |班级博客|
| 这个作业要求链接 |作业要求 |
| 我的课程学习目标 | 1、通过这次课程学习,掌握个人软件项目开发流程
2、在上一次了解了Github的基础上,学习了Github发布软件项目的操作方法。
3、再次仔细阅读了构建之法的第一二章内容,对PSP有所掌握
|
|这个作业在哪些方面帮助我实现学习目标| 1、第一次接触了PSP流程,对个人软件项目开发流程的特点和基础有了基本掌握
2、了解了Github发布软件项目的操作方法。
3、通过自己学习了动态规划算法和回溯法,通过实验任务对背包问题基本掌握,并学会使用相关算法解决背包问题 |
|项目Github的仓库链接地址| |
博客正文
- 任务1:阅读教师博客“常用源代码管理工具与开发工具”内容要求,点评班级博客中已提交相关至少3份作业。
- 任务2:详细阅读《构建之法》第1章、第2章,掌握PSP流程
- 完成情况
通过阅读我了解到PSP流程(个人软件开发流程),并用PSP流程进行了背包问题项目设计与实现。
- 完成情况
- 任务3:作为任务3的项目实施过程文字资料,请完整包含下面7个部分:
- 需求分析
- 总体思路:根据动态规划解题步骤(问题抽象化、建立模型、寻找约束条件、判断是否满足最优性原理、找大问题与小问题的递推关系式、填表、寻找解组成)找出01背包问题的最优解以及解组成,然后编写代码实现。
- 具体分析:首先对于0-1背包问题,我们需要知道的是:每一个物品只有1个,要么全拿,要么不拿,最后使得拿到的物品的总价值最大。
背包问题可以说是一个老生常谈的问题,通常被用作面试题来考查面试者对动归的理解,我们经常说学算法,初学者最难理解的就是 “二归”,一个叫递归,另一个叫动归。背包问题属于特殊的一类动归问题,也就是按值动归;通常背包这一类题目,题目大概就是给你一个容量或者大小固定的背包,然后要求你去用这个背包去装物品,一般来说这些物品都是大小固定的,但是题目对物品的限定不同,衍生出来多种背包问题,例如 0-1 背包 问题中,物品个数有且仅有一个;完全背包 问题中的物品个数是无限的;多重背包 问题中的针对不同的物品,个数不一样。通常题目会要你求出背包能装的最大价值(每个物品都会有容量和价值),当然也会有不一样的问法,类似背包能否被装满,还有背包能装的最大容量是多少,多少种方式填满背包。但是这些并不是背包问题的所有,还有分组背包问题,依赖背包问题等。 - 相关算法分析:1、动态规划算法:动态规划(简称DP)是算法设计思想当中最难也是最有趣的部分了,动态规划适用于有重叠子问题和最优子结构性质的问题,是一种在数学、计算机科学和经济学中经常使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。使用动态规划方法解题有较高的时间效率,关键在于它减少了很多不必要的计算和重复计算的部分它的思想就是把一个大的问题进行拆分,细分成一个个小的子问题,且能够从这些小的子问题的解当中推导出原问题的解。
2、回溯算法:回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
-
功能设计,获得题目需求后,要对项目做功能设计,但题目需求是项目的基本功能要求,自己思考和调研会有超出题目要求的需求,甚至你的奇思妙想会设计出特色的功能。因此,功能会有:
- 基本功能:1.可正确读入实验数据文件的有效D{0-1}KP数据;
2.能够绘制任意一组D{0-1}KP数据以重量为横轴、价值为纵轴的数据散点图;
3. 能够对一组D{0-1}KP数据按项集第三项的价值:重量比进行非递增排序;
4.用户能够自主选择动态规划算法、回溯算法求解指定D{0-1} KP数据的最优解和求解时间(以秒为单位);
- 基本功能:1.可正确读入实验数据文件的有效D{0-1}KP数据;
-
设计实现
-
思路分析:
weight[i]表示第i个物品的重量
value[i]表示第i个物品的价值
target表示背包目标的容量 -
代码及注释
`
package suanfa.dynamic;
public class KnapsackProblem {
public static void main(String[] args) {
int[] weight={1,4,3,2,5,6,7};
int[] value= {100,90,110,105,120,190,170};
int target=10;
//二维数组 记录每个重量下的最大value值
int[][] v=new int[value.length+1][target+1];
//二维数组 记录是否装入背包的物体状态
int[][] path=new int[value.length+1][target+1];
//将第一行和第一列置为0
for(int i=0;i<v[0].length;i++) {
v[0][i]=0;
}
for(int j=0;j<v.length;j++) {
v[j][0]=0;
}
//动态计算该数组 找出每个质量下的最大价值 在背包能够承受的最大重量下
for(int i=1;i<v.length;i++) {
for(int j=1;j<v[0].length;j++) {
if(weight[i-1]>j) {
v[i][j]=v[i-1][j];
}else {
if((v[i-1][j])>=value[i-1]+v[i-1][j-weight[i-1]]) {
v[i][j]=v[i-1][j];
}else {
v[i][j]=value[i-1]+v[i-1][j-weight[i-1]];
path[i][j]=1;
}
}
}
}//输出重量图 for(int i=0;i<v.length;i++) { for(int j=0;j<v[0].length;j++) { System.out.printf("%d\t",v[i][j]); } System.out.println(); } System.out.println("====================="); //输出物品存放的状态信息 for(int i=0;i<path.length;i++) { for(int j=0;j<path[0].length;j++) { System.out.printf("%d\t",path[i][j]); } System.out.println(); } //输出最大重量下的物品存取信息 int i=path.length-1; int j=path[0].length-1; while(i>0&&j>0) { if(path[i][j]==1) { System.out.println("将第"+i+"个 物品放入背包"); j-=weight[i-1]; } i--; } System.out.println("在质量不超过"+target+"的情况下"+"最大的背包价值为"+v[v.length-1][v[0].length-1]);;
-
}
}`
-
测试运行
-
总结:你设计的程序如何实现软件设计的“模块化”原则。(5分)
一、问题的提出
软件模块化的目的是建立可重用的软件组件,在不需要修改或仅作少量修改的情况下,可再次用来组建新的软件系统,提高软件的开发周期和可靠性。由于软件
模块有在不同的系统中使用的需求,决定了软件模块的设计和专用于单一系统的软件代码存在差别,这具体体现在以下方面:1. 软件模块需要更好的弹性,增加适用范围;2. 软件模块需要有更好的可移植性,增加适用范围;3. 软件模块需要更清晰地定义接口,避免模糊含混;4. 软件模块需要有更好的稳定性,避免错误影响范围扩散;5. 软件模块需要有更好的独立性,避免引用牵连模块,形成依赖链。
二、解决思路
如何提供更好的设计弹性?
长期的软件实践表明,良好的弹性来自于抽象。引入更多的更高层次的抽象会增加软件的弹性。而一个抽象的层次高低来自于设计者对问题域的本质认识的深刻程
度,所以要设计更有弹性的模块,首先需要提高自己对模块所解决的问题的认识。额外的抽象层次基本上都会带来性能损失,所以设计上应该考虑到性能和弹性的平衡,但在性能问题没有确证之前,弹性优先。弹性带来的另一个问题是复杂性,过于复杂的软件会带来理解和维护上的困难,所以过度的弹性设计也是不可取的。
改进模块弹性的一般的手段有:
1. 采用接口提供服务而非具体实例。通过不同的方案实现接口,可提供弹性,而对客户不产生影响;2. 提供可扩展机制,允许客户自行添加新的功能来强化模块;
3. 提供配置选项,允许客户定制模块的部分行为。
如何提供更好的可移植性?
可移植性是指让软件在不同平台上运行的难易程度,不需要任何修改就可以运行的就称为跨平台的。实际上真正跨平台的软件是不存在的,任何跨平台的软件必须有一个抽象的底层来支撑,这个支撑的部分是需要在不同的平台上重新实现的,也就是软件的需移植部分。认识到这一点,就可以看到,增加一个软件模块的可移植性,需要做两件事:分离不需要移植和需要移植的部分、减小需要移植部分的复杂度以方便移植。
通常,采用C语言开发的软件,标准C库是很好的可移植库;采用C++开发的软件,选择STL也会获得很好的可移植性;对于网络编程,socket api 也是受绝大多数系统平台支持的,有很好的可移植性。不同的编译器之间也会存在差异,编码时采用标准C/C++语法,避免使用特定编译器的扩展语法,也会改进可移植性。采用一些跨平台第三方库一般也能带来较好的可移植性,但在采用之前需要考虑是否开放源代码、是否收费等因素。
总之,一个软件模块的可移植性好坏,也是取决于设计者对所有目标编译/运行平台的熟悉程度。增加对各个目标的平台了解,有助于设计出具有更好可移植性的模块。 -
展示PSP:
- PSP设计过程:
PSP设计中的4个因素:
1、项目/任务大小(代码行数):120
2、花的时间(小时):50H
3、质量如何:代码在自己编写的过程中遇到的问题较多
4、是否按时交付:按时交付
- PSP设计过程:
PSP 各个阶段 | 自己预估的时间(小时) | 实际的记录(小时) |
---|---|---|
计划: 明确需求和其他因素,估计以下的各个任务需要多少时间 | 1 | 2 |
开发 | 预估时间 | 实际开发时间 |
需求分析 | 5 | 9 |
生成设计文档 | 1 | 1.5 |
设计复审 | 3 | 5 |
代码规范 | 4 | 3 |
具体设计 | 5 | 6 |
具体编码 | 8 | 12 |
代码复审 | 1 | 3 |
测试 | 1 | 1 |
报告 | 1 | 2 |
测试报告(发现了多少bug,修复了多少) | 3 | 5 |
事后总结, 并提出改进计划 (包括写文档、博客的时间) | 1 | 1 |
总共花费的时间 (小时) | 50.5 | |
原因:PSP设计过程中未把握好时间,导致作业过迟提交需求分析的过程中耗时太多,编程能力太差,在代码编写的过程中遇到的问题很多,导致时间花费太多。 |