


一句话介绍: 这套 MATLAB 工程用 5 种不同的智能优化思想求解旅行商问题(TSP):在每座城市只访问一次并最终回到起点的条件下,尽量缩短整条闭环路线。工程同时提供蚁群算法、遗传算法、Hopfield 神经网络、模拟退火和禁忌搜索,非常适合观察不同优化机制怎样解决同一个组合优化问题。
假设一个旅行商需要访问多座城市:
每座城市必须访问一次;
不能漏掉城市;
最后还要回到起点;
希望总路程尽可能短。
如果城市数量只有几座,可以人工尝试。
但当城市数量增加到 30 时,可能路线数量会迅速膨胀,已经不适合把所有排列逐一计算。
这套程序采用 5 种不同思路进行搜索:
城市坐标 → 计算距离矩阵 → 生成候选路线 → 优化算法不断修改路线 → 比较闭环距离 → 输出较短路线
算法的区别不在于“距离怎么算”,而在于:
下一条候选路线应该怎样产生,以及怎样避免一直困在较差路线附近。
假设一条路线为:
S = [s₁, s₂, …, sₙ]
程序中的 CalDist.m 按照下面的方式计算总长度:
L(S) = Σ(i=1→n-1) d(sᵢ,sᵢ₊₁) + d(sₙ,s₁)
其中:
sᵢ:第 i 个访问的城市;
d(a,b):城市 a 与城市 b 的欧氏距离;
最后一项 d(sₙ,s₁):表示旅行商最后必须回到起点。
所以所有算法最终都在解决同一个目标:min L(S)
也就是:
在合法城市排列中寻找总闭环距离尽可能短的路线。
不需要把全部文件逐个展开,理解下面这些就够了。
文件 | 作用 |
|---|---|
| 提供城市坐标并计算城市间距离矩阵 |
| 计算一条完整 TSP 路线的闭环距离 |
| 蚁群算法 |
| 遗传算法 |
| Hopfield 神经网络求解 TSP |
| 模拟退火算法 |
| 禁忌搜索算法 |
drawTSP.m 和 drawTSP10.m 主要用于把搜索到的路径可视化,不属于核心优化逻辑。
tsp.m 内置了:10 城市、30 城市、50 城市、75 城市
四组二维坐标数据,并使用普通欧氏距离:
d(i,j) = √[(xᵢ-xⱼ)² + (yᵢ-yⱼ)²]
源码注释还给出了参考结果:
10 城市参考最短距离约为 2.691
30 城市参考最短距离约为 423.741
50 城市参考最短距离约为 427.855
75 城市参考最短距离约为 549.18
但当前 5 个算法默认并没有全部使用相同城市数。
当前源码默认设置如下:
算法 | 默认城市数 | 默认搜索规模 |
蚁群算法 | 30 | 30 只蚂蚁,200 次迭代 |
遗传算法 | 30 | 100 个个体,1000 代 |
Hopfield 神经网络 | 30 | 1000 次连续状态更新 |
模拟退火 | 30 | 多温度阶段,每阶段 3000 次邻域搜索 |
禁忌搜索 | 30 | 1000 步,每步产生 200 个候选解 |
可以先用一张表快速理解。
算法 | 核心思想 | 搜索对象 | 跳出局部最优的主要方式 |
蚁群 | 多只蚂蚁根据距离和信息素逐步建路 | 多条路线 | 信息素蒸发 + 多蚂蚁随机选择 |
遗传 | 把路线当染色体进行群体进化 | 路线种群 | 交叉 + 变异 |
Hopfield | 把 TSP 转成神经网络能量下降问题 | 连续神经元状态 | 连续动力学演化 |
模拟退火 | 从一条路线出发不断产生邻域路线 | 单条路线 | 按温度概率接受较差解 |
禁忌搜索 | 在当前路线附近搜索大量交换方案 | 单条路线 | 禁忌表 + 藐视准则 |
下面分别看它们为什么能够工作。
ant_colony_system.m 默认设置:
城市数:30、蚂蚁数:30、最大迭代次数:200
α = 1、β = 5、ρ = 0.5、Q = 100
每只蚂蚁都需要从当前城市选择下一座还没有访问的城市。
源码中的选择倾向可以概括成:
P(i→j) ∝ τ(i,j)^α × η(i,j)^β
其中:
τ(i,j):城市 i 到 j 边上的信息素;
η(i,j) = 1 / d(i,j):距离启发因子;
α:信息素的重要程度;
β:短距离的重要程度。
当前 β = 5、α = 1,因此距离启发在选择中占有比较明显的作用。
简单来说:
距离短的城市更容易被选择,而过去优秀路线经过较多的边又会因为信息素增加进一步获得优势。
每轮完成以后,程序会更新信息素:
τ_new = (1-ρ) × τ_old + Δτ
其中 ρ = 0.5。
前半部分表示旧信息素会不断蒸发,后半部分则根据蚂蚁路线重新增加信息素。
源码中每只蚂蚁对所走边增加:Q / L
路线越短,L 越小,增加的信息素就越多。
因此形成了一个正反馈:
较短路线 → 信息素增加更多 → 后续更容易被选择 → 继续强化较好路线
同时,信息素蒸发和随机概率选择又避免所有蚂蚁从一开始就完全锁死在同一条路径上。
源码还把上一轮最佳路线直接放入下一轮第一只蚂蚁中,相当于保留历史较优路线。
genetic_algorithm.m 默认:
城市数:30、种群大小:100、最大代数:1000、交叉概率:0.8、变异概率:0.8、程序首先随机产生 100 条合法城市排列。
每一条排列就是一个个体,例如:[5, 2, 9, …, 1]
表示旅行商按这个顺序访问城市。
路线越短,适应度越高。
源码先计算:F = 1000 / L
之后选择概率又进一步使用:
Pᵢ ∝ Fᵢ¹⁵
这意味着当前代码的选择压力比较强:
距离稍短一些的路线,经过 15 次幂放大以后,会明显获得更高的繁殖机会。
程序主要使用两种操作。
两个父代随机选择一段路线并交换。
由于 TSP 要求每个城市只能出现一次,所以交换后还必须进行修复,避免:
某个城市重复;某个城市消失。
源码实现了一种类似部分映射交叉的修复过程。
程序随机选择两个位置,把中间一段路线反转:
原路线:A → B → C → D → E
变异后:A → D → C → B → E
这种区段反转很适合 TSP,因为它能够一次性改变多条相邻边,同时仍然保持“每个城市只出现一次”。
代码每一代都会:
直接用新生成的 100 个子代替换整个上一代种群。
没有单独设置一个明确的“历史最佳个体永久保留”步骤。
因此某一代偶然出现的很好路线,在下一代理论上可能丢失。
程序记录的 ymax 是:
当前这一代的最佳路线长度
并不严格等于“从第一代到当前为止历史最短路线”。
如果希望算法更稳定,常见改进方式是增加精英保留:每代直接把历史最优路线复 制到新种群中。
simulated_annealing.m 默认使用 30 城市。
它不是同时维护很多路线,而是从一条当前路线出发,随机选择两个位置,把中间路线反转,产生一个新方案。
如果新路线更短:
直接接受。
如果新路线更长,也不是一定拒绝,而是按照:
P = exp[(L_current - L_new) / T]
决定是否接受。
因为更差路线满足 L_new > L_current,所以指数为负数,接受概率小于 1。
这里的 T 就是“温度”。
如果算法规定:
只有变好才允许移动。
那么一旦当前路线周围所有简单修改都变差,搜索就会停在局部最优。
模拟退火允许在温度较高时暂时走向一个更差方案,相当于:
先绕开眼前的小山谷,再寻找更低的位置。
随着温度下降,接受较差路线的概率逐渐降低,搜索会越来越稳定。
当前程序:终止温度 tf = 0.01、降温系数 alpha = 0.80
每个温度下进行 100 × CityNum = 3000 次邻域尝试
初始温度则根据 100 条随机路线的距离范围自动估计。
tabu_search.m 也是针对 30 城市。
它从一条随机路线开始,每一步随机生成 200 个不同的城市位置交换方案。
也就是说:
当前路线 → 随机交换两个城市位置 → 得到一批邻域路线
程序再从这些候选路线中挑出前 100 个较好的方案进行检查。
默认参数:、搜索步数:1000、每步候选解:200
保留较好候选:100、禁忌长度:50
如果没有记忆机制,局部搜索可能出现:
A 路线 → B 路线 → A 路线 → B 路线……
程序用 Tlist 记录近期发生过的城市交换,并让它们在一定时间内处于禁忌状态。
这样就减少了:
刚刚走出去又马上走回来的循环搜索。
但禁忌不是绝对的。如果某个候选路线比历史最优路线还要短,程序会触发“藐视准则”:
即使这个移动在禁忌表里,也允许接受。
因此禁忌表负责避免短期重复,藐视准则负责防止禁忌规则挡住真正的更优解。
Hopfield 方法和前面四种算法差异最大。
它不是直接保存一个 [城市1, 城市2, ...] 的离散排列,而是建立一个:
10 × 10的神经元状态矩阵。
可以理解为:
第 u 个城市是否出现在路线第 i 个位置。
神经元输出使用 Sigmoid:
y(u,i) = 1 / [1 + exp(-2z(u,i)/μ₀)]
其中当前:μ₀ = 0.02
随着连续状态 z 更新,输出 y 会逐渐趋向 0 或 1。
最终希望形成一个近似排列矩阵:每一座城市只选一个位置;
每一个位置只放一座城市;总共选中 n 个城市;
相邻位置之间的城市距离尽量短。
源码设置:
参数 | 当前值 | 主要作用 |
A | 500 | 抑制同一城市同时占多个位置 |
B | 500 | 抑制同一位置出现多个城市 |
C | 200 | 约束总激活数量接近城市数 |
D | 500 | 惩罚相邻访问城市之间的长距离 |
因此 Hopfield 的核心思想不是直接“随机改路线”,而是:
把合法性约束和路线距离一起写进神经网络的能量下降过程。
网络不断演化,希望最终进入一个既满足排列约束、总距离又较短的稳定状态。
它们其实代表了 5 种不同的优化思路。
走得好的边留下更多信息素,让后续个体继续利用。
优秀路线拥有更多繁殖机会,再通过交叉和变异组合出新路线。
允许暂时走差,换取逃离局部最优的机会。
记住近期移动,防止搜索反复回到刚走过的状态。
把路线合法性和距离转换成神经网络约束,让网络动态演化到较低能量状态。
这也是这套工程最大的学习价值:
同一个最短路径问题,可以从群体智能、进化、物理退火、搜索记忆和神经动力学五种完全不同的角度处理。
tsp.m 的源码注释给出了 30 城市数据的参考结果:
参考最短距离约为 423.741
因此对于默认运行 30 城市的:
蚁群算法、遗传算法、模拟退火、禁忌搜索
可以把 423.741 作为一个很有用的参照。
例如某次运行得到 430,说明已经比较接近参考路线;如果得到 600,则仍有明显优化空间。
但由于算法随机性,不能保证每次运行都一定达到 423.741。
每种算法在运行过程中都会不断产生新的路线,并计算闭环总距离。
用户主要可以观察:
当前找到的城市访问顺序;
当前或历史较优路线长度;
搜索过程中路线长度是否逐渐下降;
不同算法最终收敛到什么水平。
因此这个工程既可以用来“求一条较短路线”,也可以用来观察:
不同优化机制的搜索过程有什么明显差别。
同一份工程集中包含 5 种典型优化方法,非常适合学习算法之间的机制差异。
可以修改 tsp.m 中的城市坐标,观察不同城市布局对最短路线和收敛过程的影响。
例如研究:蚁群的 α、β、ρ;
GA 的种群大小、交叉率、变异率;
SA 的降温速度;
禁忌搜索的禁忌长度;
Hopfield 的能量权重。
如果进一步统一城市数和评价方法,就可以扩展成一个较完整的多算法 TSP 对比实验。
5 种算法彼此独立,可以分别运行:
ant_colony_system.m:蚁群
genetic_algorithm.m:遗传算法
hopfield_neuro_network.m:Hopfield
simulated_annealing.m:模拟退火
tabu_search.m:禁忌搜索
如果希望统一比较 5 种方法,建议先把每个算法中的 CityNum 修改成相同值,并确认 Hopfield 在更大城市规模下的计算量是否可以接受。
算法 | 当前关键参数 |
蚁群 | α=1,β=5,ρ=0.5,Q=100,200 次迭代 |
遗传 | 种群 100,1000 代,交叉率 0.8,变异率 0.8 |
Hopfield | A=500,B=500,C=200,D=500,1000 次更新 |
模拟退火 | α=0.8,终温 0.01,每温度 3000 次搜索 |
禁忌 | 200 个候选,保留 100 个,禁忌长度 50,1000 步 |
做参数实验时最好一次只改一个参数,否则很难判断结果变化来自哪里。
这是一个 MATLAB 多算法 TSP 优化工程:所有方法都以闭环路线总距离最短为目标,但蚁群依靠信息素积累,遗传算法依靠选择/交叉/变异,模拟退火通过概率接受差解逃离局部最优,禁忌搜索利用短期记忆避免循环,而 Hopfield 则把路线约束转换成神经网络能量下降问题。源码同时内置 10、30、50、75 城市数据,因此非常适合学习不同智能优化机制,但若要做公平性能排名,需要先统一问题规模和实验条件。