【无标题】

首先简单介绍一下这个问题。01背包问题就是将若干个体积和价值各不相同物体,放入一个容量有限的背包,使得背包里面的物品总价值最大。
01背包问题其他背包问题的特殊之处在于,每个物品只有选或者不选,而且只能选一次。这就让我们不由得想到旅行商问题,其限制条件是每个城市只能经过一次,与01背包问题的限制条件类似。而蚁群算法是解决旅行商问题的常用算法。所以我们组尝试用蚁群算法解决01背包问题。
那么什么是蚁群算法呢,简单地说,就是让蚁群去探路,如果一条路较短,单位时间内往返的蚂蚁就更多,留下的信息素就会更多。并且每只蚂蚁倾向于选择信息素浓度更高的路径,这样,会形成正反馈。最终蚁群找到了最佳的路线。
对于01背包问题这样一个特定的问题,如何将蚁群算法应用呢?我们将物品组成为图。我们设置和物品数同样多的蚂蚁,它们每爬过一个物品,就将其投入背包,直到在装入某个物品后,背包剩余容量无法装入蚂蚁随机选择的下一个物品,作为终点。将该轮所以蚂蚁选择的物品组合整合,取总价值最高者。增加该路径的信息素浓度,挥发其他路径的信息素浓度。
也就是说,我们将总价值高作为蚁群的启发因子,引导蚂蚁选择总价值较高的组合。在蚂蚁走过足够多轮后,总价值最高的一个组合不再变化,答案也就水落石出。
下面我们进入代码实现的部分。在完成了初始化后,代码进入外循环。外循环管理的是,当蚂蚁走过足够轮,或者计算出的总价值最高的组合已经连续100轮不改变之后,结束计算,输出答案。
内循环是代码的核心部分。当出现两种情况,一是所有物品都已经选择过了,二是存在未被选择的物品但是一旦选取,体积将溢出背包容量,显然该循环中蚂蚁的工作结束,进入下一次循环。
如果符合内循环进行的条件,首先我们将符合条件的物品整理在一个数组内。然后根据选择每个物品的路径的信息素含量,用公式赋予该路径一个期望启发式因子,也就是rate,然后结合我们设置的辅助值r,u,生成选择该路径的概率。这样就做到了在期望启发式因子的引导下,蚂蚁随机选取路径。不断地重复这个过程,直到内循环结束。
在一轮内循环结束后,我们将选出该轮的最优解,将该路径的信息素加强,将其余路径的信息素挥发。之后再进入下一轮内循环。在信息素的引导下,每次内循环的最优解将在大量的内循环后收敛,直到多次不发生改变,或者循环次数足够后,外循环结束,完成计算。保存最优解,输出答案。
蚁群算法的优点是设置参数较少,流程固定。相比于动态规划,蚁群算法虽然运行效率略低于动态规划,但是蚁群算法拥有基本模型,好理解,对于学习者来说是重要优点。

#include "iostream"
#include <fstream>
#include <string>
#include <time.h>
 
using namespace std;
 
#define M 8//蚂蚁数
#define P 0.2//信息素挥发率
#define MAX 70
int BRoute[MAX];//最优解
int BValue=0;//最优解的总价值
int BWeight;//最优解的总重量
int max_circle=500;//外循环最大次数
int antRoute[9][MAX];
int antValue[9];
void main(){
	int n;//物品个数
	int w_limit;//背包重量限制
	int value[MAX];//各物品价值
	int weight[MAX];//各物品重量
    float inf[MAX][MAX];//信息素矩阵
	int i,j;
 
 
 
	// ************** 读数据 *************************//
	string filestring;
	cout<<"请输入测试文件名: ";
	cin>>filestring;
	
	ifstream inFile(filestring.c_str());
	//报告错误
	if(!inFile)
	{
		cerr<<"不能打开测试文件:"<<filestring<<endl;
		exit(-1);
	}
    
	inFile>>n;
	inFile>>w_limit;
 
	value[0]=0;
    weight[0]=0;
	for (i=1; i<=n; i++)
	{
		inFile >> weight[i];  
	}	
	for (i=1; i<=n; i++)
	{
		inFile >> value[i]; 
 
	}
 
	//************************* 信息素矩阵初始化 ********************//
	
	for(i=0;i<=n;i++)
		inf[i][0]=0;
	for(i=0;i<=n;i++){
		for(j=1;j<=n;j++){
			inf[i][j]=1;
		}
	}
	
 
	//************************ 数据输出 ****************************//
	cout<<"物品总数为:"<<n<<endl;
	cout<<"背包重量限制为:"<<w_limit<<endl;
 
	cout<<"各物品重量分别为:"<<endl;
   	for(i=0;i<=n;i++)
		cout<< weight[i]<<" ";
	cout<<endl<<endl;
 
	cout<<"各物品价值分别为:"<<endl;
    for(i=0;i<=n;i++)
		cout<< value[i]<<" ";
	cout<<endl<<endl;
 
	srand((int)time(0)); //初始化随机种子
	bool mark[MAX];
	int no_modify=0;
	for(int k=1;k<=max_circle && no_modify<100 ;k++){//外循环
//	for(int k=1;k<=max_circle  ;k++){//外循环
		for(i=1;i<=M;i++){
			antRoute[i][0]=0;
			int Cur_n=0; //已选取物品个数
			int Cur_w=0; //已选取物品总重量
			int Cur_v=0; //已选取物品总价值
			int Cur_ps=0; //记录当前选取物品的标号
		    for(j=1;j<=n;j++)
				mark[j]=false; //作为物品是否选取的标志
			bool finish=false; 
		   	int ok[MAX]; 
			while(finish==false){
				if(Cur_n==n){
					antRoute[i][++Cur_n]=0;
					antValue[i]=Cur_v;
					finish=true;
					
				}
				else{
					int ok_n=0;
					for(j=1;j<=n;j++){
						if(mark[j]==false &&(Cur_w+weight[j])<=w_limit){
							ok[ok_n++]=j; //该数组用于存储满足条件的物品的标号
						}
					}
					if(ok_n==0){ //无满足条件的物品
						antRoute[i][++Cur_n]=0;
						antValue[i]=Cur_v;
						finish=true;
					}
					else{
						//有满足条件的物品:按信息素来进行随机选取
						float total=0;
						float rate[MAX];						
					
						for(j=0;j<ok_n;j++){
							float total=total+inf[Cur_ps][ok[j]];							
						}
						for(j=0;j<ok_n;j++)
							rate[j]=(inf[Cur_ps][ok[j]]/total);
							
						bool choose=false;
						while(choose==false){
							double r=(double)(rand() % 1001) * 0.001f;
							int u=(int)(rand()%ok_n);
							if(rate[u]>r){
									antRoute[i][++Cur_n]=ok[u];
									Cur_ps=ok[u];
									Cur_w+=weight[ok[u]];
									Cur_v+=value[ok[u]];
									mark[ok[u]]=true;
									choose=true;
									break;		
							}
 
							/*
                            for(j=0;j<ok_n;j++)
								if(leiji[j]>r){
									antRoute[i][++Cur_n]=ok[j];
									Cur_ps=ok[j];
									Cur_w+=weight[ok[j]];
									Cur_v+=value[ok[j]];
									mark[ok[j]]=true;
						//			cout<<endl<<"选中"<<ok[j]<<endl;
									choose=true;
									break;		
							}*/
 
						}
				
					}//else
 
				}//else
			}//while
 
		}//for:内循环
        
		//与当前最优解比较
		int temp=0;
		for(i=1;i<=M;i++){
			if(antValue[i]>BValue){
				BValue=antValue[i];
				temp=i;
			}
		}
		if(temp==0)
			no_modify++; //记录连续不改变的次数,达到一定值时停止外循环
		if(temp!=0){
			no_modify=0;
			for(int s=0;s<MAX;s++){
				BRoute[s]=antRoute[temp][s];
			}
		}
		
	//信息素更新/
	//挥发
		for(i=0;i<=n;i++)
			for(j=0;j<=n;j++){
				inf[i][j]*=(1-P);
			}
	//最优解再进行信息素的增强
        inf[BRoute[0]][BRoute[1]]+=P/n;
		for(int s=1; s<MAX && BRoute[s]!=0;s++){
			inf[BRoute[s]][BRoute[s+1]]+=P/n;			
		}
 
	}//外循环
	cout<<"外循环次数为:"<<k<<endl;
	cout<<"最好的解是:";
	for( k=1;k<MAX && BRoute[k]!=0;k++)
		cout<<BRoute[k]<<" " ;
	cout<<endl<<"其总价值为:"<<BValue<<endl;
	BWeight=0;
	for(int s=1;s<MAX && BRoute[s]!=0;s++)
		BWeight=BWeight+weight[BRoute[s]];
 
	cout<<"其总重量为:"<<BWeight<<endl;
}
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值