主页 模型库 竞赛 学习中心 工具箱 论文库 前沿 建模空间 AI建模
学习中心 探索专题 数学规划与优化算法 启发式与元启发式

本页实验台

  1. 模拟退火解 TSP
  2. 遗传算法
  3. 粒子群
  4. 蚁群算法
  5. 大爆炸—大坍缩
  6. 禁忌搜索
  7. 差分进化
LAB 01

模拟退火解 TSP

温度曲线与当前路径同屏,看降温太快怎么卡在局部最优

问题
30 个城市的旅行商问题有 4×10³⁰ 条路线,精确求解不现实。可"每次只往更好的方向走"又会卡在第一个坑里。能不能允许暂时走坏一点?冶金学里退火的做法给出了答案。
模型
模拟退火:以一个随温度下降的概率接受变差的解。
P(接受) = min(1, e−ΔE/T);  降温 Tk+1 = αTk(α ≈ 0.95)
ΔE 是目标值变差的幅度,T 是温度。理论上若 Tk ≥ c/log k 则以概率 1 收敛到全局最优,但这个降温速度慢到没法用——实践中都用几何降温,换取速度、放弃保证。
当前路径长—
历史最优—
当前温度—
最近的接受率—
把降温系数从 0.985 调到 0.90,再点"直接跑到底"。降温太快等于直接变成爬山法:一开始就只接受变好的移动,于是很快卡在某个局部最优上再也动不了,路径上会留下几条明显交叉的边——那是 2-opt 本该消掉的。默认的 0.985 跑完是 2482,而 0.90 只能到 2593——差了 4.5%。但再往慢里调收益就很快饱和了:0.995 是 2481,0.999 还是 2481,代价却是多跑了上百倍的迭代。这条"先陡后平"的收益曲线在实际调参里到处都是,值得养成的习惯是先确认自己在陡的那一段,再决定要不要继续加算力。模拟退火的全部技巧就在这条曲线的形状里:早期要敢于变差,晚期要坚决不变差。它的物理来源是退火——金属慢慢冷却时原子有时间排进低能量的晶格,淬火则会冻出一堆缺陷。注意读数里的"接受率":经验上让它从 80% 左右平滑降到个位数,比盯着温度本身更好用。
LAB 02

遗传算法

种群、交叉、变异逐代演化,适应度曲线实时上升

问题
自然选择是一个不需要梯度、不需要凸性、甚至不需要目标函数可微的优化过程。把"交叉"和"变异"搬到计算机里,能不能真的解出问题?能,但它对编码方式极其敏感。
模型
遗传算法的三个算子与选择压力。
选择:P(i) ∝ fi(轮盘赌)或锦标赛;  交叉:两串在某点互换;  变异:以 pm 翻转一位
积木块假说:好的短片段(schema)会以指数速度在种群中扩散。这也说明为什么编码很关键——如果"好的性质"在编码上不连续,交叉就会不停地把它打碎。变异率太低会早熟收敛,太高就退化成随机搜索。
当前最好适应度—
动态规划的真最优—
还差多少—
种群多样性—
下面那张图是种群本身:一行一个个体,一列一个基因。刚开始是随机的噪声,跑上几十代之后会看到一条条竖纹——整个种群在那些位上取得了一致,多样性读数也会跟着往下掉。这是遗传算法最典型的失败模式:早熟收敛。一旦多样性归零,交叉就变成了复制,算法只剩变异这一条产生新解的路,等于退化成一个很慢的随机爬山。把变异率调到 0(滑到最左)看一眼:曲线会在某一代之后彻底变平。再把它调大到 0.05 以上:多样性保住了,但好基因也被不断打散,收敛明显变慢。这个取舍——探索与利用——是所有群体算法共同的核心问题。另外把"保留精英"关掉试试:图上那条实线(当代最好个体)会时不时往下掉,因为最好的那个个体可能在某一代被交叉打碎、没留下后代;虚线(历史最优)只记最好值,所以看不出这件事。在真实问题上,丢掉当前最优往往代价很大,所以绝大多数实现都默认开着精英保留。
LAB 03

粒子群

一群粒子在地形上找谷底,惯性与社会项的权重可调

问题
鸟群找食物时,每只鸟既记得自己去过的最好的地方,也知道全群找到的最好的地方。把这两条信息加权平均成一个速度,就是粒子群算法——只有两行更新公式,却在连续优化上很能打。
模型
粒子群优化(PSO)的速度与位置更新。
vi ← ω vi + c₁r₁(pi − xi) + c₂r₂(g − xi);  xi ← xi + vi
ω 是惯性权重,pi 是个体历史最优,g 是全局最优,r 是 [0,1] 随机数。ω 大偏探索、小偏开发,常用做法是让它随迭代从 0.9 线性降到 0.4;收敛性分析要求 ω 与 c₁+c₂ 落在一个特定区域内,否则粒子会发散。
全局最优值—
全局最优位置—
群体直径—
迭代次数—
把社会项 c₂ 调到 0,粒子们就各玩各的。这时每个粒子只在自己的历史最优附近徘徊,群体直径几乎不收缩,等于跑了 25 个独立的随机搜索。反过来把认知项 c₁ 调到 0、c₂ 拉满:所有粒子会飞快地扑向当前的全局最优,群体直径迅速塌缩,一旦那个点不是全局最优,整群就再也出不来了。这两个极端之间的平衡,就是粒子群唯一真正需要调的东西。惯性 w 的作用是另一回事:它决定粒子多"重"。w 接近 1 时粒子冲过头、反复穿越谷底;w 太小时粒子几乎不动,只会顺从地被拉过去。常见做法是让 w 随迭代从 0.9 线性降到 0.4——你会发现这和模拟退火的降温是同一个思路。
LAB 04

蚁群算法

信息素矩阵可视化,与涌现模块的蚁群觅食共用同一套机制

问题
蚁群算法和涌现模块里的蚁群觅食是同一套机制,区别只在于:那里是解释自然现象,这里是拿来解 TSP。同一个模型换一个用途,这本身就是"建模"这件事最常见的形态。
模型
信息素的构造与更新,以及与启发信息的权衡。
pij = τijαηijβ ⁄ Σl τilαηilβ;  τij ← (1−ρ)τij + Σk Q/Lk
α 控制"跟随经验"的权重,β 控制"贪心看距离"的权重,ρ 是蒸发率。α 太大会过早锁死在一条早期路线上,ρ 的作用正是给系统一个遗忘错误的机会。
当前最优路径长—
最近邻基准—
信息素熵—
迭代轮数—
把 α 调到 0,蚂蚁就完全不看信息素了。这时每只蚂蚁都只按"下一个城市越近越可能去"随机走,等于做了一堆随机化的最近邻。历史最优当然还会靠运气偶尔刷新,但信息素熵会一直停在高位(3.2 对默认参数的 1.9),说明群体根本没有在积累经验;100 轮之后的结果是最近邻基准的 72%,而默认参数能做到 68%。反过来把 β 调到 0、α 拉大:蚂蚁只跟着信息素走,最早偶然走出来的那条路会被迅速强化,信息素熵掉得飞快,整个群体锁死在一条平庸的路线上。蒸发率 ρ 是这里的遗忘机制。ρ 太小,早期的偶然选择会被永久记住;ρ 太大,刚学到的东西转头就忘光。这三个参数合起来做的事情,和退火的温度、粒子群的 c₁/c₂ 是同一件事:在"用已知的好路"和"去试没走过的路"之间安排一个随时间变化的配比。顺带一提,这套机制和涌现模块里蚂蚁觅食那个实验完全同源——那里是真蚂蚁,这里是拿同一个机制当优化器用。
LAB 05

大爆炸—大坍缩

只靠一个会自己缩小的撒布半径就能收敛,元启发式最干净的骨架

问题
前面四个算法各有一套复杂的机制:温度表、交叉变异、速度公式、信息素。那么一个元启发式最少需要几样东西才能工作?大爆炸—大坍缩给出的答案近乎苛刻:一个撒点半径,一个加权平均,没了。
模型
大爆炸—大坍缩(Erol & Eksin, 2006)。每一轮先「爆炸」出 N 个候选点,再「坍缩」成一个质心。
xi = xc + σk·ε,  ε ~ N(0, 1);  σk = σ₀·(定义域宽度) ⁄ kβ;  xc = Σ (xi/fi) ⁄ Σ (1/fi)
σk 是第 k 轮的撒布半径,β 控制它缩得多快。注意 xc 不是普通平均,而是以 1/f 为权重的加权平均——好点的话语权是非线性地大。整个算法没有温度、没有记忆、没有个体状态,收敛完全由 σk 这一条会自己缩小的半径提供。
当前最优值—
质心位置—
本轮撒布半径 σ—
有效参与点数—
轮次—
先盯住第三张图,再换地形。它把这一轮 N 个候选点的权重从大到小排了出来,横着那条虚线是「如果只做普通平均」每个点该有的份额 1/N。在默认的 Rastrigin 上,这排柱子相当平,读数里的「有效参与点数」中位数是 40 个里的 25.8 个;换成 Himmelblau 或者凸碗,它立刻掉到 7.2 和 7.3——同一个算法,同样的 N,真正对质心有发言权的点少了七成。原因在于 1/f 这个权重不是平移不变的。Rastrigin 的函数值有个 +20 的地板,40 个随机点的 f 大多落在 20 到 60 之间,1/f 撑死差三倍,于是权重接近均匀;而 Himmelblau 和凸碗的最小值就是 0,只要有一个点凑近谷底,它的 1/f 就能甩开其余点几个数量级。这件事在实践中很要命:给目标函数整体加一个常数,数学上的最优解一个都没变,这个算法的选择压力却彻底换了一副面孔。凡是用「适应度的倒数」或者「适应度的比例」来做选择的算法,都有同一个毛病,遗传算法的轮盘赌也在其中。再调收缩指数 β。8 组起始点、180 轮、取中位数:β=0.5 只到 2.6×10⁻²,β=1.0 是 7.8×10⁻⁵,β=2.0 是 6.7×10⁻⁹。看上去越陡越好,可把最差的那一次也报出来就不是这么回事了:β=2.0 最差的一组卡在 2.3,β=3.0 更极端——最好的一组做到 2.3×10⁻¹³,中位数却是 1.9,八组里过半彻底没收敛。半径缩得太快,等于还没找到地方就先把手收死了。这正是本页结尾那条规矩的活标本:只报一次运行的最好结果,你会得出「β 越大越好」的结论,而它是错的。最后换一下「下一轮围着谁撒」。在 Himmelblau 上,加权质心的中位结果是 1.7×10⁻⁶,换成当前最优点反而退到 4.1×10⁻⁶,取两者中点更差。这有点反直觉——四个一样深的谷,质心不是应该落在中间的高地上吗?回头看第三张图就明白了:权重早就集中在少数几个点上,所谓「质心」根本不是平均,而是一次带平滑的选优。它比单点最优多了一点抗噪能力,又没有平均的那种拖累。
LAB 06

禁忌搜索

不掷骰子也敢变差:被禁的边逼着它爬出局部最优

问题
模拟退火要敢于变差,靠的是掷骰子。可掷骰子意味着坏运气:它可能刚爬出一个坑,下一步又被推了回去。有没有办法既能接受变差,又不把方向交给运气?禁忌搜索的回答是:不禁止变差,禁止回头。
模型
禁忌搜索(Glover, 1986):每步都取邻域里最好的动作,但最近动过的属性在一段时间内不许被撤销。
xk+1 = argmin { f(y) : y ∈ N(xk),y 的属性不在禁忌表中 };  特赦:若 f(y) < f(x*) 则忽略禁忌
这里的「属性」取被拆掉的两条边,禁忌期限 tenure 是它们多少轮内不许被接回去。注意这一步是确定性的:没有任何随机接受,变差是被禁忌表逼出来的,不是掷出来的。特赦准则是必需的补丁——禁忌按属性而不是按解来禁,难免误伤那些真正更好的解。
当前路径长—
历史最优—
这一步的变化—
被迫变差的步数—
把上一步原样撤回去的次数—
特赦次数—
把禁忌期限调到 0,然后盯住读数里那一行「把上一步原样撤回去的次数」。它会一路涨到 1189/1200——算法有 99% 的步子在做同一件事:走一刀,下一步再把这一刀翻回来,走一刀,再翻回来。第二张图上「当前长度」那条线会缩成两个值之间的一条锯齿,「被迫变差」反而是 0/1200,因为它压根没往外走过。这就是「每步都取邻域里最好的动作」这条规则自带的毛病:从局部最优出发,最好的动作是上坡,而上坡之后最好的动作必然是原路下坡。禁忌表存在的全部理由,就是堵住这一步。把期限调到 3,撤回次数立刻变成 0/1200,被迫变差涨到四分之一左右的步子——它终于开始往外走了。效果是实打实的。8 组城市、40 座城、每步看整个邻域、跑 600 步,取中位数:期限 0 只能做到最近邻基准的 82.7%,期限 3 是 81.3%,期限 12 是 80.5%,再往上调到 30 还是 80.5%——收益在期限十几的时候就见顶了。经验规则是 tenure 取 √n 到 n/4 之间,40 座城对应 6 到 10,和实测的拐点基本吻合。「每步看邻域的多大一块」这个滑杆请务必拉到 100% 试一次再调小。只随机抽一小部分邻居时,撤回上一步的次数会从 1189 掉到一百上下——不是禁忌起了作用,而是随机抽样恰好没抽中那个撤回动作。这是一个很典型的陷阱:噪声掩盖了缺陷,看上去像是问题被解决了。至于「特赦准则」,实测它没有你以为的那么重要。默认参数下 600 步里它只触发 2 到 4 次,8 组城市的中位结果开与关完全一样。它是一层安全网,不是一个性能杠杆——禁忌是按「动作」而不是按「解」来禁的,难免误伤真正更好的解,特赦负责兜住那几次误伤。期限越长、禁得越宽,它的价值才越显出来。最后请回到本页第一台的模拟退火,把两条「当前值」曲线摆在一起看。退火那条是抖出来的,方向随机,运气不好会被推回原来的坑;禁忌这条是被推出来的,每一步仍然是当下能做的最好选择,只是有几个选项被暂时划掉了。同样是「允许变差」,一个交给概率,一个交给记忆——这是这一页上最值得琢磨的一组对照。
LAB 07

差分进化

步长不用人定,种群自己散开又收拢,把它一起缩小

问题
前面几个算法都有一个让人头疼的参数:步子该迈多大。退火要定降温表,粒子群要定惯性权重,大爆炸要定收缩指数。这些都是人替算法拍的脑袋。能不能让算法自己知道该迈多大步?差分进化的办法出奇地简单:从种群里随便抽两个个体,它们的差就是步长。
模型
差分进化(Storn & Price, 1997):变异来自种群自身的差分,选择是一对一的贪心。
vi = xr1 + F·(xr2 − xr3);  ui,j = vi,j 以概率 CR,否则 xi,j;  xi ← ui 当 f(ui) ≤ f(xi)
F 是缩放因子,CR 是交叉概率,r1/r2/r3 是互不相同且不等于 i 的随机下标。关键在于 (xr2 − xr3):种群散开时它就大,种群收拢时它自动变小,于是步长会随搜索进程自己退火,不需要任何时间表。常用范围 F ∈ [0.4, 0.9]、CR ∈ [0.1, 0.9],且 CR 越大越依赖各维之间的耦合。
当前最优值—
第几代首次跌破 1e-6—
本代顶替父代的个体数—
平均差分步长—
代数—
第二张图上那条蓝虚线是这一台真正的主角。它画的是这一代所有 |F·(xr2 − xr3)| 的平均值,也就是差分进化实际迈出的步子。没有人给它写过时间表,它却和上面那条最优值曲线几乎平行地一路下滑。种群收拢,差分就变小,步长跟着自己退火。回头看第一台的降温系数、第三台的惯性权重、第五台的收缩指数——那三个都是人替算法拍的脑袋,差分进化把同一个量变成了种群状态的自然推论。这是它在实践中特别好调的根本原因。读数里的「第几代首次跌破 1e-6」比最终最优值有用得多,因为二维 Rastrigin 上这一台两百代内基本必然收敛到机器精度,拿终值比参数全是 0 比 0。8 组初始种群取中位数:F=0.4 是第 73 代,F=0.6 是第 71 代,F=0.9 退到第 81 代,F=1.2 要到第 113 代,F=1.6 则有过半的种子两百代都到不了。同时盯住「本代顶替父代的个体数」:F 从 0.6 涨到 1.2,它从 26/30 掉到 9/30——步子迈太大,三分之二的函数评估都花在了比父代还差的点上。交叉率 CR 这里有个容易踩的坑,值得原样讲出来。实测 CR=0.05 是第 58 代,CR=0.90 反而要第 71 代——越低越快,和「CR 太低会白算一场」的直觉正好相反。原因是这个问题只有两维,而二项交叉强制至少一维取变异向量,所以 CR 实际只管「第二维要不要跟着改」;Rastrigin 又是可分的,一次只动一维本来就更划算。CR 的真正威力要到高维、且各维之间高度耦合时才显出来——这也提醒一件事:在低维玩具问题上调出来的参数,换到真问题上常常不作数。最值得一试的是把策略从 rand/1 换成 best/1。基向量从「随便一个个体」变成「当前最优」,主图右上角那块放大图里,红箭头的起点会一直钉在最优点附近。在 Himmelblau 上它快得离谱:中位第 23 代,而 rand/1 要第 57 代。可换到 Rastrigin 上,8 组种子里最快的一组第 27 代就到了,中位数却是「两百代没到」——整群个体都朝同一个点靠,一旦那个点不是全局最优,全体一起陷进同一个小坑。rand/1 慢而稳,best/1 快而险,这个取舍和第三台粒子群的 c₁/c₂ 是同一回事。

这一页想说的

七个算法看起来风格迥异,骨架却是同一个:产生候选解、评价、按某种偏好保留、再产生。它们之间真正的差别只在"怎么记住经验"——退火靠温度,遗传靠种群,粒子群靠个体与群体的两个最优,蚁群靠信息素,大爆炸靠一个会自己缩小的半径,禁忌搜索靠一张短期记忆表,差分进化靠种群自身的差分。每一台的第三张图画的都是这件「记忆」本身:接受概率曲线、基因位的固化、速度的三个分量、信息素矩阵、权重的集中程度、禁忌表、试验向量的去留。看懂那张图,比看懂收敛曲线重要得多——收敛曲线只告诉你结果,那张图才告诉你它为什么是这个结果。用它们的时候请守住两条底线:一、一定要有基准。至少跑一个贪心解、一个随机解,不然你无法判断算法到底有没有在工作;二、一定要报告随机性。同一份参数跑十次,把最好、最差、中位数都写出来,只报一次运行的最好结果,在工程上是没有意义的。

接着看

← 返回模块 ← 返回探索专题