综合设计性实验报告背包问题的多种算法设计与分析.doc

上传人:仙人指路1688 文档编号:3991138 上传时间:2023-03-30 格式:DOC 页数:28 大小:84KB
返回 下载 相关 举报
综合设计性实验报告背包问题的多种算法设计与分析.doc_第1页
第1页 / 共28页
综合设计性实验报告背包问题的多种算法设计与分析.doc_第2页
第2页 / 共28页
综合设计性实验报告背包问题的多种算法设计与分析.doc_第3页
第3页 / 共28页
综合设计性实验报告背包问题的多种算法设计与分析.doc_第4页
第4页 / 共28页
综合设计性实验报告背包问题的多种算法设计与分析.doc_第5页
第5页 / 共28页
点击查看更多>>
资源描述

《综合设计性实验报告背包问题的多种算法设计与分析.doc》由会员分享,可在线阅读,更多相关《综合设计性实验报告背包问题的多种算法设计与分析.doc(28页珍藏版)》请在三一办公上搜索。

1、0-1背包问题的多种算法设计与分析一、实验内容和要求: 0-1背包问题是一例典型的组合优化的NP完全问题。问题可以描述为:给定一组共n个物品,每种物品都有自己的重量wi, i=1n和价值vi, i=1n,在限定的总重量(背包的容量C)内,如何选择才能使得选择物品的总价值之和最高。选择最优的物品子集放置于给定背包中,最优子集对应n元解向量(x1,xn), xi0或1,因此命名为0-1背包问题。0-1背包问题是许多问题的原型,但它又是一个NP完全问题。此实验主要研究和实现n(0=n=200)和C(C=2000, C为整数)都较大的情形,随机产生n个物品的重量向量wi(1=wi=100, wi为整数

2、)和价值向量vi (1=vi=100, vi为整数)。0-1背包问题可以用许多方法来求解,有些算法可以得到问题的精确最优解,有些仅能获得一个近似最优解。本综合设计性实验要求用3种以上的方法求解0-1背包问题,获得精确最优解或近似最优解皆可,并对所采用的多种算法从运行时间、寻找是否为最优解、能够求解的问题规模等方面进行对比和分析。本课程讲述的算法思想都可以用来求解此问题,甚至本课程未涉及的许多算法也非常适合于求解此问题,学生可以先尝试先用本课程介绍的算法来实现和分析,学有余力或兴趣驱动下可以寻找一些智能算法的资料来试一试。涉及的方法可以有:蛮力求解、递归求解、动态规划求解、贪心求解、回溯法求解、

3、广度优先的分支限界法求解,优先队列的启发式分支限界法、遗传算法、模拟退火算法、蚁群算法、粒子群算法等。为方便调试,采用文件输入,标准输出(或文件输出也可)的形式。数据输入的格式如下:每组测试数据包含n+1行,第1行为C和n,表示背包容量为C且有n个物品,接下来n行为这n个物品的重量wi和价值vi。背包容量和物品重量都为整数。n, C , wi, vi范围如上所述。输出两行。第一行为所选物品的最大价值之和,第二行为装入背包的物品所对应的n元最优解向量(x1,xn), xi0或1。二、多种算法详细设计1. 贪心算法 输入物品价值和重量。分别用数组vo.n和w0.n来记录。根据物品价值vi/wi由高

4、到低对vo.n和w0.n进行排序。然后把物品从1到n依次加入背包,直到装不下为止。设c为背包容量,每次装一个物品,则c-=wi,当前价值为value+=vi,当c=wi =m(i+1,j) 0=j=wn =0 0=jwn 用二维数组mij存储m(i,j)的值。根据递归式算出mij的值。则m1c是给出所要求的0-1背包问题的最优值。相应的最优解可由算法Traceback计算如下。如果m1c=m2c,则x1=0,否则x1=1.当x1=0时,由m2c继续构造最优解。当x1=1时,由m2c继续构造最优解。当x1=1时,由m2c-w1继续构造最优解。依次类推,可构造出相应的最优解(x1,x2,.,xn)

5、。 void Traceback(int *m,int n,int *w,int c,int *x)int i;if(m1c=m2c)x1=0;else x1=1;for(i=1;i=n-2;i+)if(xi=1)c-=wi;if(mi+1c=mi+2c)xi+1=0;else xi+1=1;if(mnc!=0)xn=1;3. 动态规划基于递归式的讨论,用二维数组m来存储m(i,j)的相应值,可设计解0-1背包问题的动态规划算法Knapsack如下:templatevoid Knapsack(Type* v,int* w,int c,int n,Type* m)int jMax=min(wn-

6、1,c);for(int j=0;j=jMax;j+) mnj=0;for(j=wn;j1;i-)jMax=min(wi-1,c);for(j=0;j=jMax;j+) mij=mi+1j;for(j=wi;j=w1) m1c=max(m1c,m2c-w1+v1);template void Traceback(Type *m,int *w,int c,int n,int *x)fpr(int i=1;in;i+)if(mic=mi+1c) xi=0;else xi=1;c-=wi;xn=(mnc)?1:0;4. 回溯法求解 0-1背包问题是子集选取问题。一般情况下,0-1背包问题是NP难的。

7、0-1背包问题的解空间可用子集树表示。在搜索解空间树时,只要其左儿子结点是一个可行结点,搜索就进入其左子树。当右子树中有可能包含最优解时才进入右子树搜索。否则将右子树剪去。设r是当前剩余物品价值总和;cp是当前价值;bestp是当前最优价值。计算右子树上界的方法是将剩余物品依其单位重量价值排序,然后依次装入物品,直至装不下去时,再装入该物品的一部分而装满背包。由此得到的价值是右子树中解的上界。为了计算上界,可先将物品依其单位重量价值从小到大排序,此后只要按顺序考察各物品即可。将物品依其单位价值排序的算法如下:class Objectfriend int Knapsack(int*,int*,i

8、nt,int,int*);friend void Sort(Object*,int);public:int operator=a.d);private:int ID;float d;/单位重量价值;void Sort(Object *Q,int len) for(int i=0;ilen;i+) for(int j=0;jlen;j+) if(Qi=Qj) Object temp=Qi; Qi=Qj; Qj=temp; 在实现时,由Bound计算当前结点处的上界。类Knap的数据成员记录解空间树中的结点信息,以减少参数传递及递归调用所需的栈空间。在解空间树的当前扩展结点处,仅当要进入右子树时才

9、计算上界与其父节点的上界Bound,以判断是否可将右子树剪去。进入左子树时不需要计算上界,因为其上界与其父结点的上界相同。在计算最优值的时候也要在另设一个数组bestx1.n来记录最优解,在Backtrack算法中回溯得到bestx,当得到第一个最优值,若后面回溯到叶结点时值不大于当前最优值,则最优解bestx不改变。Backtrack中得到最优值和最优解的算法如下:templatevoid Knap:Backtrack(int i)int flag;if(in)/到达叶节点flag=bestpcp;if(flag)bestp=cp;for(i=1;i=n;i+)bestxIDi=xIDi;r

10、eturn;if(cw+wibestp)/进入右子树xIDi=0;Backtrack(i+1);5. 分支界限法 在解0-1背包问题的优先队列式分支限界法中,活结点优先队列中结点元素N的优先级由该结点的上界函数Bound计算出的值uprofit给出。上界函数与回溯法一样。子集树中以结点N为根的子树中任一结点的价值不超过N.profit。可用一个最大堆来实现活结点优先队列。堆中元素类型为HeapNode。其私有成员有uprofit,profit,weight和level。对于任意活结点N,N.weight是结点N所相应的重量;N.profit是N所相应的价值;N.uprofit是结点N的价值上界

11、,最大堆以这个值作为优先级。子集空间树中结点类型为bbnode。 类Knap的成员bestx用来记录最优解。Bestxi=1当且仅当最优解含有物品i。 算法MaxKnapsack实施对子集树的优先队列式分支限界搜索。 算法中E是当前扩展结点;cw是该结点所相应的重量;cp是相应的价值;up是价值上界。算法的while循环不断扩展结点,直到子集树的叶结点成为扩展结点时为止。此时优先队列中所有结点的价值上界均不超过该结点的价值。因此该叶结点相应的解为问题的最优解。 在while循环内部,算法首先检查当前扩展结点的左儿子结点的可行性。如果该左儿子结点是可行结点,则将它加入到子集树和活结点优先队列中。

12、当前扩展结点的右儿子结点一定是可行结点,仅当右儿子结点满足上界约束时才将它加入子集树和活结点优先队列。 建立最大堆MaxHeap,并对HeapNode类中做相应修改,目的是为了使最大堆能根据N.uprofit值作相应调整。修改如下:templateclass HeapNodefriend Knap;public:int operator (HeapNode a)const return uprofit (HeapNode a)const return uprofita.uprofit;private:Typep uprofit,/结点的价值上界profit;/结点所相应的价值Typew wei

13、ght;/结点所相应的重量int level;/活结点在子集树中所处的层序号bbnode *ptr;/指向活结点在子集树中相应结点的指针;三、 多种算法调试和测试1. 贪心算法 对于背包问题,贪心选择最终可得到最优解,但对于0-1背包问题,贪心选择无法保证最终能将背包装满,部分闲置的背包空间使单位背包空间的价值降低了。测试中数据基本与最优值相似,有的则相差较大。贪心算法得出的结不一定是最优解。2.递归求解 在编写递归的算法,最关键的就是怎样用二维数组来表示相应最优解的值,编写的时候需要注意传值。思路清晰,把数学的递归式用代码函数表示出来。 对于递归求解,算法简单,但消耗时间太长。在数据较少的情

14、况下,可以很快得到正确结果,当数据达到35个数据的时候,需要较长时间,当数据达到40个以上的时候,由于时间太长则等待不出结果。可见递归的效率很低。 针对递归算法效率较低,可采用动态规划进行改进。3. 动态规划 采用动态规划算法,效率较高。用所提供的数据测试,均能很快得出正确结果。 在编写动态规划算法时,没有太大问题,只需要编写求出最大值和最小值的函数。还有回溯求最优解的算法。4. 回溯法求解 在编写该算法时首先遇到的第一个问题是要运用C+的一些语法,这对于只懂C的同学确实存在一下障碍。好在我们的小组成员有些人之前学过,所以在这方面问题不大,再加上不懂时通过网上查阅资料和相关书籍,问题很快就得到

15、解决。 另外,就是在Backtrack算法中求出最优值和最优解,每次当剩余的价值大于bestp,则进入右子树进行搜索。当到达叶结点时即得到一个最优值,把值赋给bestp,得到新的最优值。同时需记录最优解,此时把记录路径的数组x1.n的值赋给bestx1.n。 在记录最优解时需要注意一个问题。因为回溯的顺序是按照物品单位价值从高到低的顺序进行的,而输出的最优解则需要按照原顺序输出。所以在回溯中记录路径时,需注意记录的是原来次序下的路径,而不是排序后的路径。在这里我们在排序之前已经定义好一个类Q。类中的数据成员ID用来记录物品原来的位置。则排序后,物品原来的位置记录仍能被找到。则搜索到某个结点时,

16、则xIDi=1,否则xIDi=0.5. 分支界限法 分支限界法的与回溯法不同的是前者是深度优先搜索,后者是广度优先搜索。分支限界法的效率会高些,但是需要建立一个最大堆作为优先队列。定义一个类MaxHeap来实现优先队列,定义如下:template class MaxHeap public: MaxHeap(T a, int size,int maxsize=50); MaxHeap(int maxsize=50); virtual MaxHeap(); void Initialize(T arrays,int size,int array_size);/用数组对堆重新进行初始化 MaxHeap

17、& Insert( T value); MaxHeap& DeleteMax(T& value ); bool IsEmpty()return CurrentSize=0?true:false; int Size()return CurrentSize; void Init(T a);private: void ModifyUp(int start); void ModifyDown(int start,int end);private: T *Data; int MaxSize; int CurrentSize;调试后小结:从编程到调试到可以运行中总会遇到各种各样的问题。在本次综合实验中我们

18、一会颇深,在试验中我们学到了新知识,一些C+的语法,一些调试的技巧,以及根据纠错提示改错。同时也巩固了一些旧的知识,如文件的读写操作,输入输出。先把总结如下:(1) C+的语法,定于各种类,增加了程序可读性,是程序比较容易理解。同时在类中定义友元类或者友元函数,使类之间的某些操作不受限制,方便程序员的编写,完善功能。另外,类中的函数可在类中先申明,然后类在外定义,也是非常方便的。还有运算符的重载,以及类模板,函数模板的使用都增加了程序的可读性和易编写性。(2) 说来有点惭愧,在这次综合实验中,才真正学会VC的单步调试和断点,这给我们编写程序带来极大便利。另外通过查看每歩程序的输出,也可以方便的

19、找出错误。(3) 这里是一些纠错提示: 1.C程序编译时出现warnning no newline at end of file。解决办法:在文件最后添加一个空白行。 2.LINK : fatal error LNK1168: cannot open Debug/Test.exe for writing解决办法: 打开任务管理器,将test.exe进程杀掉,然后重新编译链接 (4)数据的格式读入,由于数据很多,所以采用文件输入比较方便。FILE *fp;fp=fopen(D:bag.txt,r);/以读的方式打开文件if(!fp)printf(file cannot be opened);ex

20、it(1);fscanf(fp,%Ld%Ld,&c,&n);数据的文件格式输出FILE *ptr;ptr=fopen(D:result.txt, w);/以写的方式打开文件fprintf(ptr,%dn,value); for(i=1;i=n;i+)fprintf(ptr,%d %dn,i,xi); 最后还要关闭文件。fclose(fp);fclose(ptr);四、 多种算法对比算法运行时间/s(输入数据个数)是否为最优解能够求解问题规模2030401002001.贪心算法00000否-2.递归求解00.15614.07800是40+3.动态规划0000.0150.015是-4.回溯法000

21、00.015是-5.分支限界法00000是-五、附录多种算法实现清单:带注释和功能模块说明的源程序清单:1.贪心算法#includeusing namespace std;void sort2(int n,int *v,int *w,int*& sort)int i,j,temp;for(i=2;i=n;i+)for(j=1;j=i;j+)if(vsorti/wi=1;i-)c-=wsorti;if(c=0)xsorti=1;value+=vsorti;else break;return value;int main()int n,c,i,value;FILE *fp;FILE *ptr;in

22、t *x,*w,*v,*sort;fp=fopen(D:bag.txt,r);/以读的方式打开文件if(!fp)printf(file cannot be opened);exit(1);fscanf(fp,%Ld%Ld,&c,&n);x=new intn+1;w=new intn+1;v=new intn+1;sort=new intn+1;for(i=1;i=n;i+)sorti=i; xi=0;for(i=1;i=n;i+)fscanf(fp,%Ld%Ld,&wi,&vi);sort2(n,v,w,sort);/从小到大value=Greedy(n,w,v,x,c,sort);/调用函数

23、ptr=fopen(D:result.txt, w);/以写的方式打开文件fprintf(ptr,%dn,value); for(i=1;i=n;i+)fprintf(ptr,%d %dn,i,xi); fclose(fp);fclose(ptr);return 0;2.递归求解#includeusing namespace std;int ma(int i,int j,int n,int *&m,int *w,int *v)int a,b,value;if(i=n) if(j=wn)value=vn;else value=0;return value;if(i=wi)mi+1j=ma(i+1

24、,j,n,m,w,v);a=mi+1j;mi+1j-wi=ma(i+1,j-wi,n,m,w,v);/递归的调用b=mi+1j-wi+vi;if(ab)value=a;else value=b;return value;elsemi+1j=ma(i+1,j,n,m,w,v);/递归的调用value=mi+1j;return value;elsereturn 0; void Traceback(int *m,int n,int *w,int c,int *x)int i;if(m1c=m2c)x1=0;else x1=1;for(i=1;i=n-2;i+)if(xi=1)c-=wi;if(mi+

25、1c=mi+2c)xi+1=0;else xi+1=1;if(mnc!=0)xn=1;int main()int n,c,i;FILE *fp;FILE *ptr;int *x,*w,*v,*m;fp=fopen(D:bag.txt,r);/以读的方式打开文件if(!fp)printf(file cannot be opened);exit(1);fscanf(fp,%Ld%Ld,&c,&n);x=new intn+1;w=new intn+1;v=new intn+1;m=new int*n+1;for(i=1;i=n;i+)mi=new intc+1;xi=0;fscanf(fp,%Ld%

26、Ld,&wi,&vi);for(i=1;i=wn)mni=vn;else mni=0;m1c=ma(1,c,n,m,w,v);/调用函数Traceback(m,n,w,c,x);/调用函数ptr=fopen(D:result.txt, w);/以写的方式打开文件fprintf(ptr,%dn,m1c); for(i=1;i=n;i+)fprintf(ptr,%d %dn,i,xi); fclose(fp);/关闭文件fclose(ptr);/关闭文件return 0;3. 动态规划#includeusing namespace std;int min(int a,int b)if(ab)ret

27、urn a;else return b;templatevoid Knapsack(Type* v,int* w,int c,int n,Type* m)int jMax=min(wn-1,c);for(int j=0;j=jMax;j+) mnj=0;for(j=wn;j1;i-)jMax=min(wi-1,c);for(j=0;j=jMax;j+) mij=mi+1j;for(j=wi;j=w1) m1c=max(m1c,m2c-w1+v1);template void Traceback(Type *m,int *w,int c,int n,int *x)fpr(int i=1;in;i

28、+)if(mic=mi+1c) xi=0;else xi=1;c-=wi;xn=(mnc)?1:0; void Traceback(int *m,int n,int *w,int c,int *x)int i;if(m1c=m2c)x1=0;else x1=1;for(i=1;i=n-2;i+)if(xi=1)c-=wi;if(mi+1c=mi+2c)xi+1=0;else xi+1=1;if(mnc!=0)xn=1;int main()int n,c,i;FILE *fp;FILE *ptr;int *x,*w,*v,*m;fp=fopen(D:bag.txt,r);/以只读的方式打开文件i

29、f(!fp)printf(file cannot be opened);exit(1);fscanf(fp,%Ld%Ld,&c,&n);x=new intn+1;w=new intn+1;v=new intn+1;m=new int*n+1;for(i=1;i=n;i+)mi=new intc+1;xi=0;fscanf(fp,%Ld%Ld,&wi,&vi);Knapsack(v,w,c,n,m);Traceback(m,n,w,c,x);ptr=fopen(D:result.txt, w);fprintf(ptr,%dn,m1c); for(i=1;i=n;i+)fprintf(ptr,%d

30、 %dn,i,xi); fclose(fp);fclose(ptr);/关闭两个文件return 0;4.回溯法求解#includeusing namespace std;templateclass Knapfriend Typep Knapsack(Typep*,Typew*,Typew,int,int*);private:Typep Bound(int i);void Backtrack(int i);Typew c;/背包容量int n;/物品总数Typew *w;/物品重量数组Typep *p;/物品价值数组int *x;int *ID;Typew cw;/当前装包重量Typep cp

31、;/当前装包价值Typep bestp;/当前最优价值int *bestx;/*templateTypep Knap:Bound(int i)/计算结点所相应价值的上界int j;Typep pp=cp;Typew ww=cw;for(j=i;j=n;j+)ww+=wj;if(ww=c)pp+=pj;else break;ww-=wj;pp+=pj/(c-ww);return pp;*/templateTypep Knap:Bound(int i)/计算结点所相应价值的上界Typew cleft=c-cw;Typep b=cp;/以物品单位重量价值递减序装入物品while(i=n&wi=cle

32、ft)cleft-=wi;b+=pi;i+;/装填剩余容量装满背包if(i=n)b+=pi*cleft/wi;return b;templatevoid Knap:Backtrack(int i)int flag;if(in)/到达叶节点flag=bestpcp;if(flag)bestp=cp;for(i=1;i=n;i+)bestxIDi=xIDi;return;if(cw+wibestp)/进入右子树xIDi=0;Backtrack(i+1);class Objectfriend int Knapsack(int*,int*,int,int,int*);friend void Sort(

33、Object*,int);public:int operator=a.d);private:int ID;float d;/单位重量价值;void Sort(Object *Q,int len) for(int i=0;ilen;i+) for(int j=0;jlen;j+) if(Qi=Qj) Object temp=Qi; Qi=Qj; Qj=temp; /*void Sort(Object* Q,int n)int i,j,temp;for(i=2;i=n;i+)for(j=1;j=i;j+)if(Qi-1.dQj-1.d)temp=Qi-1.ID;Qi-1.ID=Qj-1.ID;Qj

34、-1.ID=temp;*/templateTypep Knapsack(Typep p,Typew w, Typew c,int n,int *x)/返回最大价值,bestx返回最优解 Typew W=0;/装包物品重量Typep P=0;/装包物品价值 /定义依单位重量价值排序的物品数组Object *Q=new Objectn;for(int i=1;i=n;i+) /单位重量价值数组Qi-1.ID=i;Qi-1.d=1.0*pi/wi;P+=pi;W+=wi;if(W=c) return P;/所有物品装包 /依单位重量价值排序Sort(Q,n); /创建类Knap的数据成员KnapK;K.p=new Typepn+1;K.w=new Typewn+1;K.ID=new intn+1;K.x=new intn+1;for(i=1;i=n;i+)K.pi=pQi-1.ID;K.wi=wQi-1.ID;K.IDi=Qi-1.ID;K.xi=0;K.cp=0;K.cw=0;K.c=c;K.n=n;K.bestp=0; /回溯搜索K.bestx=x;K.Backtrack(1);deleteQ;deleteK.w;deleteK

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 办公文档 > 其他范文


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号