目录
一、算法概述
二、实例详解
1)问题分析
2)初始化参数
2)设置蚂蚁初始位置
3)选择路径
4)记录本次最佳路径
5)更新信息素
6)清空禁忌表
三、计算结果
四、总结
一、算法概述
一群蚂蚁在空旷的地面总能找到从洞口到远处食物相对较近的路径,不是洞口到食物有一条设计好的道路,而是因为无数的蚂蚁始终无休止的在旷野中随机的寻找食物。当一只蚂蚁找到食物后,就会引来越来越多的蚂蚁,最后所有的蚂蚁都沿着同样的路径搬运食物。正如鲁迅所说,世上本没有路,走的人多了就有了路。蚁群算法就是通过模拟蚂蚁寻找食物的过程来求解问题的最优解的,它仍是一种概率算法。其核心仍然是让解朝概率最大的方向变化,而概率的计算则依赖于信息素和启发式因子。
信息素和启发式因子两个概念具体指什么呢?它们其实就是仿真蚂蚁在进行道路选择时受影响的两个因素。那么蚂蚁在选择道路时会受什么因素影响呢?一个就是其他蚂蚁在路径上留下的气味浓度(姑且也就称为信息素吧),另一个就是蚂蚁自己的想法(假设蚂蚁也会思考)。当然,影响蚂蚁选择道路的因素肯定还有很多的,但是我们认为这两个是主要因素,我们的算法也就模拟这两个因素,不然算法会太复杂。我们就用信息素和启发式因子两个参数来模拟这两个因素,当然我们的模拟需要更彻底一点,蚂蚁留下的气味浓度会慢慢消散,因此我们还引入蒸发系数来表征这个过程。每条路径上的信息素和启发式因子共同决定了蚂蚁下一步选择哪条路径的概率,路径概率越大,被选择的可能性越高。
以上只是介绍了信息素和启发式因子,并没有讲解如何去实现蚁群算法,因为我们首先要了解蚁群算法思想的核心就是信息素和启发式因子这两个概念(参数)。也就是说,其核心是为解的下一步演进找到最大概率变化方向。说到这里,我们就要和经典遗传算法做个算法思想的比较了。遗传算法的核心在于适应度函数,它决定了哪些优秀个体才能被选择到遗传至下一代,是影响解演进方向的核心要素,而后续的交叉、变异等都是盲目的无方向性的随机操作,有各种各样的方法去具体实现。而蚁群算法解的演进就只靠路径概率,它直接为解的演进提供明确的方向。由此我们可以知道,蚁群算法和遗传算法一样,刚开始时都需要随机初始化一个解群,随着计算推进,最后这些解都会趋同,也就是收敛。当然,蚁群算法也同样存在被限制在局部最优解的情况。而影响这种情况发生的因素正是启发式因子大小,以及它在计算概率时所占比重的大小。启发式因子的作用类似于遗传算法中的交叉和变异,它为解的演进增加了更大的不可预测性。
二、实例详解
前面对蚁群算法的思想进行了阐述,而并没有说明具体的实现方法。其实,任何算法的核心在于其思想,只要掌握了算法的核心思想,我们才能在针对具体问题时,根据算法思想分析设计出具体的实现方法,并在解算过程中知道如何去优化方法。一个针对具体问题的计算方法往往不能套用到其它问题求解上,因此方法是表,思想是内核。但是我们也不能忽视具体实现方法,正所谓万变不离其宗,透过表面的方法我们也能更好理解其内核。下面我们就以一个具体的算例,来详细说明蚁群算法的实现过程。
1)问题分析
此处仍以旅行商问题为例进行算法实现过程说明,通过蚁群算法计算遍历所有城市的最短路径。选用旅行商问题,主要是因为这是一个路径规划问题,其问题形式与蚂蚁觅食的过程非常类似,更便于直观理解算法实现过程。在遗传算法中,我们通过随机生成一组路径,作为初始解。而在蚁群算法中,我们需要随机初始化蚁群蚂蚁(解的初始位置)分布在给定的城市上,然后让每一只蚂蚁遍历完所有城市,如此我们便得到了一组解。当然,此组解中一般不会有最优解,因此,我们需要再派出蚁群蚂蚁重复上述步骤,不过这时城市路径上的信息素已经发生了变化,蚂蚁的选择也将发生变化。如此往复,直到我们得到最终需要的最优解为止。
2)初始化参数
在蚁群算法中,我们需要初始化的参数有:蚁群数量、信息素重要程度、启发因子重要程度、信息素蒸发系数、总信息量。具体代码如下。此处城市的数量为31,设定蚂蚁数量为50只,最大信息素量Q为100。函数pdist2用于计算两两城市之间的距离,得到距离矩阵D。启发因子直接取两两城市之间距离的倒数,得到矩阵eta,因此距离越近,启发因子越大。初始化信息素矩阵tau为一个元素全为1的矩阵,矩阵大小与启发因子矩阵一样,它表示给两两城市之间的路径上初始化信息素为1。其他初始化变量不再赘述。
m = 50;%蚂蚁数量
a = 1;%信息素重要程度
b = 5;%启发因子重要程度
rho = 0.1;%信息素蒸发系数
G = 200;%最大迭代次数
Q = 100;%信息素增加强度系数
n = size(city_coordinates,1); %城市数量
D = pdist2(city_coordinates, city_coordinates);
eta = 1./D; %启发因子,取距离的倒数
tau = ones(n,n);%信息素矩阵
tabu = zeros(m,n);%存储路径 tabu的行序号就代表了蚂蚁编号
R_best = zeros(G,n);%存储每次迭代的最佳路线
L_best = inf.*ones(G,1);%最佳路线长度
2)设置蚂蚁初始位置
在完成上述初始化之后,就可以开始蚂蚁选择路径的操作了。当然首先是要设置蚂蚁的初始位置。在初始化中我们设置迭代200次,也表示我们需要派出200群蚂蚁,每派出一群蚂蚁都需要给出蚂蚁的初始位置。初始位置设置代码如下。矩阵tabu每行存放的就是每只蚂蚁走过的路径,它的行号可以代表蚂蚁编号。设置蚂蚁的初始位置,就是为矩阵tabu的第一列设置城市编号。因为蚂蚁数量为50,大于城市数量31,通过以下代码,会得到一个城市可能会有两只蚂蚁。
c = ceil(m/n);
Randpos = zeros(1,c*n);
for i = 1:cRandpos(1,1+(i-1)*n:i*n) = randperm(n);
end
tabu(:,1) = (Randpos(1,1:m))';%50只蚂蚁放到31个城市,一个城市可能会有两只蚂蚁 Randpos中存放的城市编号
3)选择路径
在蚁群算法中,每一次路径选择是指每一只蚂蚁完成一次全部城市遍历,得到一个完整路径,也就是得到一个解。因此,针对旅行商问题,我们要进行两重循环,完成所有城市和蚂蚁的遍历。这里需要进一步说明的是,在旅行商问题中,每一只蚂蚁不是问题的解,问题的解是蚂蚁走过的路径。选择路径的具体代码如下。visited用于存放蚂蚁已经走过的城市,避免重复访问。tabu前文已说明,存放的就是蚂蚁走过的路径。选择路径的算法核心是计算下一步每种可能的概率,然后根据概率选择出下一步。P中存放的是未访问城市被访问的概率,它的计算公式如下:
Tau表示当前城市到下一个城市路径上的信息素,Eta表示启发因子,a和b分别表示两个参量的重要程度。由前文我们知道,Tau信息素是会变化的,Eta是不变的,它由两个城市之间的距离决定,而距离是固定的。程序后面对矩阵P做了归一化。正式选择下一个城市时,采用了轮盘赌的方式,将选中的城市作为下一个访问城市。
for j = 2:n%次循环是所有蚂蚁访问所有城市,从2开始,因为蚂蚁初始位置占用一个城市for i = 1:m%对每只蚂蚁逐一计算visited = tabu(i,1:(j-1));%存放已访问过的城市J = zeros(1,n-j+1);%存放待访问的城市P = J;Jc = 1;for k = 1:n%此循环是找出该蚂蚁未访问过的城市if isempty(find(visited == k, 1))J(Jc) = k;Jc = Jc + 1;endend%%%%%%%%%%计算待选城市概率%%%%%%%%%%%%%%%for k = 1:length(J)%遍历待选城市P(k) = (tau(visited(end),J(k))^a).*(eta(visited(end),J(k))^b);endP = P/(sum(P));%%%%%%%%%%%%%根据概率选择下一个城市%%%%%%%%Pcum = cumsum(P);select = find(Pcum >= rand);to_visit = J(select(1));tabu(i,j) = to_visit;end
end
4)记录本次最佳路径
在完成上述第3步计算后,所有蚂蚁均对所有城市遍历了一遍,我们已经得到了每只蚂蚁的行走路径。因此,我们可以从这些路径中选出距离最短的路径。代码实现如下。
L = zeros(m,1);
for i = 1:mR = tabu(i,:);for j = 1:(n-1)L(i) = L(i) + D(R(j),R(j+1));endL(i) = L(i) + D(R(1),R(n));
end
L_best(NC) = min(L);
pos = find(L == L_best(NC));
R_best(NC,:) = tabu(pos(1),:);
5)更新信息素
tau为信息素矩阵,它存放的是两两城市之间路径上的信息素值。信息素矩阵更新的方法是,假设每只蚂蚁出发时,身上均携带总量相同的信息素Q,并在到大终点后将信息素Q全部均匀的释放到其所走过的路径上。代码中delta_tau矩阵就表示在一群蚂蚁完成出动后,所有蚂蚁释放到路径上的信息素。tau = (1-rho).*tau+delta_tau用于计算本轮出动后释放的信息素与之前残留信息素的总和。其中rho表示信息素增发速率,即表示在每轮迭代中,路径上的信息素会按设定比率蒸发掉,模拟真实场景中蚂蚁信息素蒸发过程。
delta_tau = zeros(n,n);
for i = 1:m%蚂蚁数量for j = 1:(n-1)%城市数量delta_tau(tabu(i,j),tabu(i,j+1)) = delta_tau(tabu(i,j),tabu(i,j+1))+Q/L(i);%Q是信息素强度系数,按距离值分配后加到两两城市之间enddelta_tau(tabu(i,n),tabu(i,1)) = delta_tau(tabu(i,n),tabu(i,1))+Q/L(i);
end
tau = (1-rho).*tau+delta_tau;%rho信息素蒸发系数,更新信息素矩阵
6)清空禁忌表
所谓清空禁忌表,就是清空矩阵tabu。tabu存放的是每轮蚂蚁出动完成后,每只蚂蚁走过的城市路径。完成一次出动后,矩阵tabu中的最优路径信息已经在前面提出,对该矩阵的使用已结束,因此矩阵中的信息可以清空,再下一轮计算中重新赋值。
至此,蚁群算法的全部步骤就已结束。
三、计算结果
程序实现的源代码可在此处下载,程序运行结果如下。左图是蚁群算法得到的最优解路径,右图是每步迭代的最优解结果,可以看到蚁群算法的收敛速度比较快。
四、总结
针对旅行商这类问题,相比遗传算法,蚁群算法的求解结果更趋于稳定,每次计算得到的结果基本一致。在蚁群算法中,启发因子对解优化方向具有很强的约束性,遗传算法中对解的约束主要体现在选择上,但是交叉变异的随机性较强。相比较而言,蚁群算法可能更容易陷入局部最优解中,这也是为什么多次使用蚁群算法计算,得到的最优解都不如遗传算法的求解结果。