🔨优化

type
status
date
slug
summary
tags
category
icon
password

引入

什么是优化

优化(Optimization)是指在给定限制条件或满足一定约束的情况下,从所有可能选择中找出最好的那个,或通过调整决策变量使某个目标函数达到最大值或最小值的过程。例如:
  • 路径规划:从城市 应选择怎样的路线才能时间最短或者油耗最小?
  • 资源分配:在有限预算下如何投资才能获得最大的回报?
  • 产品设计:在满足设计容积的前提下,如何设计容器才能最省材料?
  • 生产计划:如何安排生产流程才能降低成本或提高利润?
这些都属于优化问题,需要在众多可能解中寻找最优解。

目标函数、约束与解空间

  • 目标函数(Objective function):我们想要最大化或最小化的函数,可以是利润、距离、成本等。
  • 约束(Constraints):限制解的可行性,如预算、时间、资源等限制。
  • 解空间(State space):所有可行解的集合,也称为状态空间。在优化中,每个状态对应一个解,其“高度”或“深度”对应目标函数的值。

0. 局部搜索

在大多数人工智能问题中,解空间规模巨大,无法穷举所有可能的解。局部搜索(Local Search)是一类从一个初始状态(节点/解)出发,通过在其邻域内移动、搜索、迭代,以寻找更优解的算法。
局部搜索不关心如何获得初始解,也不关心移动多少步;它只关注能否找到一个满意的解。
模拟退火这样的元启发式算法在理论上一定可以找到最优解,但是可能会花费很多时间。而局部搜索可以在节省计算资源的情况下,找到一个不是最佳、但是已经“足够好”的解决方案。

状态、邻居和目标函数

  • 当前状态(Current state):算法当前正在考虑的解。
  • 邻居状态(Neighbor state):通过合法操作从当前状态可以达到的状态。
  • 目标/代价函数:给每个状态分配一个数值,用于比较状态的优劣。 一般地,当我们想要最小化目标时称为代价函数;想要最大化目标时称为目标函数

全局最优与局部最优

  • 全局最大值/最小值(Global maximum/minimum):整个解空间中目标函数值最高或最低的状态。
  • 局部最大值/最小值(Local maximum/minimum):比所有邻居状态都更好但不一定是全局最优的状态。这些陷阱让局部搜索算法容易停留在非最优解上。

1. 爬山算法

爬山算法(Hill Climbing)是一种简单的局部搜索方法,适用于求解连续或离散优化问题。核心思想是:从当前状态出发,选择一个邻居状态,如果邻居比当前更好则移动过去;当没有更好的邻居时停止。对于最大化问题算法倾向“爬高”,对于最小化问题则“往低处走”。

算法步骤

  1. 选择一个 初始状态 作为当前状态 。在许多应用中,这个初始状态可随机生成。
  1. 重复以下步骤:
      • 找出当前状态的所有邻居状态
      • 在这些邻居中选择一个目标函数值更优的状态(对于最大化问题选择值更高的邻居,对于最小化问题选择值更低的邻居);
      • 如果选出的邻居比当前状态更好,就令其成为新的当前状态;否则,当前状态就是局部最优,算法结束。
伪代码如下:

示例:社区医院选址问题

假设某城市需要在地图上建两个社区医院,为居民区提供服务。我们用 表示地图上的坐标,
  • 状态:两个社区医院的放置位置集合。
  • 代价函数:所有居民区到最近医院的曼哈顿距离之和; 曼哈顿距离定义为
  • 邻居:移动某个医院到相邻的一个空格(不与其他医院或房屋重叠)。
初始状态随机生成两个医院位置。然后重复选择能降低总距离的邻居,直到无法改进为止。通过这种方法可能找到一个较优的选址,但算法会陷入局部最优。例如,图中展示的几个迭代中代价从 下降到 ;这已经比初始状态好,但仍不一定是全局最优。
Cost: 17
Cost: 17
Cost: 15
Cost: 15
Cost: 13
Cost: 13
Cost: 11
Cost: 11
Cost: 9
Cost: 9

示例:旅行商问题的建模

notion image
Answered by ChatGPT 5 Thinking ① 状态(state)是什么?
一次完整的投递顺序(路径),把 五个点各访问一次。
为了避免“等价的旋转”造成重复,常把起点固定(例如以 开始);是否需要回到起点视任务而定:
  • 开线路径(只派送,不回起点):序列如
  • 环线路径(巡回):序列如
② 代价函数(cost function)是什么?
总行程长度(或总耗时)。设 是访问顺序, 为已知的两点最短路径距离:
  • 开线:
    • 环线:
      • 局部搜索/爬山算法目标是最小化该代价。
    ③ 相邻状态(neighbor)是什么?
    在当前顺序上做一次“小改动”得到的新顺序。可选但等价的邻域定义很多,任选其一即可保持“合法路径”:
    • 交换(swap):交换任意两个点的位置(例如 )。
    • 插入(insert):把某一点取出,插入到另一个位置(如 )。
    • 2-opt:选两条边,断开后反转中间段并重连,常用于旅行商问题。

    小提示(有助于实现):
    • 固定起点 A 后,合法顺序的数量是 (开线);若按环线并把“逆时针/顺时针”和“起点平移”视为同一条路线,则唯一巡回路线数为
    • 由于题目给的是“每两点之间的最短路径”, 满足三角不等式;在这种图上,2-opt 这类邻域往往收敛更快、效果更好。
    • 若用爬山算法:从一个随机顺序出发,枚举(或采样)邻域,选择代价更小的邻居前进,直到没有更优邻居为止。

    代码实现:使用爬山算法解决社区医院选址问题

    1. 将问题抽象(让计算机明白选址问题)
      1. 模拟地图数据以测试算法效果
      2. 地点用 表示
      3. 设施类型:hospitalhouses
    1. 按照解优化问题的步骤编写代码
    1. 输出结果
    notion image
    题目:实现 hill_climb() 函数 (W3-Optimization-E4)
    找到放置2个社区医院的位置,是的每个房子到达最近医院的曼哈顿距离之和最小
    输出为2个社区医院的坐标几何,如 {(3, 2), (2, 7)}
    代码 from e03 import * 已经将练习#3中的 available_spaces, get_neighbors, get_cost 函数导入,并且已经放置了初始的 houses

    答案:实现 hill_climb() 函数 (W3-Optimization-E4)

    潜在问题

    尽管简单高效,爬山算法存在下列缺陷,并不能总是找到全局最优解:
    notion image
    notion image
    notion image
    • 局限于局部最优:算法容易停留在比邻居状态更好但不一定是全局最优的状态。
    • 平坦高原局部极值(Flat local maximum/minimum):若一大片邻居状态具有相同的目标值,算法无法判断前进方向,可能随机停滞。
    • 肩部(Shoulder):部分邻居更好、部分更差,算法可能在局部高地上徘徊。

    变种

    为了改善上述缺陷,人们提出了多种改进的爬山算法变种:
    • 最抖爬山(Steepest-ascent hill climbing):在所有邻居中选择最优的那个,确保每一步提升最大。
    • 随机爬山(Stochastic hill climbing):从比当前状态更好的邻居中随机挑选一个,增加多样性。
    • 首选爬山(First‑choice hill climbing):按某个顺序遍历邻居,遇到一个更好就立即跳过去,不用评估全部邻居。
    • 随机重启爬山(Random‑restart hill climbing):重复多次运行爬山算法,每次从新初始状态开始,最后取最优结果。此方法常能跳出局部最优,但计算开销较大。

    代码实现:使用随机重启爬山算法找到最佳选址地点

    notion image

    2. 梯度下降法

    在连续优化问题中,状态可以用实值向量 表示,我们希望最小化某个可微函数

    梯度的概念

    梯度是函数沿各坐标轴方向的偏导数组成的向量:
    梯度矢量指向函数值增长最快的方向;其反方向 则是下降最快的方向。在单变量情况下,梯度就是导数。例如函数 的导数为 ,在 处导数为

    梯度下降算法

    梯度下降(Gradient Descent)是一种迭代式优化算法,用来最小化可微函数。
    1. 随机初始化:选择一个随机的初始向量
    1. 迭代更新
      1. 其中 学习率(Learning rate)或步长,控制每次移动的尺度。
    1. 停止条件:当梯度的范数足够小(接近零),或达到最大迭代次数时,认为已经到达局部最小值并停止。
    选择合适的步长非常重要:太大可能越过最优解甚至发散,太小则收敛缓慢。在实际应用中,可采用固定步长或在优化过程中动态调整。

    示例:单变量函数最小化

    对函数 ,其导数为 。初始状态取 ,步长
    1. 计算梯度
    1. 更新
    1. 重复上述步骤,直到导数接近零(退出条件常设置为 ,或固定循环次数)。
    这一过程与爬山算法类似,但利用梯度提供的方向信息比枚举邻居更高效。

    代码实现:构建梯度下降算法

    notion image
    题目:实现 gradientgradient_descent (W3-Optimization-E6)

    任务1

    完成 gradient(f,x,h=1e-5) 函数
    • 功能:求函数 f(x)x 的梯度
      • 采用导数的定义: 
      • ℎ=0.00001
      • 你可以默认函数 f 为二元函数,有两个输入 x1,x2
    • 输出:np.array 格式的梯度

    任务2

    根据梯度下降伪代码完成gradient_descent函数
    其中,𝜖=1e-6, 𝛼=0.001

    多维示例:Rosenbrock 函数

    Rosenbrock 函数是一种经典的测试函数,形状像香蕉,定义为:
    其全局最小值为 ,梯度下降可以找到这一点。课堂演示代码中通过数值计算偏导,使用学习率 控制更新步长,反复迭代直到梯度范数小于某个阈值。

    3. 模拟退火算法

    爬山算法只能接受更好的邻居,因此容易困在局部最优。模拟退火(Simulated annealing, SA)通过引入“温度”参数,允许偶尔接受更差的状态,从而有概率跳出局部最优,随着温度降低逐渐转向精细搜索。
    金属材料加热后,内部粒子处于高能量状态;缓慢降温时,粒子逐渐排列成低能量的稳定结构。模拟退火算法借鉴这一思想:
    • 高温阶段:算法更容易接受比当前状态更差的邻居(随机性大),可以探索广泛区域。
    • 低温阶段:算法倾向于接受更好的邻居,随机性降低,更像爬山算法。

    算法步骤

    1. 初始化:从一个初始状态 开始,设定初始温度
    1. 迭代直到温度降低到终止条件
        • 从当前状态的领域解中随机选一个
        • 计算目标函数差值 对于最小化问题, 表示邻居更好。(能量较小的状态优于能量较大的状态。)
          • 如果邻居更优(),则接受它作为新的当前状态。
          • 如果邻居更差(),则以概率 接受更差的状态,其中 是当前温度。
        • 设置全局随时间变化的参数
          • 随时间递减温度,常见的衰减函数是
    1. 输出:返回当前的最终状态作为近似全局最优解。

    示例:模拟退火算法

    notion image
    1. 找不到全局最大,只会停在边界点 这一局部最大(值为 )。
      1. 很接近于 0,几乎不可能。
      2. 理论上,只有在“极慢”的冷却下,模拟退火才有以概率 1 收敛到全局最优的保证。

    代码实现:模拟退火算法

    notion image
    题目:实现 simulated_annealing(maximum) (W3-Optimization-E8)
    找到放置2个社区医院的位置,是的每个房子到达最近医院的曼哈顿距离之和最小
    输出为坐标几何,如 {(3, 2), (2, 7)}
    Temperature(t)为剩余时间的比例
    注意:我们想找最小值,所以需要正确计算ΔE
    代码 from e03 import * 已经将练习 #3 中的 available_spaces, get_neighbors, get_cost函数导入,并且已经放置了初始的 houses
    答案:实现 simulated_annealing(maximum) (W3-Optimization-E8)

    与爬山算法的比较

    • 爬山算法始终选择更好的邻居;模拟退火在高温阶段允许“退步”以避免局部最优。
    • 在理论上,如果温度下降得足够慢且随机性充分,模拟退火能够以概率 1 找到全局最优解。但实际问题中需要在计算时间与精度之间权衡。

    应用

    模拟退火和局部搜索在很多领域有重要应用,如:
    • 设施选址与城市规划:解决选址问题、物流中心定位等。
    • 数据分析与机器学习:用于特征选择和模型参数调优。
    • 组合优化:解决旅行商问题、排课表、投资组合等复杂问题。

    编程实践

    课堂的 Jupyter Notebook 中提供了社区选址问题的代码实现,下面总结一些关键函数和经验:
    • 生成随机房屋和医院的位置:使用 random.seed() 固定随机性,便于复现。房屋位置通常保存在 houses 集合,医院位置保存在 hospitals 集合。
    • 寻找可放置医院的位置available_spaces() 函数遍历地图并排除已有房屋和医院的位置,返回可选坐标集合。它是爬山算法初始状态和邻居生成的基础。
    • 获取邻居get_neighbors(row, col) 返回指定坐标上下左右四个不越界、且没有房屋或医院的位置。
    • 计算代价函数get_cost(potential_hospitals) 遍历所有房屋,计算其到最近医院的曼哈顿距离并求和。若没有医院,则返回空字符串。
    • 深拷贝与浅拷贝:在生成邻居或更新状态时要注意复制集合/列表,否则直接修改会影响原状态。Python 的 .copy() 方法可以生成浅拷贝。
    • 随机重启和模拟退火实现:在随机重启中记录所有运行的最优结果,最后返回最优解;模拟退火中根据温度与差值计算接受概率。
    • 梯度计算:数值梯度可用导数定义近似,h 取较小数值如

      总结

      • 局部搜索(包括爬山算法)适用于解空间巨大而无法穷举的优化问题。通过迭代改进邻居,它可以快速找到一个“足够好”的解,但可能陷入局部最优。
      • 随机重启、随机爬山等改进提供了跳出局部最优的方法,但不能保证一定找到全局最优。
      • 梯度下降将搜索推广到连续空间,通过梯度提供的方向信息快速逼近极小点。学习率的选择与梯度计算的精确性影响收敛速度。
      • 模拟退火借鉴物理退火过程,引入温度控制搜索的随机性,在理论上可以找到全局最优,但实际应用中需平衡效率与精度。
      • 这些算法在设施选址、组合优化、机器学习等领域有广泛应用,学习它们可以为解决复杂决策问题提供有力工具。
      上一篇
      Python 常用速查表
      下一篇
      机器学习
      Loading...