1.建立模型
1.1 问题描述
有一个原材料供应商,从工厂出发,用一辆货车为若干个下游工厂配送原材料且只配送一次,最后仍回到工厂,问应如何选择行车路线,使总耗油量最少。 模型的基本假设:
a.货车的载重量无限大。
b.货车行驶路面的粗糙程度相同并且无上下坡。
c.货车的耗油量只与货车的载重量和行驶路程决定,其他因数忽略不计。
耗油量的多少可以用货车在整个过程中做功的多少来衡量,
(PS:将这个问题完整描述将有助于整个算法的理解)
1.3模型目标
在本模型中,在每一行或每一列中各取一个值得到货车的一条行车路径,共有n!种货车行驶路线(n为下游工厂数量),目标是通过有限次计算,寻找一条行车路径,使总的耗油量相对最低。1.4模型分析
本问题的组合情况数量为n!,其中还未考虑其中每一种情况本身的计算量。
当n=10,n!=3628800;
当n=20,n!=2.432902e+18;
当n=50,n!=3.041409e+64;
当n=100,n!=9.332622e+157;
......
当n=100时,一般个人计算机的频率约为2GHz,这样需要持续计算n!/(2*e+9)=4.666311e+148秒~1.479677e+141年,列举所有情况将产生组合爆炸,要穷举所有的情况基本是不可能的,这也是一类典型的np-hard问题。当n很大时,按照常规的方法根本没法解决此类问题,一般用启发式算法来解决此类问题,常用的启发式算法有:遗传算法、模拟退火算法、禁忌搜索算法、蚁群算法等,但启发式算法也只能获得相对最优解,我将用蚁群算法(AntColony Optimization)来解决本问题。
2.蚁群算法(AntColony Optimization)
2.1 基本思想
蚁群算法是受到人们对自然界中真实蚁群的集体行为的研究成果的启发而提出的一种基于种群的模拟进化算法,属于随机搜索算法,由意大利学者M.Dorigo等人首先提出。蚂蚁在行动过程中会在路径上留下信息素,这些物质信息能被同一蚁群中后来的蚂蚁感知到并作为一种信号影响后到的蚂蚁的路径的选择,后到的蚂蚁选择这些路径的可能性比没有这些物质的路径的可能性大的多,并且路径上的信息素浓度愈大,被后面的蚂蚁选择的可能性就愈大。
蚁群算法信息素的更新方式有局部更新和全局更新,在本问题中,局部更新在构建一条合法路径的过程中进行信息素的更新,当蚂蚁走过一条边之后,就对该边进行信息素的更新;全局更新是在所有蚂蚁都构建了一条合法路径之后才对各边进行信息素更新。蚁群算法在解决具体问题时总共有三种模型:蚁量模型、蚁密模型和蚁周模型,蚁量模型和蚁密模型的信息素更新方式为局部更新,蚁量模型中,蚂蚁在自己所走过的边上释放的信息素是边的长度的倒数,蚁密模型中,蚂蚁在自己所走过的边上释放的信息素是一个常量,蚁周模型的信息素更新方式是全局更新,但是进行全局更新时要记录蚂蚁所走过的所有路径,这在下游工厂数量较多的本问题中是很难实现的,本问题所用的蚁群算法采用蚁量模型。
2.2 算法流程
本问题的蚁群算算法的流程图为:
2.3 C语言实现
我用C语言通过蚁群算法解决本问题,c语言实现的代码如下,共有两个文件,ACO.h:记录一些全局常量和函数声明,ACO.c为主要实现文件。ACO.h和 ACO.c的代码:
/*
============================================================================
Name : ACO.c
Author : sheng
Description : 此模型采取信息素局部更新方式,为蚁量模型:蚂蚁在自己所走过的边上释
放的信息素是蚂蚁走过边的长度的倒数
============================================================================
*/
#include
#include
#include
#include
#include "ACO.h"
int main()
{
int i,j,interationCount=0; /*定义迭代次数指标并初始化为0*/
int robustness=0; /*用于判断是否要减小信息素传递参数rou和确定性性搜索或探索性搜索q0的值*/
int stopFlag=0; /*算法停止标志*/
int pheromoneRoute[FACTORY_COUNT+2]={0}; /*定义信息素浓度最大的路径并初始化为0*/
double pheromoneBasedTotalWork; /*按信息素选择的路径的总功*/
double pheromoneDeliverConst=ROU_MAX; /*信息素传递参数,初始化为最大值*/
double searchChooseConst=SEARCH_CHOOSE_MAX; /*探索性方式选择参数,初始化为最大值*/
double distance[FACTORY_COUNT+1][FACTORY_COUNT+1];
double pheromone[FACTORY_COUNT+1][FACTORY_COUNT+1];
struct factory factoryInfo[FACTORY_COUNT+1]; /*定义每个工厂(包括原材料配送中心)的信息*/
struct ant antInfo[ANT_COUNT]; /*定义蚂蚁信息的结构体,包括蚂蚁以走的路径信息*/
Init_Factory_Information(factoryInfo); /*初始化各个下游工厂以及供应商的位置及需求信息*/
Init_Distance_Information(factoryInfo,distance); /*初始化各个下游工厂以及供应商的距离矩阵*/
Init_Pheromone_Information(pheromone); /*初始化各个下游工厂以及供应商之间的信息素浓度,无单位,为具体值*/
Ant_Information(antInfo); /*初始化蚂蚁信息*/
while ((stopFlag
<=INTERATION_COUNT)){ interationCount++; /*路径选择确定性搜索与探索性搜索*/ Choose_Route(antInfo,distance,pheromone,factoryInfo,&searchChooseConst); /*更新每一边路径上的信息素*/ Update_Pheromone(factoryInfo,antInfo,pheromone,distance,&pheromoneDeliverConst); /*求出基于信息素的相对最优路径信息*/ Based_Pheromone_Route(pheromone,pheromoneRoute,&robustness); /*更新信息素传递参数和搜索选择的值的函数*/ Update_Row_Q(&pheromoneDeliverConst,&searchChooseConst,&robustness,&stopFlag); } pheromoneBasedTotalWork=Calculate_Total_Work(factoryInfo,pheromoneRoute); /*根据相对最优路径信息计算总功*/ printf("1.Output the total work of Ant[0]-Ant[9]:\n"); for (i=0;i<10;i++) printf("Total Work[%d]:%f\n",i,antInfo[i].totalWork); printf("2.The Relative Optimal Value:\n"); printf("totalWork:%f\n",pheromoneBasedTotalWork); /*输出最小的总功*/ printf("the route is: "); /*输出路径*/ for (j=0;j
"); else printf("\n"); } return EXIT_SUCCESS; } /* ============================================================================ Function Name:Update_Row_Q Function :更新信息素传递参数和搜索选择的值 ============================================================================ */ void Update_Row_Q(double *pheromoneDeliverConst,double *searchChooseConst,int *robustness,int *stopFlag) { if (*robustness==ROBUSTNESS){ *robustness=0; /*清零*/ (*pheromoneDeliverConst)*=ROU_CHANGE_RANGE; /*信息素传递参数乘以ROU_CHANGE_RANGE ,减小*/ if (*pheromoneDeliverConst<=ROU_MIN) *pheromoneDeliverConst=ROU_MIN; /*ROU不能无限小,确保收敛*/ (*searchChooseConst)*=ROU_CHANGE_RANGE; /*探索方式参数乘以ROU_CHANGE_RANGE,减小*/ if (*searchChooseConst<=SEARCH_CHOOSE_MIN) *searchChooseConst=SEARCH_CHOOSE_MIN; /*探索方式选择参数q0不能无限小,确保收敛*/ } if (*pheromoneDeliverConst==ROU_MIN) (*stopFlag)++; return ; } /* ============================================================================ Function Name:Based_Pheromone_Route Function :找出信息素浓度最大的路径,即蚂蚁最终都会走的路径 ============================================================================ */ void Based_Pheromone_Route(double (*pheromone)[FACTORY_COUNT+1],int *pheromoneRoute,int *robustness) { int i,j,k; int choosePoint,flag,choosePointFalg,robustnessFlag; /*选择点、退出标志、选择标志、选择点标志、鲁棒性检查标志*/ int pheromoneRoutefront[FACTORY_COUNT+2]; /*用于装上一次得到的信息素的路径信息*/ int tabuNumber; /*禁忌的数量*/ tabuNumber=1; robustnessFlag=0; for (i=0;i
antInfo[j].totalWork){ /* 按总功大小从小到大排序*/ totalWorkChange=antInfo[i].totalWork; /* 交换总功*/ antInfo[i].totalWork=antInfo[j].totalWork; antInfo[j].totalWork=totalWorkChange; for (k=0;k
数据来源说明:本例子中的FACTORY_COUNT个工厂的位置、以及各个工厂的需求数据(共(FACTORY_COUNT*3)个数)均是通过OpenOffice Calc的随机数生成的。
FACTORY的X与Y坐标生成算式均为:IF(RAND()>0.5;ROUND((RAND()*(1000-100)+100);2);-ROUND((RAND()*(1000-100)+100);2))。X与Y坐标代有正负,生成的正负值的概率基本相等,式子中的三个RAND()函数在每一次都是独立取值的,说明了本数据服从(-1000,100)U(100,1000)之间的均匀分布,配送中心的坐标为(0,0)。
FACTORY的需求生成算式:ROUND((RAND()*(100-10)+10);2)。各个下游工厂的需求量服从(10,100)之间的均匀分布,保留两个有效数字,配送中心的需求为0。本例中数据没能直观的体现数据本身的意义,只是将其用到本问题算法的验证中。代码参数说明:本例中存在许多全局参数设置,如ALPHA,BETA,CONSTRICTION_CONST,INTERATION_COUNT,SEARCH_CHOOSE_MAX,SEARCH_CHOOSE_MIN,ROU_MAX,ROU_MIN,ROU_CHANGE_RANGE,SEARCH_CHOOSE_RANGE,INIT_PHEROMONE等,比较重要的参数是ALPHA和BETA,SEARCH_CHOOSE_RANGE,ALPHA表示信息素在概率计算中的权重,值越大,信息素在蚂蚁选择下一个所要到达的点的作用越大,BETA表示启发因子在概率计算中的权重,值越大,启发因子在蚂蚁选择下一个所要到达的点的作用越大,即蚂蚁有更大的可能会去走新的路径;SEARCH_CHOOSE_RANGE表示信息素的衰减程度,即每进行一次迭代,就会进行一次信息素的衰减,然后逐次递减,信息素对于蚂蚁选择的影响作用也越小。
C语言实现的输出结果:
由于使用启发式算法解决np-hard问题,几乎不可能得到最优解,在本例中,随着参数(全局常量)设置的不同,结果不相同,即使在参数设置相同的情况下,运行的结果也很难相同,因为在信息素的作用减弱之后,蚂蚁可以随机选择路线,而这种随机性也就导致不同的结果。从结果可看出,第一组结果表示的是从最后一次迭代中选择标号为0~9的蚂蚁,输出他们在最后一次行走的路线的总功(总功与耗油量成正比,总功越小,耗油量越少,结果越优),从中可以看出,不同蚂蚁走的路线不尽相同;第二组结果表示通过蚁群算法得出的相对最优解,明显比上面随机选择一组的结果要好,后面的一组线路图表示相对最优解所对应的行车路线,该线路表示从配送中心(0,0)出发,经过FACTORY_COUNT个(本例中是100个)下游工厂后回到配送中心(0,0)。
总结:本例通过蚁群算法解决原材料配送这个np-hard问题,通过C语言实现了基本的算法并求出了一组相对最优解,但是本文对算法中参数选择、数据选择以及最终结果的解释没能做有效地分析,这需要后续不断地修正和改进。
PS:本算例是我在大二时候实现的(遗传算法实现会在后续整理出来),始终感觉这个简单的模型对于算法的学习非常有用,这几天就把这个模型以及其蚁群算法实现整理出来,希望这个简单的模型能够能够引导读者对算法的思考,加深对蚁群算法的理解。
参考:
[1] 童若锋,张维泽,许星,董金祥. 基于改进蚁群算法的物流配送路径优化.浙江大学人工智能研究所.
[2] 张居阳,孙吉贵. 组合优化调度问题求解方法. 计算机科学,2003,30(2).
[3] 詹士昌 徐婕 吴俊. 蚁群算法中有关算法参数的最优选择. 科技通报,第19卷第5期, 2003 年9 月