启发式优化算法简介
一、启发式算法到底解决什么问题
优化问题的基本形式通常可以写成:
或者:
其中,\(x\) 是一个候选方案,\(\Omega\) 是所有可行方案组成的搜索空间,\(f(x)\) 是衡量方案质量的目标函数。
很多现实问题虽然容易描述,却很难精确求解。例如:
- 一辆车应该按什么顺序访问几十个客户,才能使总路程最短;
- 几百个任务应该怎样分配给多台机器,才能使完工时间最短;
- 应该从上千个特征中选择哪些特征,才能提高模型性能;
- 控制器的多个参数应该取什么值,才能使系统响应最好;
- 在预算、时间、容量等限制下,应该选择哪些项目;
- 神经网络超参数应该如何组合,才能获得较好的验证结果。
这些问题的共同特点是搜索空间很大。假设有 \(n\) 个物品,每个物品只有“选”和“不选”两种状态,则方案数量为:
当 \(n=100\) 时,方案数量约为:
如果问题是对 \(n\) 个任务进行排序,方案数量为:
当 \(n=50\) 时,\(50!\) 已经远远超过能够穷举的范围。
启发式算法的目标不是把所有方案都检查一遍,而是利用问题结构、随机性、历史经验或群体协作,在有限时间内搜索到质量较高的方案。
它们通常具有以下特点:
- 不要求目标函数可导;
- 可以处理离散、连续、排列和混合变量;
- 可以处理复杂约束;
- 适用于黑盒函数;
- 一般不能保证一定找到全局最优解;
- 算法结果可能具有随机性;
- 实际效果高度依赖问题表示和搜索操作。
学习启发式算法时,最重要的不是记住某种自然现象的比喻,而是理解三个共同问题:
- 一个解怎样表示;
- 新解怎样产生;
- 怎样平衡探索和利用。
二、探索与利用是所有算法的共同核心
启发式搜索始终面临两个相反的目标。
探索是指搜索新的区域,希望发现此前没有见过的优良方案。利用是指围绕已经发现的好方案继续优化,希望把当前结果做得更精细。
如果探索过弱,算法会很快聚集在某个局部最优附近;如果探索过强,算法会像随机搜索一样四处跳动,难以积累已有经验。
不同算法使用不同机制平衡探索与利用:
- 局部搜索主要利用当前区域;
- 模拟退火通过温度控制接受较差解的概率;
- 遗传算法用选择进行利用,用交叉和变异进行探索;
- 粒子群通过个体最优、群体最优和惯性共同控制运动;
- 蚁群算法通过信息素强化优秀路径,通过挥发和随机选路保持探索;
- 禁忌搜索通过记忆机制避免搜索循环;
- 变邻域搜索通过切换邻域改变搜索尺度。
可以把一个通用启发式算法抽象为:
生成初始解或初始种群
评估候选解质量
重复:
根据当前信息生成新解
决定是否接受或保留新解
更新搜索经验
调整探索强度
直到满足停止条件
输出搜索过程中发现的最好解
这里的“搜索经验”可以是历史最优位置、信息素、禁忌表、种群结构或温度状态。
三、局部搜索
3.1 核心思想
局部搜索从一个当前解出发,不断检查它附近的方案。如果发现更好的邻域解,就移动过去,然后继续搜索。
对于最小化问题,设当前解为 \(x\),它的邻域为 \(N(x)\)。如果存在:
并且:
则可以用 \(x'\) 替换当前解。
局部搜索的关键不是某个固定公式,而是邻域函数。邻域函数决定什么样的方案被认为是当前方案的“附近”。
3.2 邻域的含义
在连续优化中,邻域可以是给变量加入小扰动:
其中 \(\epsilon\) 可以服从均匀分布或正态分布。
在二进制问题中,邻域可以是随机翻转一个基因:
101001 → 101101
在任务排序问题中,邻域可以是交换两个任务:
[1, 2, 3, 4, 5]
→
[1, 4, 3, 2, 5]
在旅行商问题中,常见邻域包括:
- 交换两个城市;
- 插入一个城市;
- 反转一段路线;
- 2-opt;
- 3-opt。
2-opt 的思想是删除路线中的两条边,再以另一种方式连接,从而消除路线交叉或缩短总距离。
3.3 邻域选择策略
局部搜索并不一定检查全部邻域,可以采用不同策略。
第一改进策略:按某种顺序检查邻域,一旦发现第一个更好的解就立即移动。它速度快,但不一定选择改进幅度最大的方案。
最优改进策略:检查整个邻域,选择其中最好的解。它单次改进更充分,但计算量更大。
随机改进策略:随机选择邻域解,只要满足条件就接受。它实现简单,也能增加一定随机性。
3.4 核心流程
随机生成初始解 current
best = current
重复:
从 current 的邻域中生成候选解 candidate
如果 candidate 更好:
current = candidate
如果 current 优于 best:
best = current
否则:
保持 current 不变
直到邻域中无法继续改善
输出 best
3.5 为什么会陷入局部最优
局部最优是指当前解比所有邻域解都好:
但它不一定比整个搜索空间中的所有解都好。
局部搜索只能看到当前邻域。如果通往更优区域的路径必须先经过一个较差解,它就无法继续前进。
这也是后续算法要解决的核心问题:
- 模拟退火允许暂时接受较差解;
- 禁忌搜索允许移动到较差解,但禁止立即返回;
- 变邻域搜索改变邻域结构;
- 遗传算法同时维护多个搜索区域;
- 粒子群通过群体经验移动;
- 蚁群通过概率构造新方案。
3.6 适用场景
局部搜索适合:
- 可以快速评价邻域变化的问题;
- 邻域结构明确的问题;
- 需要快速获得一个较好方案的问题;
- 作为其他算法的局部改进模块。
很多优秀算法并不是完全替代局部搜索,而是负责把搜索带到新的区域,再让局部搜索进行精细优化。
四、模拟退火算法
4.1 核心思想
模拟退火在局部搜索的基础上增加了一个关键机制:新解更差时,不一定拒绝,而是按一定概率接受。
对于最小化问题,定义:
如果:
说明新解更好,直接接受。
如果:
说明新解更差,此时按照下面的概率接受:
其中 \(T\) 是当前温度。
4.2 为什么接受较差解
假设当前解位于一个局部最低点,周围所有小范围移动都会让目标值变差。如果算法永远拒绝较差解,就会停在这里。
模拟退火允许先走到一个稍差的位置,再从那里进入新的区域。虽然短期看是退步,但可能换来长期更好的结果。
因此,接受较差解不是算法犯错,而是一种有控制的探索策略。
4.3 温度的作用
当温度较高时,较差解也有较高概率被接受。算法前期更像大范围随机探索。
当温度较低时,接受较差解的概率迅速降低。算法后期逐渐变成稳定的局部搜索。
例如 \(\Delta E=5\):
当 \(T=100\) 时:
当 \(T=10\) 时:
当 \(T=1\) 时:
温度越低,算法越不愿意冒险。
4.4 当前解与历史最优解
模拟退火必须同时维护两个解:
- 当前解:用于继续搜索,可以变好,也可以变差;
- 历史最优解:记录搜索过程中发现的最好结果,只会改善。
如果把两者混为一谈,那么一旦接受较差解,就会丢失之前找到的好结果。
4.5 降温策略
最常见的是几何降温:
其中:
例如 \(\alpha=0.95\) 时,温度依次为:
\(\alpha\) 越接近 1,降温越慢,搜索通常越充分,但运行时间越长。
还可以使用:
线性降温:
倒数降温:
对数降温:
理论上的全局收敛通常要求非常缓慢的降温,但工程实践更常使用几何降温。
4.6 初始温度的估计
如果希望一个典型较差变化 \(\Delta E\) 在初始阶段以概率 \(p_0\) 被接受,可以令:
整理得到:
例如,希望典型坏变化 \(\Delta E=10\) 以 80% 的概率被接受:
实际中可以先随机采样若干邻域变化,统计正的 \(\Delta E\),再估计初始温度。
4.7 邻域与步长
模拟退火的效果高度依赖邻域设计。
连续变量可以采用:
其中:
高温时可以使用较大的 \(\sigma\),进行大范围探索;低温时逐渐减小 \(\sigma\),进行精细搜索。
排列问题可以使用交换、插入、逆序或 2-opt。
如果邻域太小,算法移动缓慢;如果邻域太大,候选解变化过于剧烈,低温时几乎不会被接受。
4.8 核心流程
生成初始解 current
best = current
设置初始温度 T
while T 大于最低温度:
在当前温度下重复若干次:
candidate = neighbor(current)
delta = f(candidate) - f(current)
如果 delta <= 0:
接受 candidate
否则:
p = exp(-delta / T)
生成随机数 r
如果 r < p:
接受 candidate
如果 current 优于 best:
best = current
T = alpha * T
输出 best
4.9 适用问题与局限
模拟退火适合连续、二进制、排列和调度问题,只要能够定义合理邻域。
优点:
- 结构简单;
- 内存占用低;
- 能跳出局部最优;
- 不要求目标函数可导;
- 容易与局部搜索结合。
局限:
- 对邻域和降温策略敏感;
- 降温太快容易早熟;
- 降温太慢运行时间长;
- 通常是单解搜索;
- 实际快速版本不能保证全局最优。
五、遗传算法
5.1 核心思想
遗传算法同时维护多个候选解,称为种群。
每个候选解称为个体,个体的表示称为染色体。算法让较好的个体获得更多繁殖机会,通过交叉重新组合已有信息,通过变异持续引入新信息。
遗传算法可以概括为:
保留优秀信息 + 重新组合信息 + 持续引入新信息
其中:
- 选择负责保留;
- 交叉负责组合;
- 变异负责创新;
- 精英保留防止最好解丢失;
- 种群多样性保证搜索不会过早停止。
5.2 编码方式
遗传算法的第一步是确定染色体怎样表示一个实际解。
二进制编码:
[1, 0, 1, 1, 0]
适合背包、特征选择和设备开关问题。
整数编码:
[2, 1, 3, 2]
可以表示四个任务分别分配给哪台机器。
实数编码:
[2.5, -1.2, 7.8]
适合连续参数优化。
排列编码:
[3, 1, 5, 2, 4]
适合旅行商、排序和调度问题。
树形编码适合数学表达式、程序和规则优化。
混合编码适合同时含有布尔、整数、连续和排列变量的问题。
编码不只是存储格式,它决定交叉和变异能否产生有意义的新解。
5.3 初始种群
初始种群可以完全随机生成,也可以使用启发式方法生成部分优质个体。
完全随机的优点是多样性高,缺点是初始质量可能很差。
完全启发式的优点是起点好,缺点是个体之间可能过于相似。
实际中常使用混合初始化:
- 一部分随机;
- 一部分由贪心或领域规则生成。
5.4 适应度函数
适应度函数决定算法认为哪个个体更优秀。
对于最大化问题,可以直接使用:
对于最小化问题,可以使用排序选择或锦标赛选择,不一定必须把目标函数转换成正数。
也可以使用:
但这种转换会改变适应度尺度,需要谨慎。
约束问题常使用惩罚函数:
其中 \(V(x)\) 是约束违反程度。
适应度函数是遗传算法的方向盘。如果设计错误,算法会高效地优化错误目标。
5.5 选择操作
选择决定哪些个体有机会成为父代。
轮盘赌选择的概率为:
适应度越高,选择概率越大。但轮盘赌对适应度数值尺度比较敏感。
锦标赛选择更常用。每次随机抽取 \(k\) 个个体,选择其中最好的一个。
当 \(k\) 较小时,随机性较强,多样性较好;当 \(k\) 较大时,优秀个体更容易胜出,收敛更快,但早熟风险更高。
排名选择先按适应度排序,再根据排名分配概率,能够减少极端个体对种群的垄断。
5.6 交叉操作
交叉将两个父代中的部分信息组合起来。
单点交叉示例:
父代1:111|100
父代2:110|010
子代1:111|010
子代2:110|100
两点交叉交换中间片段。
均匀交叉对每个基因独立决定来自哪个父代。
实数编码可以使用算术交叉:
排列编码必须使用专用交叉,例如:
- 顺序交叉 OX;
- 部分映射交叉 PMX;
- 循环交叉 CX。
普通单点交叉可能导致元素重复和缺失。
5.7 变异操作
二进制变异:
实数变异可以添加随机扰动:
其中:
排列变异可以使用交换、插入或逆序。
变异不是为了每次都改进解,而是为了引入种群中没有的新信息。
如果某个基因值已经从整个种群中消失,仅靠交叉无法重新产生它,变异可以恢复这种可能性。
5.8 精英保留
精英保留将当前最好个体直接复制到下一代。
这样通常可以保证:
但精英数量不能太多,否则种群会迅速同质化。
5.9 过早收敛
如果种群变成:
110101
110101
110101
110101
110101
交叉几乎无法产生新结构,算法只能依赖少量变异继续搜索。
解决方法包括:
- 增大种群;
- 降低选择压力;
- 减少精英数量;
- 提高变异率;
- 定期加入随机个体;
- 使用多种群;
- 根据多样性自适应调整参数;
- 与局部搜索结合。
5.10 核心流程
设计染色体编码
初始化种群 population
计算所有个体适应度
重复:
保存少量精英
while 新种群未满:
选择两个父代
按概率执行交叉
对子代执行变异
修复非法解或计算惩罚
将子代加入新种群
加入精英
用新种群替换旧种群
重新计算适应度
直到满足停止条件
输出历史最优个体
5.11 适用问题与局限
遗传算法适合:
- 背包和选择问题;
- 排列和路径问题;
- 任务分配;
- 连续参数优化;
- 混合变量优化;
- 特征选择;
- 规则和程序结构优化。
优点是通用、易并行、能维护多个搜索区域。
局限是函数评价次数多、参数较多、结果有随机性、编码和算子设计需要问题知识。
六、粒子群算法
6.1 核心思想
粒子群算法同时维护多个粒子。每个粒子代表一个候选解,并拥有位置和速度。
粒子根据三种信息更新运动:
- 保留上一轮的运动趋势;
- 靠近自己的历史最好位置;
- 靠近群体发现的最好位置。
速度更新公式为:
位置更新公式为:
其中:
- \(\mathbf{x}_i\):当前位置;
- \(\mathbf{v}_i\):速度;
- \(\mathbf{p}_i\):个人历史最优位置;
- \(\mathbf{g}\):群体历史最优位置;
- \(\omega\):惯性权重;
- \(c_1\):个体学习因子;
- \(c_2\):社会学习因子;
- \(r_1,r_2\):区间 \([0,1]\) 上的随机数。
6.2 三个速度分量
惯性项:
它让粒子继续沿原来的方向运动。惯性较大时,搜索范围更广;惯性较小时,搜索更稳定。
个体认知项:
它让粒子回忆自己的成功经验。
社会项:
它让粒子学习群体中目前最成功的位置。
PSO 的核心就是:
保持原方向 + 学习自己的经验 + 学习群体的经验
6.3 一个简单数值例子
求:
某粒子当前状态:
个人最优:
群体最优:
设置:
随机数:
则:
得到:
新位置:
目标值从:
下降到:
6.4 参数作用
惯性权重 \(\omega\):
- 大:探索能力强;
- 小:局部搜索能力强。
常使用线性递减惯性:
例如从 \(0.9\) 逐渐减小到 \(0.4\)。
\(c_1\) 较大时,粒子更相信自己的经验;\(c_2\) 较大时,粒子更容易追随群体。
常见初始设置:
或接近 2 的数值。
6.5 速度限制与边界处理
为了防止粒子跨越搜索区域,可以限制:
位置越界时,可以:
- 截断到边界;
- 反弹速度;
- 重新随机采样;
- 使用周期映射。
截断简单,但可能导致大量粒子堆积在边界。
6.6 全局拓扑与局部拓扑
gbest 模型中,所有粒子参考同一个全局最优位置,收敛快,但容易早熟。
lbest 模型中,每个粒子只参考邻域最好位置,信息传播较慢,但多样性通常更好。
6.7 核心流程
随机初始化粒子位置和速度
计算目标函数
设置每个粒子的个人最优
找到群体全局最优
重复:
对每个粒子:
更新速度
限制速度
更新位置
处理越界
计算目标函数
更新个人最优
更新群体最优
直到达到停止条件
输出群体最优位置
6.8 适用问题与局限
标准 PSO 天然适合连续参数优化,例如:
- 控制器参数;
- 工程设计变量;
- 模型超参数;
- 连续资源分配;
- 黑盒函数优化。
优点:
- 结构简单;
- 参数较少;
- 前期收敛快;
- 易于并行;
- 不需要梯度。
局限:
- 容易过早聚集;
- 后期可能停滞;
- 对变量尺度敏感;
- 离散问题需要重新设计;
- 不能保证全局最优。
七、蚁群算法
7.1 核心思想
蚁群算法通过信息素实现群体间接协作。
每只蚂蚁根据当前信息素和启发式信息逐步构造一个完整解。优秀解中的决策元素获得更多信息素,使后续蚂蚁更倾向于选择这些元素。
以旅行商问题为例,蚂蚁从城市 \(i\) 选择城市 \(j\) 的概率通常为:
其中:
- \(\tau_{ij}\):边 \((i,j)\) 上的信息素;
- \(\eta_{ij}\):启发式信息;
- \(A_i\):当前允许访问的城市集合;
- \(\alpha\):信息素重要程度;
- \(\beta\):启发式信息重要程度。
旅行商问题中常使用:
距离越短,启发值越大。
7.2 信息素更新
完成一轮构造后,信息素通常进行挥发和增强:
其中 \(\rho\) 是挥发率。
挥发的作用是:
- 减弱过时经验;
- 防止信息素无限增长;
- 保持探索能力;
- 降低早期错误路径的长期影响。
优秀路径可以增加更多信息素,例如:
其中 \(L\) 是路线长度,路线越短,增量越大。
7.3 正反馈机制
蚁群算法的核心是正反馈:
较好的路径
→ 获得更多信息素
→ 后续蚂蚁更容易选择
→ 更多蚂蚁构造相似好路径
→ 信息素进一步增强
正反馈能快速积累优秀经验,但也可能导致所有蚂蚁过早集中在一条次优路径上。
因此必须使用信息素挥发、随机选择和信息素上下限维持多样性。
7.4 构造式搜索
蚁群算法与粒子群不同。
粒子群中的每个粒子本身就是一个完整解,并在搜索空间中移动。
蚁群中的蚂蚁通常从空解开始,一步一步选择决策元素,直到构造出完整方案。
例如旅行商问题:
起点 1
→ 选择城市 4
→ 选择城市 2
→ 选择城市 5
→ 选择城市 3
→ 返回起点
因此,蚁群算法特别适合可以逐步构造解的问题。
7.5 核心流程
初始化所有信息素
重复:
对每只蚂蚁:
从初始状态出发
根据概率规则逐步选择决策
构造完整解
计算解的质量
所有信息素执行挥发
根据优秀解增加信息素
更新历史最好解
直到达到停止条件
输出历史最好解
7.6 关键参数
\(\alpha\) 控制信息素的重要程度。过大时,算法过度依赖历史路径。
\(\beta\) 控制启发式信息的重要程度。过大时,算法可能接近贪心策略。
\(\rho\) 控制挥发速度。挥发太慢容易早熟,挥发太快则经验难以积累。
蚂蚁数量影响每轮搜索覆盖范围和计算量。
7.7 适用问题与局限
蚁群算法适合:
- 旅行商问题;
- 车辆路径规划;
- 网络路由;
- 作业调度;
- 排序;
- 组合构造问题。
优点:
- 适合图和路径;
- 能利用启发式信息;
- 群体并行;
- 信息素具有长期记忆;
- 容易与局部搜索结合。
局限:
- 前期收敛可能较慢;
- 参数之间相互影响;
- 信息素容易过早集中;
- 连续问题需要专门改造;
- 大规模问题的信息素矩阵可能占用较多资源。
八、禁忌搜索
8.1 核心思想
禁忌搜索也是单解局部搜索,但它增加了记忆机制。
普通局部搜索容易在几个解之间循环。例如:
A → B → A → B
禁忌搜索记录近期执行过的动作,在一段时间内禁止反向操作,从而迫使搜索进入新的区域。
它通常允许当前解变差,但不会随机接受,而是选择当前允许候选中最好的一个。
8.2 禁忌对象
禁忌表可以记录:
- 最近访问过的解;
- 最近执行的交换;
- 最近改变的变量;
- 最近添加或删除的边;
- 某种解属性。
直接记录完整解可能占用较多内存,因此实践中常记录“动作”或“属性”。
例如旅行商问题中,刚刚交换了城市 2 和城市 5,可以暂时禁止立即把它们交换回来。
8.3 禁忌期限
禁忌期限表示一个动作被禁止多少轮。
期限太短,算法容易循环;期限太长,又会禁止过多有价值的移动。
禁忌期限可以固定,也可以在一定范围内随机变化。
8.4 特赦准则
禁忌不是绝对禁止。
如果某个禁忌动作能产生比历史最优解更好的结果,可以允许它执行。这称为特赦准则。
最常见的特赦条件是:
对于最小化问题,只要候选解优于历史最优,即使动作处于禁忌状态,也可以接受。
8.5 核心流程
生成初始解 current
best = current
初始化禁忌表
重复:
生成 current 的多个邻域解
对每个候选解:
判断对应动作是否被禁忌
如果被禁忌,检查是否满足特赦条件
从允许候选中选择最好的一个
current = 该候选解
如果 current 优于 best:
best = current
将本次动作加入禁忌表
更新禁忌期限
删除过期记录
直到达到停止条件
输出 best
8.6 强化与多样化
禁忌搜索不仅可以使用短期记忆,还可以使用长期记忆。
强化策略让搜索更多关注历史上表现较好的区域。
多样化策略鼓励搜索较少访问的区域,例如惩罚经常出现的边或变量组合。
8.7 适用问题与局限
适合:
- 生产调度;
- 车辆路径;
- 排课;
- 任务分配;
- 设施选址;
- 组合优化。
优点:
- 能主动避免循环;
- 允许移动到较差解;
- 可结合长期记忆;
- 对组合问题效果常较稳定。
局限:
- 禁忌对象和期限需要设计;
- 每轮通常要评估多个邻域解;
- 大邻域下计算量较大;
- 缺少随机性时可能搜索范围有限。
九、变邻域搜索
9.1 核心思想
一个解可能在某种邻域下是局部最优,但在另一种邻域下仍有改进空间。
例如一条路线通过交换任意两个城市都无法继续缩短,但通过反转一段路线可能得到明显改进。
变邻域搜索不固定使用一个邻域,而是在多个邻域之间系统切换。
9.2 邻域集合
以旅行商问题为例,可以设计:
- \(N_1\):交换两个城市;
- \(N_2\):插入一个城市;
- \(N_3\):反转一段路线;
- \(N_4\):2-opt;
- \(N_5\):3-opt。
邻域通常从小到大排列。
小邻域适合精细优化,大邻域适合结构性跳跃。
9.3 扰动与局部搜索
经典变邻域搜索通常包含两个阶段。
扰动阶段:从当前解的第 \(k\) 个邻域中随机产生一个新解。
局部搜索阶段:从新解出发执行局部搜索,直到达到局部最优。
如果得到更好的解,就接受并重新从第一个邻域开始;否则尝试下一个邻域。
9.4 核心流程
准备邻域 N1, N2, ..., Nk
生成初始解 current
重复:
neighborhood_index = 1
while neighborhood_index <= k:
candidate = 在对应邻域中扰动 current
candidate = 对 candidate 执行局部搜索
如果 candidate 优于 current:
current = candidate
neighborhood_index = 1
否则:
neighborhood_index += 1
直到满足停止条件
输出 current
9.5 为什么有效
局部最优是相对于邻域定义的。
如果:
只能说明 \(x\) 在 \(N_1\) 下局部最优,不能说明它在 \(N_2\)、\(N_3\) 下也局部最优。
切换邻域相当于改变了“附近”的定义,因此能够突破单一邻域的限制。
9.6 适用问题与局限
适合:
- 路线规划;
- 调度;
- 设施选址;
- 分配问题;
- 聚类;
- 组合优化。
优点:
- 思想直观;
- 易结合问题知识;
- 通常比单一邻域稳定;
- 容易与其他算法混合。
局限:
- 需要设计多个高质量邻域;
- 邻域顺序会影响效果;
- 大邻域可能计算昂贵;
- 如果所有邻域都过于相似,改进有限。
十、七种算法的本质对比
| 算法 | 搜索单位 | 新解产生方式 | 主要记忆 | 跳出局部最优方式 | 典型问题 |
|---|---|---|---|---|---|
| 局部搜索 | 单个解 | 邻域移动 | 当前解 | 通常没有 | 快速局部改进 |
| 模拟退火 | 单个解 | 随机邻域 | 当前解、历史最优 | 概率接受较差解 | 连续、排列、调度 |
| 遗传算法 | 种群 | 选择、交叉、变异 | 种群和精英 | 多样性、变异 | 通用、混合变量 |
| 粒子群 | 粒子群 | 更新速度和位置 | pbest、gbest | 惯性和随机项 | 连续参数优化 |
| 蚁群算法 | 多只构造个体 | 概率构造完整解 | 信息素 | 挥发和随机选路 | 路径、排序、组合 |
| 禁忌搜索 | 单个解 | 邻域移动 | 禁忌表 | 禁止返回、允许劣化 | 路径、调度、分配 |
| 变邻域搜索 | 单个解 | 多种邻域切换 | 当前解 | 改变邻域结构 | 组合优化 |
十一、同一个问题为什么可以用多种算法
以旅行商问题为例:
- 局部搜索:不断做 2-opt;
- 模拟退火:对路线交换或逆序,并概率接受更差路线;
- 遗传算法:把路线作为排列染色体,执行顺序交叉和逆序变异;
- 蚁群算法:根据边的信息素逐步构造路线;
- 禁忌搜索:选择最好的交换动作,并禁止立即撤销;
- 变邻域搜索:在交换、插入、逆序和 2-opt 之间切换;
- 粒子群:需要设计离散速度或交换序列,通常不如前几种自然。
同一个问题能用多种算法,不代表它们效果一样。真正决定效果的是:
- 解的表示是否自然;
- 新解操作是否保留问题结构;
- 约束处理是否合理;
- 搜索参数是否匹配;
- 是否结合了有效的局部搜索;
- 函数评价成本;
- 问题规模和实例特征。
十二、怎样根据问题选择算法
12.1 连续参数优化
优先考虑:
- 粒子群;
- 模拟退火;
- 实数遗传算法;
- 差分进化;
- 进化策略。
如果目标函数平滑且可导,应优先考虑梯度法,而不是直接使用启发式算法。
12.2 二进制选择问题
例如背包、特征选择和开关配置,优先考虑:
- 二进制遗传算法;
- 模拟退火;
- 禁忌搜索;
- 二进制粒子群;
- 贪心加局部搜索。
12.3 排列与路径问题
优先考虑:
- 蚁群算法;
- 模拟退火;
- 禁忌搜索;
- 变邻域搜索;
- 排列遗传算法;
- 2-opt、3-opt。
12.4 复杂调度问题
常用:
- 遗传算法;
- 禁忌搜索;
- 变邻域搜索;
- 模拟退火;
- 混合算法。
12.5 混合变量问题
如果问题同时包含是否选择、机器编号、连续参数和顺序,可以考虑:
- 混合编码遗传算法;
- 分层优化;
- 不同算法分别负责不同变量;
- 大邻域搜索;
- 混合元启发式。
十三、约束条件的通用处理方法
实际问题往往有约束:
以及:
常见方法如下。
13.1 惩罚函数
对于最小化问题:
其中 \(V(x)\) 表示违反约束的程度。
优点是实现简单,缺点是惩罚系数难以设置。
13.2 修复方法
生成非法解后,把它修复成合法解。
例如背包超重时,不断移除价值重量比较低的物品。
13.3 可行编码
从编码或邻域设计上保证所有候选解天然满足约束。
例如旅行商问题使用排列编码,并使用保持排列合法的交叉与变异。
13.4 可行解优先
比较两个解时:
- 可行解优于不可行解;
- 两个都可行时,目标值更好的获胜;
- 两个都不可行时,违反约束更少的获胜。
这种方式可以减少对惩罚系数的依赖。
十四、停止条件与实验设计
常见停止条件:
- 达到最大迭代次数;
- 达到最大函数评价次数;
- 运行时间达到上限;
- 历史最优连续若干代没有改善;
- 种群多样性低于阈值;
- 找到满足目标要求的解。
对于随机算法,不能只运行一次。
应使用多个随机种子独立运行,并记录:
- 最好值;
- 最差值;
- 平均值;
- 标准差;
- 中位数;
- 运行时间;
- 函数评价次数;
- 达到目标质量的成功率。
比较算法时,应保证相同的计算预算,例如相同函数评价次数,而不是只比较迭代次数。
十五、常见错误
15.1 只关注算法名称
很多初学者花大量时间比较“鲸鱼”“灰狼”“萤火虫”等名称,却忽略了解表示和搜索算子。
应先判断算法如何产生新解、如何记录经验、如何维持多样性。
15.2 不设置基线
至少应与以下方法比较:
- 随机搜索;
- 贪心算法;
- 简单局部搜索;
- 问题专用算法。
如果复杂算法不能明显超过简单基线,就需要重新检查设计。
15.3 只报告一次最好结果
随机算法偶尔找到好解并不代表稳定有效。应报告多次实验统计结果。
15.4 参数调得太多
参数越多,越容易在测试集上过度调优。应先使用合理默认值,再进行系统实验。
15.5 忽略变量尺度
连续变量范围差异很大时,应考虑归一化,否则某些变量可能主导搜索。
15.6 邻域或编码不合法
排列问题使用普通交叉,可能产生重复元素;约束问题不修复非法解,可能浪费大量计算。
15.7 当前解与历史最优混淆
模拟退火、粒子群和其他算法中,当前状态与历史最好状态必须分开维护。
十六、混合算法为什么常常更好
单一算法通常有明显偏向。
遗传算法擅长全局组合,但后期局部精细搜索可能较弱。
局部搜索收敛快,但容易陷入局部最优。
因此可以组合:
遗传算法负责全局探索
+
局部搜索负责精细改进
这种方法常称为模因算法。
其他常见组合:
- 粒子群 + 局部搜索;
- 蚁群 + 2-opt;
- 模拟退火 + 变邻域;
- 禁忌搜索 + 大邻域;
- 遗传算法优化蚁群参数;
- 蚁群构造路线,粒子群优化连续参数。
混合算法不是简单叠加多个名称,而是让不同模块承担互补职责。
十七、推荐学习顺序
推荐顺序:
局部搜索
→ 模拟退火
→ 遗传算法
→ 粒子群算法
→ 蚁群算法
→ 禁忌搜索
→ 变邻域搜索
局部搜索帮助理解邻域和局部最优。
模拟退火帮助理解为什么需要接受较差解。
遗传算法帮助理解种群、编码、交叉、变异和多样性。
粒子群帮助理解个人经验与群体经验。
蚁群帮助理解构造式搜索和间接通信。
禁忌搜索帮助理解记忆机制。
变邻域搜索帮助理解邻域定义本身就是搜索策略。
十八、推荐练习路线
18.1 背包问题
可以练习:
- 二进制编码;
- 适应度函数;
- 约束惩罚;
- 遗传算法;
- 模拟退火;
- 禁忌搜索。
18.2 旅行商问题
可以练习:
- 排列编码;
- 交换、插入和逆序邻域;
- 2-opt;
- 模拟退火;
- 遗传算法;
- 蚁群算法;
- 禁忌搜索;
- 变邻域搜索。
18.3 多峰连续函数
可以练习:
- 粒子群;
- 模拟退火;
- 实数遗传算法;
- 探索与利用;
- 参数敏感性;
- 多次运行统计。
常见测试函数包括 Sphere、Rastrigin、Rosenbrock 和 Ackley。
十九、学习每种算法时应该回答的问题
学习一种算法时,建议依次回答:
- 一个解如何表示?
- 算法维护一个解还是多个解?
- 新解怎样产生?
- 什么信息会被保留?
- 如何利用历史好解?
- 如何探索新区域?
- 如何跳出局部最优?
- 如何处理边界和约束?
- 关键参数分别控制什么?
- 如何判断算法是否早熟或停滞?
- 应该与什么基线比较?
- 算法最适合哪类问题?
- 在什么情况下不应该使用它?
如果这些问题能够清楚回答,就不仅是记住了算法流程,而是真正理解了算法。
二十、最终总结
启发式算法的共同目标是在无法穷举或精确求解的情况下,以有限计算资源寻找较好的近似解。
局部搜索从邻域中寻找改进,是很多算法的基础。
模拟退火通过温度控制接受较差解的概率,从而跳出局部最优。
遗传算法通过选择、交叉和变异,让种群中的优秀信息不断被保留、重组和创新。
粒子群通过惯性、个人最优和群体最优共同更新粒子运动,特别适合连续参数优化。
蚁群算法通过信息素和概率构造解,特别适合路径和组合问题。
禁忌搜索利用记忆机制避免循环,并允许搜索经过暂时较差的区域。
变邻域搜索通过系统切换邻域,突破单一邻域下的局部最优。
这些算法表面形式不同,但真正的核心始终是:
怎样表示解、怎样产生新解、怎样保存经验、怎样平衡探索与利用。
在实际问题中,算法名称通常不是最重要的。更重要的是问题建模、编码方式、邻域设计、约束处理和实验验证。一个简单算法配合优秀的问题设计,往往比一个复杂算法直接套用更有效。
未经作者同意请勿转载
本文来自博客园作者:aixueforever,原文链接:https://www.cnblogs.com/aslanvon/p/22188187

浙公网安备 33010602011771号