201971010157-张颖 实验二 个人项目-《0-1背包问题》项目报告
项目 | 内容 |
课程班级博客链接 | 2019级卓越工程师班 |
这个作业要求链接 | 实验二 软件工程个人项目 |
我的课程学习目标 |
1.掌握软件项目个人开发流程。 2.掌握Github发布软件项目的操作方法。 |
这个作业在哪些方面帮助我实现学习目标 |
1.详细阅读了《建构之法》第一、二章,了解了PSP流程 2.复习了动态规划算法、回溯算法、贪心算法解决0-1背包问题。 3.将此项目提交到Github中 |
项目Github的仓库链接地址 | https://github.com/xiaokaxi08042/text1 |
任务1:点评班级博客中本次已提交作业至少三份
作业点评链接:
201971010142-王玉慧 实验一 软件工程准备-走进软件工程
201971010241-王晨阳 实验一 软件工程准备-新人报道
201971010231-毛玉贤 实验一 软件工程准备-基本操作及《构建之法》初步三问
任务2:总结详细阅读《构建之法》第1章、第2章,掌握PSP流程
第1章-概论
- 软件=程序+软件工程
- 一个复杂的软件不但要有合理的软件架构、软件设计与实现,还要有各种文件和数据来描述各个程序文件之间的依赖关系、编译参数、连接参数等。
- 软件开发的不同阶段:玩具阶段、业余爱好阶段、探索阶段、成熟的产业阶段
- 软件工程的定义:把系统的、有序的、可量化的方法应用到软件的开发、运营和维护上的过程
- 软件的特殊性:复杂性、不可见性、易变性、服从性、非连续性
第2章-个人技术和流程
- 单元测试:让自己的负责的模块功能定义尽量明确,模块内部的改变不会影响其他模块,而且模块的质量能得到稳定的、量化的保证。
- 创建单元测试的主要步骤是:设置数据→使用被测试类型的功能→比较实际结果和预期的结果
- 好的单元测试的标准:应该在最基本的功能/参数上验证程序的正确性;必须由最熟悉代码的人来写;机器状态保持不变;要快;应该产生可重复、一致的结果;独立性;应该覆盖所有代码路径。
- 回归测试:回到以前不正常的状态。最好要自动化,就可以对于每一个构建快速运行所有回归测试,以保证尽早发现问题。
- 效能分析工具:确保程序是Release版本;两种分析方法:抽样、代码注入
- 个人开发流程(PSP):软件工程师的任务清单:
计划- 明确需求和其他相关因素,指明时间成本和依赖关系
开发- 分析需求、生成设计文档、设计复审、代码规范、具体设计、具体编码、代码复审、测试
记录用时
测试报告
计算工作量
事后总结
提出过程改进计划
- PSP的特点:不局限于某一种软件技术、不依赖考试、PSP依赖于数据、PSP的目的是记录工程师如何实现需求的效率,而不是记录顾客对产品的满意度
任务3:项目开发
1.开发背景:
背包问题(Knapsack Problem,KP)是NP Complete问题,也是一个经典的组合优化问题,有着广泛而重要的应用背景。{0-1}背包问题({0-1 }Knapsack Problem,{0-1}KP)是最基本的KP问题形式,它的一般描述为:从若干具有价值系数与重量系数的物品(或项)中,选择若干个装入一个具有载重限制的背包,如何选择才能使装入物品的重量系数之和在不超过背包载重前提下价值系数之和达到最大?
2.需求分析
(1)数据由用户键盘输入
(2)价值重量为横轴,价值为纵轴的数据散点图
(3)对一组数据按重量比进行非递增排序
(4)输出的最优解和求解时间可保存为txt文件或导出excel文件
(5)用户自主选择贪心算法、动态规划算法或回溯算法解决问题
(6)代码提交到github仓库
3.功能设计及程序流程
4.设计实现:
创建的函数有:DP(),initBackpack(),backTrace(),Sort()等等,具体画出逻辑图如下:
(1)散点图流程图
(2)动态规划算法流程图:
(3)贪心算法流程图
(4)回溯算法流程图
5.从以下几个方面制定我的编程规范,并在此次作业中严格执行:
缩进 | 使用tab |
变量命名 |
1)变量名的开头必须是字母或下划线,不能是数字;2)变量名中的字母是区分大小写的;3)变量名绝对不可以是C语言关键字;4)变量名中不能有空格 |
每行最多字符数 | 不超过256个 |
函数最大行数 | 不超过120行 |
函数、类命名 |
函数采用帕斯卡命名法;设备名_操作名() |
常量 | 整型、字符型、浮点型 |
空行规则 | 1) 文件注释区、头文件引用区、函数间应该有且只有一行空行。 2) 相邻函数之间应该有且只有一行空行。 3) 函数体内相对独立的程序块之间可以用一行空行或注释来分隔。 4) 函数注释和对应的函数体之间不应该有空行。 5) 文件末尾有且只有一行空行 |
注释规则 | 一般采用 //… ,多行注释必须采用 /*…*/ |
操作符前后空格 | 一元操作符如“!”、 “~”、“++”、“--”、“&”(地址运算符)等前后不 加空格 |
其他规则 | { 和 } 分别都要独占一行。互为一对的 { 和 } 要位于同一列,并且与引用它们的语句左对齐 |
6.代码段粘贴
(1)动态规划算法
查看代码
int DP(int n,int w[],int v[],int x[],int C)
{
FILE *fp;
fp=fopen("1.txt","w+");
int i,j;
//填表,其中第一行和第一列全为0
for(i=0;i<=n;i++)
V[i][0]=0;
for(j=0;j<=C;j++)
V[0][j]=0;
for(i=1;i<=n;i++)
{
printf("%d %d %d ",i,w[i-1],v[i-1]);
fprintf(fp,"%d %d %d ",i,w[i-1],v[i-1]);
for(j=1;j<=C;j++)
{
if(j<w[i-1])
{
V[i][j]=V[i-1][j];
printf("[%d][%d]=%2d ",i,j,V[i][j]);
fprintf(fp,"[%d][%d]=%2d ",i,j,V[i][j]);
}
else
{
V[i][j]=max(V[i-1][j],V[i-1][j-w[i-1]]+v[i-1]);
printf("[%d][%d]=%2d ",i,j,V[i][j]);
fprintf(fp,"[%d][%d]=%2d ",i,j,V[i][j]);
}
}
printf("\n");
fprintf(fp,"\n");
fclose(fp);
}
//判断哪些物品被选中
j=C;
for(i=n;i>=1;i--)
{
if(V[i][j]>V[i-1][j])
{
x[i]=1;
j=j-w[i-1];
}
else
x[i]=0;
}
printf("选中的物品是:\n");
for(i=1;i<=n;i++)
printf("%d ",x[i]);
printf("\n");
return V[n][C];
}
(2)回溯算法
查看代码
void backTrace(int position){
if((position+1) > bp.number){
//当物品全部测试后,查看最大价值量,如何新的价值量大于上一次价值量
//替换掉,并记忆存放的物品
if(bp.curValue > bp.maxValue){
bp.maxValue = bp.curValue;
for(int i=0;i < bp.number;i++){
bp.lastBackpack[i] = bp.curBackpack[i];
}
}
return;
}
(3)贪心算法
查看代码
float knapsack()
{
int i;
float x[N],sum=0;
for(i=0;i<n;i++) x[i]=0;
for(i=0;i<n;i++)
{
if(d[i].w>c) break;
x[d[i].num]=1;
sum+=d[i].v;
c=d[i].w;
}
if(i<n)
{
x[d[i].num]=c/d[i].w;
sum+=x[d[i].num]*d[i].v;
}
return sum;
}
(4)保存为txt文件——在此我将选择目录、输入的数据以及输出的数据通通导入到txt文件中,其中的两个代码片段如下:
查看代码
int xz=1; //输入选项
FILE *fp;
fp=fopen("1.txt","w+");
while(xz!=0){
printf("----------算法选择----------------\n\n");
fprintf( fp, "%s", "----------算法选择----------------\n\n");
printf(" 1.动态规划算法 \n");
fprintf( fp, "%s", " 1.动态规划算法 \n");
查看代码
printf("请输入背包的最大容量:\n");
fprintf( fp, "%s", "请输入背包的最大容量:\n");
scanf("%d",&C);
fprintf( fp, "%d\n",C);
printf("输入物品数:\n");
fprintf( fp, "%s", "输入物品数:\n");
scanf("%d",&n);
fprintf( fp, "%d\n",n);
printf("请分别输入物品的重量:\n");
fprintf( fp, "%s", "请分别输入物品的重量:\n");
for(i=0;i<n;i++)
scanf("%d",&w[i]);
fprintf( fp, "%d\n", w[i]);
printf("请分别输入物品的价值:\n");
fprintf( fp, "%s", "请分别输入物品的价值:\n");
for(i=0;i<n;i++)
scanf("%d",&v[i]);
fprintf( fp, "%d\n", v[i]);
s=DP(n,w,v,x,C);//调用动态规划
printf("最大物品价值为:\n");
fprintf( fp, "%s", "最大物品价值为:\n");
printf("%d\n",s);
fprintf(fp,"%d\n",s);
fclose(fp);
7.测试运行
(1)选择目录
(2)选择1,进入动态规划算法
(3)保存到txt文件
(4)选择2,进入回溯算法
贪心算法类似
(5)将一组KP数据进行非递增排序
(6)绘制散点图
8.你设计的程序如何实现软件设计的“模块化”原则
首先,我是将动态规划算法、回溯算法、贪心算法解决0-1背包问题的代码逐一编写、运行成功,然后将它们合并主函数,合并成为一个程序,即“模块化”的原则:自顶向下、逐步分解、分而治之,即将一个较大的程序按照功能分割成一些小模块,各模块相对独立、功能单一、结构清晰、 接口简单。
9.展示PSP,这个环节重要的是让自己看到自己的估计和实际消耗时间,哪个环节耗时最多,哪个环节估计和实践相差巨大?为什么?
PSP2.1 |
任务内容 |
计划共完成需要的时间(min) |
实际完成需要的时间(min) |
Planning |
计划 |
10 |
20 |
Estimate |
估计这个任务需要多少时间,并规划大致工作步骤 |
10 |
20 |
Development |
开发 |
470 |
770 |
Analysis |
需求分析(包括学习新技术) |
10 |
10 |
Design Spec |
生成设计文档 |
20 |
20 |
Design Review |
设计复审(和同事审核设计文档) |
10 |
10 |
Coding Standard |
代码规范(为目前的开发制定合适的规范) |
10 |
20 |
Design |
具体设计 |
20 |
30 |
Coding |
具体编码 |
100 |
180 |
code Review |
代码复审 |
100 |
200 |
Test |
测试(自我测试,修改代码,提交修改) |
200 |
300 |
Reporting |
报告 |
120 |
300 |
Test Report |
测试报告 |
90 |
270 |
Size Measurement |
计算工作量 |
10 |
10 |
Postmortem & |
事后总结,并提出过程改进计划 |
20 |
20 |
开发和报告的耗时都非常多,它们在我计划完成需要的时间和实际完成的时间相差较大,具体原因分析如下:
(1)对c语言的运用还不够熟练,浪费了大量时间调试;
(2)将几个单独的程序合成为一个程序时需要考虑很多问题,如命名的重复、函数的冲突等等;
(3)在此之前我只会将特定的信息写入代码,并将它保存到txt文件,本次项目是将程序运算出来的结果保存到txt文件,了解并学习了这种方式;
(4)在写报告时不小心关闭导致报告没有保存。
(5)提交代码到github时遇到了多种错误
10.总结与心得
在本次项目中,有很多不完美的地方需要我去学习和改进,如画散点图时,由于编译器不能识别#include<graphics.h>,即没有头文件中的库函数而不能调用图像库;用turbo c与64位电脑没有办法兼容,散点图的c语言代码没有运行出来,所以我使用了python画散点图。通过本次项目,我了解到PSP的概念,掌握了个人创建软件项目的开发流程,感受到一个个人软件项目应该怎样去完成,受益匪浅。
任务4:完成任务3的程序开发,将项目源码的完整工程文件提交到你注册Github账号的项目仓库中
已完成提交
1.github中commit的使用
2.创建release