蚁群优化算法,最直观的一个示例用途是解决TPS(TravelingSealsmanProblem)问题,快速寻找一个可观的解,但需要你调的一手好参

Ant Colony Optimization

蚁群,退火,遗传,三大知名优化算法

蚁群优化算法,启发在于蚂蚁觅食的行为,即一段时间后蚂蚁能够找到最短的路径到达食物的位置;核心在于对信息素的抽象,蚁群算法之所以能够快速优化,得益于算法已有的成果积累;一句话说明白这个算法,那就是在当前解的基础上去寻找更优解

本文记录一下写这个算法的unity可视化实现过程,但是我还是建议您自己写一遍👼,体验一下令人非常不愉快的调参体验💢;如果你决定自己写一遍,可以在看了Getting Start理解算法原理后直接开干,卡住了再来参考一下后面的具体实现方法

之后应该会先去学一些shader相关的内容,以免在很多之后想做的工程中受限

Getting Start

TPS

旅行商人问题,一句话概括就是

找到一条长度最短的线串联起一些固定的点

假设有n个点,那么将有(n-1)!/2种可能性,如果遍历所有结果,n稍微一大即刻计算不可行,蚁群优化算法的作用就是在短时间内找到一个可观的解,虽然这个解不一定会是最优解,但相对于算法消耗的算力与时间来说性价比高

Process Outline

在用蚁群算法解决TPS问题时,有两个可以提高效率与结果质量的关键,这两个点不易被注意,所以放在开头就说

  • 一、将蚂蚁按组统计:不要在一只蚂蚁结束搜索后立即更新数据,而需要等待一定数量的蚂蚁均完成后再更新
  • 二、随机选取每只蚂蚁的起点:多数时候,蚂蚁多绕的路程均来自于蚂蚁在走到最后一个点后返回起点的那一条线段,如果设定每只蚂蚁的起点不变,效率会更低,多次迭代后的结果也还是会产生交叉路线(which is obviously not the best solution)

接下来是每轮(每组)蚂蚁跑的基本流程:

  • 随机选择起点
  • 用距离与信息素对路径加权后随机选择路径
  • 更新已选择过的点,使得蚂蚁不会返回已经到达过的点
  • 当走到最后一个点,直接返回起点

一组蚂蚁跑完后更新信息素矩阵

  • 对信息素矩阵乘以一个系数使得已有的信息素浓度降低
    • 根据当前组蚂蚁跑出来的路径距离长度,当前组中路径最短的蚂蚁留下的信息素最多,而路径长的蚂蚁信息素少

Process

由于算法的计算过程比较零碎,就不拿出来说了,在最后会直接放源码,这里主要解释一些参数

Misc

点之间距离的表格是对称的

为了只读写一个数据,只取表格的一半就够了,然后写一个函数,输入两个点的index,返回对应的表格中路径的index

1
2
3
4
5
6
7
8
9
10
11
int GetPathIndex(int cur1, int cur2){
if (cur1 > cur2){
int tmp = cur1;
cur1 = cur2;
cur2 = tmp;
}
int index = 0;
for (int i = 1; i <= cur1; i++) index += numPoint - i;
index += cur2 - cur1 - 1;
return index;
}

Parameters

首先需要介绍一下算法涉及的几个重要参数

Ant

numInGroup规定了一个轮次中的蚂蚁数量,一轮中的蚂蚁越少迭代频率越高,蚂蚁越多则在更短路径上留下信息素的可能越大

Recommended Value:5~25

Pheromone

信息素的必要参数有:最小值,初始值,增量,衰减值,但我为了防止浓度过高也弄了个最大值

  • deltaPhe规定了信息素在迭代时的增量

    Recommended Value:10

  • pheMaxTimes调整了信息素最大值,由于我的最大值还决定了初始值以及后面的一些对算法的tweak,所以我给出的推荐值会比较大,以制造出不同路径的差异性,从而更快地收敛到可观的解

    Recommended Value:25~50

  • pheDrop即每次寻路后衰减的百分比

    1
    2
    pheromone[i] *= pheDrop;
    if (pheromone[i] < pheMin) pheromone[i] = pheMin;

    Recommended Value:0.7

  • 其他几个参数为了省事就通过自己估计的公式计算了嗯

    1
    2
    3
    4
    // calculate pheromone parameters
    pheMax = deltaPhe * pheMaxTimes * numInGroup;
    pheInit = pheMax / 5;
    pheMin = pheInit * Mathf.Pow(pheDrop, 5);

  • emphasizeBest因为在一轮迭代中可能存在没有蚂蚁按照当前最优解行进的情况,从而导致目前已发现的最优解路径上的信息素浓度降低,故需要在每轮迭代中都假设有一定数量的蚂蚁经过了最优路线,此参数就是用于规定这一具体数量

    1
    pheromone[i] += deltaPhe * emphasizeBest;

    Recommended Value:15

Weight

蚁群优化算法的核心就在于调整每条路径的概率,而概率由下面的公式决定

ACO_formula

其中,τ=信息素浓度,η=1/距离,αβ就是俩权重的参数

phePow,即α

Recommended Value:3.5

disPowβ

Recommended Value:6

Improvement

Crossing

最开始说过:交叉的路线明显不是最优解

但通常在最开始的时候都会出现交叉路线,而且随着点的数目增加,交叉路线被消除的概率逐步减小。所以我每次在看到最优解的图中一直有一个交叉线半天不动时就会非常难受,所以我们需要考虑如何让蚂蚁自动规避交叉

  • 在寻路时规避交叉:降低选择到 会与已选择的路径交叉的 点的概率
  • 在强调最优解时规避交叉:不强调交叉路线的最优解,甚至反手将交叉的路线的信息素浓度降低,并提升不交叉线路的信息素浓度来引导

应用以上两条,可以使路径快速迭代至一个没有交叉的状态

Small Angle

路线中出现极小的角度也是非常不理想的一件事

ACO_smallAngle

说实话目前没有什么很好的解决方案,目前我做的也仅仅是不去强化这个路径之类的,但不能快速处理掉这种情形

Annealing

调参时会发现许多参数需要动态调整,如初期应增加信息素浓度的权重,而后期却需要信息素与距离的平衡下进行优化,否则就容易自闭收敛,停滞不前;再比如初期应使信息素衰减速率较大,以促进收敛,而后期却需要额外的信息素来减少自闭的概率

这些需求与另一优化算法:模拟退火算法 相呼应,在此我没有进行这已实现,但若将这两个算法相结合,则肯定能够使得效率与结果质量都得到提升

Source Code

Comments

⬆︎TOP