《算法分析与设计》大作业报告
0-1背包问题求解方法综述
院 系:
专 业:
班 级:
学 号:
指导教师:
报告日期:
算法分析与设计大作业报告
0-1背包问题求解方法综述
总计:论文设计 26页
图片: 11 个
0-1背包问题求解方法综述
软件工程 宋帅
【摘要】
0-1背包问题是最基本的背包问题,它包含了背包问题中设计状态、方程的最基本思想。0-1背包问题说的是,背包只能容得下一定重量b的物品,物品有m种,每种物品有自己的重量w(i)和价值v(i)(0<i<=m),从这些物品中选择装入背包,使背包不超过重量b,但价值又要最大。本实验主要使用了解决01背包问题中的蛮力算法;贪心算法;动态规划算法;遗传算法;分支限界法;回溯解法六种方法对01背包问题进行求解.分析了 0-1 背包问题的数学模型 , 刻划了最优解的结构特征, 建立了求最优值的递归关系式。本文通过六个算法求解01背包问题的效果及时间复杂度等的对比找寻求解最优解的方法
【关键词】
0-1 背包问题;价值;容量;单位价值;蛮力算法;贪心算法;动态规划算法;遗传算法;分支限界法;回溯解法;最优解。
目录
一.前言1
二.技术说明1
三.原理描述1
四.算法实现4
五.测试与运行22
六.小结26
参考文献26
致谢26
一.前言
0-1背包问题说的是,背包只能容得下一定重量b的物品,物品有m种,每种物品有自己的重量w(i)和价值v(i)(0<i<=m),从这些物品中选择装入背包,使背包不超过重量b,但价值又要最大。很多问题都可以抽象成该问题模型如配载问题、物资调运问题等,因此研究该问题具有较高的实际应用价值。目前,解决0-1 背包问题的方法有很多,主要有动态规划法、回溯法、分支限界法、遗传算法、粒子群算法、人工鱼群算法、蚁群算法、模拟退火算法、蜂群算法、禁忌搜索算法等。其中动态规划、回溯法、分支限界法时间复杂性比较高,计算智能算法可能出现局部收敛,不一定能找出问题的最优解。本文通过六个算法求解01背包问题的效果及时间复杂度等的对比找寻求解最优解的方法
二.技术说明
本实验主要用了解决01背包问题中的蛮力算法;贪心算法;动态规划算法;遗传算法;分支限界法;回溯解法六种方法对01背包问题进行求解.以及通过时间复杂度等的对比寻求最优解的方法
三.原理描述
1,蛮力算法:
给定n个重量为{w1,w2,w3,....,wn}、价值为{v1,v2,v3,...,vn}的物品和一个容量为C的背包,0/1背包问题是求解这些物品中的一个最有价值的子集,并且要能够装到背包中。
使用蛮力法解决0/1背包问题,就是将所有的物品装入背包的可能全部列举出来(背包问题的蛮力解法是穷举这些物品的所有子集,找出能够装到背包中的所有子集,并在这些子集中找出价值最大的子集)。这个可以通过递归的方式实现。递归的过程可以看成是对一棵树的深度优先遍历:
例如上图,假设从背包中的1号物品开始列举所有的可能。如果每一层仅仅简单的在循环中使用递归,则该程序就不会结束。需要使用一个一维向量用于标记当前哪些编号已经装入了。例如,当前1号物品已经装入背包,首先标记1号物品已经装入。之后,在循环中首先选择2号物品,如果没有超出背包的最大承重的话,标记2号物品已经装入。当前还有3、4号物品没有装入,所以继续递归尝试装入3、4号物品。在装入2号物品之后的所有可能的递归全部完成后,修改当前标记,并将2号物品从背包中拿出,之后就可以递归遍历放入3号物品之后的可能的情况了。
蛮力法求解0/1背包问题的时间复杂度为:O(2^n)。
2,贪心算法
对于0-1背包问题,贪心算法之所以不能得到最优解是因为在这种情况下,它无法保证最终能将背包装满,部分闲置的背包空间使每千克背包空间的价值降低了。事实上,在考虑0-1背包问题时,应比较选择该物品和不选择该物品所导致的最终方案,然后再做出最好的选择。由此可导出许多互相重叠的子问题。
用贪心算法求解0-1背包问题的步骤是,首先计算每种物品单位重量的价值vi/wi;然后,将物品的vi/wi的大小进行降序进行排列,依贪心选择策略,将尽可能多的单位重量价值最高的物品装入背包。若将这种物品全部装入背包后,背包内的物品总量未超过c,则选择单位重量价值次高的物品并尽可能多地装入背包。依此策略一直进行下去,直到背包装满为止。
3,动态规划算法:
关于动态规划最经典的问题当属背包问题。动态规划(Dynamic programming)是一种在数学、计算机科学和经济学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。 动态规划常常适用于有重叠子问题和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再合并子问题的解以得出原问题的解。 通常许多 子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,从而减少计算量: 一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个 子问题解之时直接查表。 这种做法在重复子问题的数目关于输入的规模呈指数增长时特别有用。
算法步骤:
1. 最优子结构性质。如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质(即满足最优化原理)。最优子结构性质为动态规划算法解决问题提供了重要线索。
2. 子问题重叠性质。子问题重叠性质是指在用递归算法自顶向下对问题进行求解时,每次产生的子问题并不总是新问题,有些子问题会被重复计算多次。 动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只计算一次,然后将其计算结果保存在一个表格中,当再次需要计算已经计算过的子问题时,只是 在表格中简单地查看一下结果,从而获得较高的效率。
动态规划法求解0/1背包问题的时间复杂度为:O(min{nc,2^n})。
4,回溯算法:
为了避免生成那些不可能产生最佳解的问题状态,要不断地利用限界函数(bounding function)来处死那些实际上不可能产生所需解的活结点,以减少问题的计算量。这种具有限界函数的深度优先生成法称为回溯法。对于有n种可选物品的0/1背包问题,其解空间由长度为n的0-1向量组成,可用子集数表示。在搜索解空间树时,只要其左儿子结点是一个可行结点,搜索就进入左子树。当右子树中有可能包含最优解时就进入右子树搜索。
回溯法求解0/1背包问题的时间复杂度为:O(n*2^n)。
上面两棵解空间树是用回溯法解题时常遇到的两类经典的解空间树。n个物品的0-1背包问题所对应的解空间树就是一棵子集树。这类子集树通常有2^n个叶结点,其结点总个数为2^(n 1)-1.遍历子集树的任何算法均需要Ω(2^n)的计算时间。当所给的问题是确定n个元素满足某种性质的排列时,相应的解空间树成为排列树。排列树通常有n!个叶结点。因此遍历排列树需要Ω(n!)的计算时间。
5,分支限界算法:
首先,要对输入数据进行预处理,将各物品依其单位重量价值从大到小进行排列。在下面描述的优先队列分支限界法中,节点的优先级由已装袋的物品价值加上剩下的最大单位重量价值的物品装满剩余容量的价值和。算法首先检查当前扩展结点的左儿子结点的可行性。如果该左儿子结点是可行结点,则将它加入到子集树和活结点优先队列中。当前扩展结点的右儿子结点一定是可行结点,仅当右儿子结点满足上界约束时才将它加入子集树和活结点优先队列。当扩展到叶节点时为问题的最优值。
分支限界法求解0/1背包问题的时间复杂度为:O(2^n)。
6,遗传算法:
遗传算法(Genetic Algorithm)是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法。
遗传算法主要有以下几个步骤
初始化种群
种群适应度计算
种群选择
种群交配
种群变异
第五步完成又又跳转到第二部迭代计算,直到达到收敛条件
四.算法实现
蛮力算法:
#include <iostream>
#include<cstdio>
using namespace std;
#define N 100
struct goods
{
int wight;//物品重量
int value;//物品价值
};
int n,bestValue,cv,cw,C;//物品数量,价值最大,当前价值,当前重量,背包容量
int X[N],cx[N];//最终存储状态,当前存储状态
struct goods goods[N];
int Force(int i){
if(i>n-1)
{
if(bestValue < cv && cw <= C)
{
for(int k=0;k<n;k )
X[k] = cx[k];//存储最优路径
bestValue = cv;
}
return bestValue;
}
cw = cw goods[i].wight;
cv = cv goods[i].value;
cx[i] = 1;//装入背包
Force(i 1);
cw = cw-goods[i].wight;
cv = cv-goods[i].value;
cx[i] = 0;//不装入背包
Force(i 1);
return bestValue;
}
int main()
{
printf("物品种类n:");
scanf("%d",&n);
printf("背包容量C:");
scanf("%d",&C);
for(int i=0;i<n;i )
{
printf("物品%d的重量w[%d]及其价值v[%d]:",i 1,i 1,i 1);
scanf("%d%d",&goods[i].wight,&goods[i].value);
}
int sum1 = Force(0);
printf("蛮力法求解0/1背包问题:\nX=[");
for(int i=0;i<n;i )
{
cout << X[i]<<" ";
}
printf("] 装入总价值[%d]\n",sum1);
return 0;
}
蛮力法求解0/1背包问题的时间复杂度为:O(2^n)。
贪心算法:
#include <iostream>
#include<time.h>
#include<Windows.h>
using namespace std;
#define max 100 // 自定义物品最大数
void package(int v[],int w[],int n,int c) // 定义包函数
{
double a[max];
int i,totalv=0,totalw=0,index[max];
for(int i=0;i<n;i )
{
a[i]=(double)v[i]/w[i]; // 单位价值计算
index[i]=i;
}
for(int i=1;i<n;i )
{
for(int j=0;j<n-i;j )
{
if(a[j]<a[j 1]) // 进行循环判断
{
double b=a[j];
a[j]=a[j 1];
a[j 1]=b;
int c=v[j];
v[j]=v[j 1];
v[j 1]=c;
int d=w[j];
w[j]=w[j 1];
w[j 1]=d;
int e=index[j];
index[j]=index[j 1];
index[j 1]=e;
}
}
}
cout<<" 单位价值: "; // 输出单位价值
for(i=0;i<n;i )
{
cout<<a[i]<<" ";
}
cout<<endl<<" 物品价值: "; // 输出物品价值
for(i=0;i<n;i )
{
cout<<v[i]<<"\t";
}
cout<<endl<<" 物品重量: "; // 输出物品重量
for(i=0;i<n;i )
{
cout<<w[i]<<"\t";
}
cout<<endl;
double x[max]={0};
i=0;
while(w[i]<=c)
{
x[i]=1;
c=c-w[i];
i ;
}
cout<<" 所选择的商品如下 :"<<endl;
cout<<" 序号 i:\t 重量 w:\t 价格 v:\t"<<endl; // 输出所选择的商品
for(i=0;i<n;i )
{
if(x[i]==1)
{
totalw=totalw w[i];
totalv=totalv v[i];
cout<<index[i] 1<<"\t"<<w[i]<<"\t"<<v[i]<<endl;
}
}
cout<<" 背包的总重量为: "<<totalw<<endl; // 背包所装载总重量
cout<<" 背包的总价值为: "<<totalv<<endl; // 背包的总价值
}
int main(void) // 主函数定义
{
LARGE_INTEGER begin,end,frequency;
QueryPerformanceFrequency(&frequency);
srand(time(0));
int n,i,x[max];
int v[max],w[max],W; // 变量的定义
cout<<" 请输入物品种数 n 和背包容量 W:";
cin>>n>>W;
for(i=0;i<n;i )
x[i]=0; // 物品选择情况表初始化为 0
for(i=0;i<n;i )
{
v[i]=rand()00;
w[i]=rand()00;
}
cout<<" 商品的重量和价值如下: "<<endl;
for(int i=0;i<n;i )
{
cout<<w[i]<<"\t";
cout<<v[i]<<endl;
}
QueryPerformanceCounter(&begin);
package(v,w,n,W); // 函数的调用
QueryPerformanceCounter(&end);
cout<<" 时间:"<<(double)(end.QuadPart- begin.QuadPart) / frequency.QuadPart<<"s"<<endl;
}
动态规划算法:
#include<stdio.h>
#include<math.h>
#include<string>
int knapSack(int w[], int v[], int C)
{
int size = sizeof(w)/sizeof(w[0]);
if (size == 0)
{
return 0;
}
int dp[C 1];
//初始化第一行
//仅考虑容量为C的背包放第0个物品的情况
for (int i = 0; i <= C; i ) {
dp[i] = w[0] <= i ? v[0] : 0;
}
for (int i = 1; i < size; i )
{
for (int j = C; j >= w[i]; j--)
{
if(dp[j]<v[i] dp[j - w[i]])
{
dp[j] = v[i] dp[j - w[i]];
}
}
}
return dp[C];
}
int main()
{
int w[4]= {7, 1, 8, 8};
int v[4] = {9, 2, 7, 9};
int num = knapSack(w, v, 5);
printf("最大价值为:%d\n",num);
}
回溯算法:
#include <iostream>
#include <cstdio>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 100
struct goods{
int wight;//物品重量
int value;//物品价值
};
int n,bestValue,cv,cw,C;//物品数量,价值最大,当前价值,当前重量,背包容量
int X[N],cx[N];//最终存储状态,当前存储状态
struct goods goods[N];
int Bound(int i);
int BackTrack(int i)
{
if(i > n-1)
{
if(bestValue < cv)
{
for(int k = 0; k < n; k )
X[k] = cx[k];//存储最优路径
bestValue = cv;
}
return bestValue;
}
if(cw goods[i].wight <= C)
{ //进入左子树
cw = goods[i].wight;
cv = goods[i].value;
cx[i] = 1; //装入背包
BackTrack(i 1);
cw -= goods[i].wight;
cv -= goods[i].value;
}
int B = Bound(i 1);
if(B > bestValue) //进入右子树
{
cx[i] = 0;
BackTrack(i 1);
}
return bestValue;
}
int Bound(int i)
{
int cleft = C - cw; //剩余容量
int b = cv;
//以物品单位重量价值递减序列装入物品
while(i <= n && goods[i].wight <= cleft)
{
cleft -= goods[i].wight;
b = goods[i].value;
i ;
}
//装满背包
if(i <= n)
{
b = goods[i].value * cleft/goods[i].wight;
}
return b;
}
bool m(struct goods a, struct goods b)
{
return (a.value/a.wight) > (b.value/b.wight);
}
int KnapSack(int n, struct goods a[], int C,int x[N])
{
memset(x, 0, N);
sort(a,a n,m);//将各物品按单位重量价值降序排列
BackTrack(0);
return bestValue;
}
int main()
{
printf("物品种类n:");
scanf("%d",&n);
printf("背包容量C:");
scanf("%d",&C);
for(int i = 0; i < n; i )
{
printf("物品%d的重量w[%d]及其价值v[%d]:",i 1,i 1,i 1);
scanf("%d%d",&goods[i].wight,&goods[i].value);
}
int sum = KnapSack(n,goods,C,X);
printf("回溯法求解0/1背包问题:\nX=[");
for(int i = 0; i < n; i )
cout << X[i] <<" ";//输出所求X[n]矩阵
printf("] 装入总价值[%d]\n",sum);
return 0;
}
回溯法求解0/1背包问题的时间复杂度为:O(n*2^n)。
分支限界算法:
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
#define N 100 //最多可能物体数
struct goods //物品结构体
{
int sign; //物品序号
int w; //物品重量
int p; //物品价值
}a[N];
bool m(goods a,goods b)
{
return (a.p/a.w) > (b.p/b.w);
}
int max(int a,int b)
{
return a<b?b:a;
}
int n,C,bestP=0,cp=0,cw=0;
int X[N],cx[N];
struct KNAPNODE //状态结构体
{
bool s1[N]; //当前放入物体
int k; //搜索深度
int b; //价值上界
int w; //物体重量
int p; //物体价值
};
struct HEAP //堆元素结构体
{
KNAPNODE *p; //结点数据
int b; //所指结点的上界
};
//交换两个堆元素
void swap(HEAP &a, HEAP &b)
{
HEAP temp = a;
a = b;
b = temp;
}
//堆中元素上移
void mov_up(HEAP H[], int i)
{
bool done = false;
if(i != 1)
{
while(!done && i != 1)
{
if(H[i].b > H[i/2].b)
{
swap(H[i], H[i/2]);
}
else
{
done = true;
}
i = i/2;
}
}
}
//堆中元素下移
void mov_down(HEAP H[], int n, int i)
{
bool done = false;
if((2*i)<=n)
{
while(!done && ((i = 2*i) <= n))
{
if(i 1 <= n && H[i 1].b > H[i].b)
{
i ;
}
if(H[i/2].b < H[i].b)
{
swap(H[i/2], H[i]);
}
else
{
done = true;
}
}
}
}
//往堆中插入结点
void insert(HEAP H[], HEAP x, int &n)
{
n ;
H[n] = x;
mov_up(H,n);
}
//删除堆中结点
void del(HEAP H[], int &n, int i)
{
HEAP x, y;
x = H[i]; y = H[n];
n --;
if(i <= n)
{
H[i] = y;
if(y.b >= x.b)
{
mov_up(H,i);
}
else
{
mov_down(H, n, i);
}
}
}
//获得堆顶元素并删除
HEAP del_top(HEAP H[], int&n)
{
HEAP x = H[1];
del(H, n, 1);
return x;
}
//计算分支节点的上界
void bound( KNAPNODE* node,int M, goods a[], int n)
{
int i = node->k;
float w = node->w;
float p = node->p;
if(node->w > M)
{ //物体重量超过背包载重量
node->b = 0; //上界置为0
}
else
{
while((w a[i].w <= M)&&(i < n))
{
w = a[i].w; //计算背包已装入载重
p = a[i ].p; //计算背包已装入价值
}
if(i<n)
{
node->b = p (M - w)*a[i].p/a[i].w;
}
else
{
node -> b = p;
}
}
}
//用分支限界法实现0/1背包问题
int KnapSack(int n,goods a[],int C, int X[])
{
int i, k = 0; //堆中元素个数的计数器初始化为0
int v;
KNAPNODE *xnode, *ynode, *znode;
HEAP x, y, z, *heap;
heap = new HEAP[n*n]; //分配堆的存储空间
for(i = 0; i < n; i )
{
a[i].sign=i; //记录物体的初始编号
}
sort(a,a n,m); //对物体按照价值重量比排序
xnode = new KNAPNODE; //建立父亲结点
for(i = 0; i < n; i )
{ //初始化结点
xnode->s1[i] = false;
}
xnode->k = xnode->w = xnode->p = 0;
while(xnode->k < n)
{
ynode = new KNAPNODE; //建立结点y
*ynode = *xnode; //结点x的数据复制到结点y
ynode->s1[ynode->k] = true; //装入第k个物体
ynode->w = a[ynode->k].w; //背包中物体重量累计
ynode->p = a[ynode->k].p; //背包中物体价值累计
ynode->k ; //搜索深度
bound(ynode, C, a, n); //计算结点y的上界
y.b = ynode->b;
y.p = ynode;
insert(heap, y, k); //结点y按上界的值插入堆中
znode = new KNAPNODE; //建立结点z
*znode = *xnode; //结点x的数据复制到结点z
znode->k ; //搜索深度
bound(znode, C, a, n); //计算节点z的上界
z.b = znode->b;
z.p = znode;
insert(heap, z, k); //结点z按上界的值插入堆中
delete xnode;
x = del_top(heap, k); //获得堆顶元素作为新的父亲结点
xnode = x.p;
}
v = xnode->p;
for(i = 0; i < n; i )
{ //取装入背包中物体在排序前的序号
if(xnode->s1[i])
{
X[a[i].sign] =1 ;
}
else
{
X[a[i].sign] = 0;
}
}
delete xnode;
delete heap;
return v; //返回背包中物体的价值
}
/*测试以上算法的主函数*/
int main()
{
goods b[N];
printf("物品种数n: ");
scanf("%d",&n); //输入物品种数
printf("背包容量C: ");
scanf("%d",&C); //输入背包容量
for (int i=0;i<n;i ) //输入物品i的重量w及其价值v
{
printf("物品%d的重量w[%d]及其价值v[%d]: ",i 1,i 1,i 1);
scanf("%d%d",&a[i].w,&a[i].p);
b[i]=a[i];
}
int sum=KnapSack(n,a,C,X);//调用分支限界法求0/1背包问题
printf("分支限界法求解0/1背包问题:\nX=[ ");
for(int i=0;i<n;i )
cout<<X[i]<<" ";//输出所求X[n]矩阵
printf("] 装入总价值[%d]\n",sum);
return 0;}
遗传算法:
#include <iostream>
//#include <fstream>
#include <string>
#include <stdlib.h>
#include <time.h>
using namespace std;
const int PACK_MAX_W = 80; //背包最大承受重量
const int PACK_MAX_V = 75; //背包最大承受容积
const int NUM = 32; //物品数
const int MAX_GENERATION = 100; //遗传最大代数
const int PS = 500; //种群规模
const float PC = 0.8; //交叉率
const float PV = 0.1; //变异率
const int zl[NUM]={22,15,4,5,10,19,21,20,8,13,2,3,3,17,12,5,12,4,1,21,14,23,17,15,20,22,25,0,22,15,25,13};
const int tj[NUM]={11,22,12,21,21,13,1,10,13,8,6,25,13,27,12,23,12,24,23,11,6,24,28,10,20,13,25,23,5,26,30,15};
const int value[NUM]={8,9,15,6,16,9,1,4,14,9,3,7,12,4,15,5,18,5,15,4,6,2,12,14,11,9,13,13,14,13,19,4};
//随机产生01
int pp(){
float p;
p = rand() % 1000 / 1000.0;
if(p < 0.8)
{//cout<<0<<" ";
return 0;}
else
{//cout<<1<<" ";
return 1;}
}
//个体类
class Entity{
public:
int fit;
int sum_w;
int sum_v;
int sum_val;
int gene[NUM];
int _count;
Entity(){
fit = 0;
sum_v = 0;
sum_w = 0;
sum_val = 0;
int i;
for(i = 0;i < NUM;i )
gene[i] = 0;
}
};
//遗传算法类
class GA{
private:
Entity zq[PS]; //种群
Entity max_single; //最优个体
public:
//读取物品的价值体积重量
//void readData();
//初始化种群
void Init();
//计算个体价值重量重量
int Cal_SingleValue(int row);
int Cal_SingleW(int row);
int Cal_SingleV(int row);
//计算个体适应度
void Cal_Fitness();
//计算价值最大个体
void Cal_Maxval_Single(int _generation);
//选择
void Select();
//是否交叉
bool IsCross() { return ( ( rand() % 1000 / 1000.0 ) <= PC ); }
//交叉
void Cross();
//是否变异
bool IsVariation() { return ( ( rand() % 1000 / 1000.0 ) <= PV ); }
//变异
void Variation();
//进行遗传,每五代几率变异
void Run(){
int i;
//readData();
Init();
for(i = 0; i < MAX_GENERATION; i ){
Cal_Fitness();
Cal_Maxval_Single(i);
Select();
Cross();
if(i % 5 == 0 && i != 0){
Variation();
}
}
Cal_Fitness();
Cal_Maxval_Single(MAX_GENERATION);
cout<<"最优值是:"<<max_single.fit<<endl;
cout<<"最好的基因是:"<<endl;
for(int i = 0;i < NUM;i ){
cout<<max_single.gene[i];
if(i!=NUM-1)
cout<<" ";
}
cout<<endl<<"最好的实体是"<<max_single._count<<"一代."<<endl;
}
};
void GA::Init(){
int i,j,wsum,vsum;
for(i = 0;i < PS;i ){
wsum = 0;
vsum = 0;
for(j = 0;j < NUM;j ){
zq[i].gene[j] = pp();
wsum = zq[i].gene[j]*zl[j];
vsum = zq[i].gene[j]*tj[j];
}
if(wsum > PACK_MAX_W||vsum > PACK_MAX_V) //产生符合条件的个体
i--;
}
}
int GA::Cal_SingleValue(int row){
int j,valuesum = 0;
for(j = 0;j < NUM;j ){
valuesum = zq[row].gene[j]*value[j];
}
zq[row].sum_val = valuesum;
return valuesum;
}
int GA::Cal_SingleW(int row){
int j,wsum = 0;
for(j = 0;j < NUM;j ){
wsum = zq[row].gene[j]*zl[j];
}
zq[row].sum_w = wsum;
return wsum;
}
int GA::Cal_SingleV(int row){
int j,vsum = 0;
for(j = 0;j < 32;j ){
vsum = zq[row].gene[j]*tj[j];
}
zq[row].sum_v = vsum;
return vsum;
}
void GA::Cal_Fitness(){
int i,w,v,val;
for(i = 0; i < PS; i ) {
w = Cal_SingleW(i);
v = Cal_SingleV(i);
val = Cal_SingleValue(i);
if(w > PACK_MAX_W || v > PACK_MAX_V) { zq[i].fit = 0; continue; }
zq[i].fit = val;
}
}
void GA::Cal_Maxval_Single(int _generation){
int i,maxval = zq[0].fit,id = 0;
for(i = 0;i < PS;i )
if(maxval < zq[i].fit){
maxval = zq[i].fit;
id = i;
}
if(maxval > max_single.fit){
max_single = zq[id];
max_single._count = _generation;
}
}
void GA::Select(){
int fit_sum = 0,i,j;
float rand_rate,cur_rate;
float selected_rate[PS];
Entity new_zq[PS];
for(i = 0;i < PS; i ){
fit_sum = zq[i].fit;
}
//使用轮赌法进行选择
selected_rate[0] = float(zq[0].fit) / fit_sum;
for(i = 1; i < PS; i ){
cur_rate = selected_rate[i-1] float(zq[i].fit) / fit_sum;
selected_rate[i] = cur_rate;
}
for(i = 0; i < PS; i ) {
rand_rate = ( rand() % 1000 / 1000.0 );
for (j = 0; j < PS; j ) {
if(rand_rate <= selected_rate[j]) {
new_zq[i] = zq[j];
break;
}
}
}
for(i = 0;i < PS;i ){
zq[i]= new_zq[i];
}
}
void GA::Cross(){
int i,j;
for(i = 0;i < PS - 1;i = 2){
Entity en1 = zq[i];
Entity en2 = zq[i 1];
for(j = 0;j < NUM; j ){
if(IsCross()){
int tmp = en1.gene[j];
en1.gene[j] = en2.gene[j];
en2.gene[j] = tmp;
}
}
zq[i]=en1;
zq[i 1]=en2;
}
}
void GA::Variation(){
int i,j;
for(i = 0;i < PS;i ){
if(IsVariation()){
for (j = 0;j < NUM;j ){
if(IsVariation()){
zq[i].gene[j] = zq[i].gene[j] ? 0 : 1;
}
}
}
}
}
int main(){
srand(time(NULL));
GA temp;
temp.Run();
return 0;
}
五.测试与运行
蛮力算法:
贪心算法:
动态规划算法:
回溯算法:
分支限界算法:
遗传算法:
六.小结
01背包问题是最基本的背包问题,它包含了背包问题中设计状态、方程的最基本思想。此次通过对0-1背包问题的算法研究可以看出,回溯法和分枝限界法以及动态规划法可以得到问题的最优解,可是计算时间太慢;分支限界法求解0/1背包问题的时间复杂度为:O(2^n);回溯法求解0/1背包问题的时间复杂度为:O(n*2^n);动态规划法求解0/1背包问题的时间复杂度为:O(min{nc,2^n})。
虽然背包问题具有最优子结构性质,但是背包问题用贪心算法求出来的是最优解,0-1背包问题通过贪心算法得不到最优解,因为无法保证最后能将背包装满,部分闲置的背包空间使总价值降低了。
实验中遇到不懂得就上网查阅资料参考一些书籍等最终完成了本次实验。总的来说此次实验的完成收获是巨大的,逻辑思维也有了一定的提升。
参考文献
[1] 王晓东 . 计算计算法设计与分析 [M] . 北京:电子工业出版社, [2] 曹亚非 . 背包问题中贪心算法的应用探讨
[3] 陈玉福 . 《算法设计与分析》北京清华大学出版社
[4] 王银年 . 遗传算法的研究与应用[J].2009-08-01
致谢
非常感谢胡老师这学期的悉心教导!通过老师这学期的悉心教导认真负责的教学系统的学习了有关算法的一些知识,收获是巨大的,这学期对于算法方面的学习使我受益匪浅,对我的逻辑思维能力也有了颇大的提升,相信所学到的知识在以后的工作中也有巨大的用处,最后再次感谢胡老师的悉心教导!