项目 | 内容 |
---|---|
课程班级博客链接 | 班级博客 |
这个作业要求链接 | 作业要求 |
我的课程学习目标 | (1)阅读《构建之法》,理解并掌握PSP基本流程;(2)学习并解决背包问题。 |
这个作业在哪些方面帮助我实现学习目标 | PSP基本流程、代码练习、项目开发基本流程 |
项目Github的仓库链接地址 | 仓库链接 |
博客正文
任务1
阅读教师博客“常用源代码管理工具与开发工具”内容要求,点评班级博客中已提交相关至少3份作业。
作业点评链接如下:
(1)https://www.cnblogs.com/weinana/p/14550875.html
(2)https://www.cnblogs.com/baofengmei/p/14544245.html
(3)https://www.cnblogs.com/xiejinxin/p/14548968.html
任务2
详细阅读《构建之法》第1章、第2章,掌握PSP流程。
第1章
重要概念:软件=程序+软件工程
第2章
涉及到几个重要的概念,单元测试,回归测试,效能分析PSP(个人软件开发流程)。
- 单元测试:程序要进行单元测试来保证程序的健壮性。
用VSTS写单元测试,要先编写代码从而可以创建新的单元测试,之后solution Explorer中出现三个新的文件。双击设置文件进入管理及设置界面,让单元测试产生代码覆盖报告。(在单元测试中,VSTS自动生成了测试的股价,但最起码还要将//TODO标注的事情做完)。之后创建单元测试函数,运行单元测试。
好的单元测试标准:在最基本的功能/参数上验证程序的正确性;必须由最熟悉代码的人来写;单元测试过后,机器状态保持不变;单元测试要快;应该产生可重复,一致的结果;独立性,不依赖于别的测试,可以人为构造数据,以保持单元测试的独立性;应覆盖所有代码路径;单元测试应该集成到自动测试的框架中;必须和产品一起保存和维护。 - 回归测试:如果在原版本上运行的测试用例通过的话,在下一版本上再运行时,却没有通过,这就是软件"退化",所以需要进行回归测试。在新版本上运行所有已经通过的测试用例,来验证后面的版本没有出现软件"退化"的情况。但是如果是模块功能发生了变化,那么测试用例也需要修改来测试新的模块。
- 效能分析:就是找出程序运行时,哪个函数或方法消耗的时间多,就是程序运行的瓶颈所在,进行效能分析,从而对相应模块的代码进行优化。
进行效能分析的方法有抽样和代码注入,各有优缺点。不过普遍用的是先用抽样方法找到瓶颈所在,然后对特定模块的代码用代码注入的方法进行详细分析。还要注意避免没有做分析就过早进行"效能提高"。 - PSP(Personal Software Process),个人软件开发流程的任务清单如下所示:
一个软件工程师接到一个任务之后:
任务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文件。
任务4
完成任务3的程序开发,将项目源码的完整工程文件提交到你注册Github账号的项目仓库中。(50分)
前期知识学习:
动态规划算法:动态规划算法是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。
回溯算法:回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。其实回溯法就是对隐式图的深度优先搜索算法。
-
需求分析
背包问题(Knapsack Problem,KP)是一个经典的组合优化问题,组合优化问题的求解方法研究已经成为了当前众多科学关注的焦点,这不仅在于其内在的复杂性有着重要的理论价值,同时也在于它们能在现实生活中广泛的应用。比如资源分配、投资决策、装载设计、公交车调度等一系列的问题都可以归结到组合优化问题中来。但是,往往由于问题的计算量远远超出了计算机在有效时间内的计算能力,使问题的求解变为异常的困难。尤其对于NP完全问题,如何求解其最优解或是近似最优解便成为科学的焦点之一。
应用前景很好。 -
功能设计
(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文件。 -
设计实现
-
测试运行
-
粘贴自己觉得比较独特的或满意的代码片段,用博客园的代码控件来显示。
#include <stdio.h>
#define N 3 //物品的数量
#define C 16 //背包的容量
int w[N]={10,8,5}; //每个物品的重量
int v[N]={5,4,1}; //每个物品的价值
int x[N]={0,0,0}; //x[i]=1代表物品i放入背包,0代表不放入
int CurWeight = 0; //当前放入背包的物品总重量
int CurValue = 0; //当前放入背包的物品总价值
int BestValue = 0; //最优值;当前的最大价值,初始化为0
int BestX[N]; //最优解;BestX[i]=1代表物品i放入背包,0代表不放入
//t = 0 to N-1
void backtrack(int t)
{
//叶子节点,输出结果
if(t>N-1)
{
//如果找到了一个更优的解
if(CurValue>BestValue)
{
//保存更优的值和解
BestValue = CurValue;
for(int i=0;i<N;++i) BestX[i] = x[i];
}
}
else
{
//遍历当前节点的子节点:0 不放入背包,1放入背包
for(int i=0;i<=1;++i)
{
x[t]=i;
if(i==0) //不放入背包
{
backtrack(t+1);
}
else //放入背包
{
//约束条件:放的下
if((CurWeight+w[t])<=C)
{
CurWeight += w[t];
CurValue += v[t];
backtrack(t+1);
CurWeight -= w[t];
CurValue -= v[t];
}
}
}
}
-
“模块化”涉及原则
“模块化”涉及原则 | 解释 |
---|---|
单一职责原则 | 类的职能要单一:遵循单一职责原则。分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险 |
里氏替换原则 | 子类对象可以替换父类对象。子类不要增加父类没有的约束。这样会导致父类有些方法不能用。从而不能真正的实现 : 子类对象可以替换父类对象,如果子类重写了父类已实现的方法,那么子类调用的父类的方法就完全没用了,从而不是真正意义上的继承。 |
依赖倒置原则 | 高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。 |
接口隔离原则 | 在设计接口的时候,给每一个接口设计不多不少的方法,因为,如果设计的方法多了,当某个类通过接口来依赖某个类的时候,被依赖的那个类要实现的方法太多了,会造成那个类中大量的代码冗余,不可过少的原因是,接口太多,会让设计变复杂,且不便于管理。 |
迪米特原则 | 低耦合,高内聚,即类A与类B,如果没必要依赖吗,则代码尽量不要耦合,如果这两个类要产生通信,则创建一个中间的通信类C去与这两个类进行交互。但是这样的通信类要适量。 |
开闭原则 | 对实现封闭,对扩展开放。即当一个一个方法需要增加其他的功能,或者代码需要重构的时候,要扩展软件的行为,尽量不要去修改已有的代码。用抽象构建框架,方法的实现来扩展细节。 |
-
PSP流程:
PSP2.1 | 任务内容 | 计划共完成需要的时间(min) | 实际完成需要的时间(min) |
---|---|---|---|
Planning | 计划 | 15 | 15 |
· Estimate | · 估计这个任务需要多少时间,并规划大致工作步骤 | 10 | 12 |
Development | 开发 | 960 | - |
·· Analysis | 需求分析 (包括学习新技术) | 40 | 50 |
· Design Spec | · 生成设计文档 | 10 | 10 |
· Design Review | · 设计复审 (和同事审核设计文档) | - | - |
· Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 20 | - |
· Design | 具体设计 | 580 | - |
· Coding | 具体编码 | 600 | - |
· Code Review | · 代码复审 | 100 | - |
· Test | · 测试(自我测试,修改代码,提交修改) | 200 | - |
Reporting | 报告 | 15 | 20 |
·· Test Report | · 测试报告 | 5 | 5 |
· Size Measurement | 计算工作量 | 8 | 7 |
· Postmortem & Process Improvement Plan | · 测试报告 | 6 | 6 |
·· Test Report | · 事后总结 ,并提出过程改进计划 | 5 | 8 |
总结心得
基本完成了任务1和任务2,通过阅读《构建之法》了解并掌握了PSP基本流程,关于任务3,只能按照实验内容做适当的分析,利用网络学习了相关算法,但是对算法仍然不能熟练地掌握,其实是,编程能力实在有限,未能顺利地完成实验任务,以后定会努力学习,多加改进。