尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

模拟退火算法:从物理退火到全局优化的启发式搜索实践

模拟退火算法:从物理退火到全局优化的启发式搜索实践 1. 项目概述从“炼丹”到“寻宝”的优化哲学如果你在数学建模或者优化问题的圈子里混过一段时间大概率听说过“模拟退火”这个名字。它不像梯度下降那样有严谨的数学推导也不像遗传算法那样有生动的生物隐喻它的名字听起来甚至有点“玄学”——模拟退火。我第一次接触它时脑子里浮现的是铁匠打铁或者玻璃工匠退火的画面心想这跟数学优化能扯上什么关系但恰恰是这种从物理世界“借用”来的智慧解决了一大批让传统优化方法头疼不已的难题。简单来说模拟退火算法是一种启发式随机搜索算法专门用来在庞大的、可能充满“坑洼”局部最优解的解空间中寻找那个全局的“宝藏”全局最优解或近似最优解。它的核心思想模仿了金属冶炼中的退火过程先将金属加热到高温使其原子活跃然后缓慢冷却原子逐渐趋向于能量最低的稳定排列。对应到优化问题就是允许算法在搜索初期以一定的概率接受“更差”的解从而有几率跳出局部最优的“小水坑”随着“温度”降低这种接受差解的概率越来越小算法最终稳定在一个较好的解附近。为什么数学建模者需要它因为现实世界的建模问题尤其是组合优化问题比如旅行商问题、背包问题、调度问题其解空间往往是指数级增长的并且结构复杂像一座连绵起伏的山脉有无数个山峰局部最优和山谷。传统的穷举法不现实梯度类方法又容易卡在第一个遇到的峰顶。模拟退火提供了一种“迂回”的策略我不强求每一步都往上爬有时候也往下走走看看山那边是不是有更高的山峰。这种“以退为进”的哲学正是其魅力所在。2. 算法核心思想与物理隐喻拆解理解模拟退火关键在于吃透它的物理隐喻和由此衍生的几个核心概念。这不是单纯的比喻而是算法每一步设计的根本依据。2.1 退火过程的物理映射我们先来拆解一下金属退火的实际过程加温固体金属被加热到足够高的温度粒子原子的动能增大排列从有序的晶体结构变为无序的液态。此时粒子可以相对自由地移动。等温在高温下保持一段时间确保系统达到热平衡状态此时粒子分布符合玻尔兹曼分布。冷却退火非常缓慢地降低温度。随着温度降低粒子动能减小逐渐趋向于能量更低的稳定状态。如果冷却得足够慢系统最终会达到基态能量最低状态形成完美的晶体。将这个过程映射到优化问题物理系统状态-优化问题的候选解。比如旅行商问题中一条特定的路径。状态的能量-解的目标函数值。对于最小化问题目标函数值越小能量越低解越好。温度 (T)-控制算法随机性的一个参数。高温对应高随机性算法活跃易于探索低温对应低随机性算法稳定倾向于局部精细搜索。退火进度-温度下降的调度表。即温度如何从初始高温T0逐步降低到接近零的终温T_end。这个映射的精妙之处在于它引入了一个关键机制基于概率的状态转移而不仅仅是贪心地选择更好的状态。2.2 Metropolis准则算法的心脏这是模拟退火从统计物理中借鉴的核心公式决定了算法何时接受一个新解。其接受概率P的公式为P { 1, 如果 ΔE 0 (新解更优) exp(-ΔE / (k * T)), 如果 ΔE 0 (新解更差) }其中ΔE E_new - E_old即新解与当前解的目标函数值之差对于最小化问题。k是玻尔兹曼常数在算法中通常被吸收到温度T中简化为P exp(-ΔE / T)。T是当前温度。这个公式意味着什么如果新解更优 (ΔE 0)百分之百接受。这是贪心策略保证向好的方向前进。如果新解更差 (ΔE 0)以概率P exp(-ΔE / T)接受这个更差的解。温度T很高时exp(-ΔE / T)接近 1即使ΔE很大接受差解的概率也很高。这对应退火初期的高温阶段算法几乎“随机游走”广泛探索解空间有能力跳出局部最优。温度T很低时exp(-ΔE / T)接近 0除非ΔE非常小即新解只比当前解差一点点否则很难接受差解。这对应退火末期的低温阶段算法行为类似局部搜索在好解附近进行微调。差值ΔE很大时exp(-ΔE / T)很小接受一个差很多的新解的概率极低。算法倾向于接受“稍微差一点”的解而不是“差很多”的解这符合直觉。实操心得很多初学者在这里会困惑k到底取多少在实际编程中我们几乎总是忽略这个常数或者将其设为1。因为温度T的绝对数值大小本身是由我们设定的初始温度T0决定的。T0和k共同决定了概率尺度所以我们只需要通过调整T0来达到理想的初始接受概率即可无需纠结k的具体物理值。2.3 为什么需要接受“更差”的解这是模拟退火区别于爬山算法等局部搜索方法的核心。想象你在一片多峰的山脉中寻找最高点全局最优。如果你采用“只上不下”的爬山策略那么从任何一个起点出发你都会爬到离你最近的那个山顶局部最优然后就卡在那里了无法知道远处的山峰是否更高。模拟退火在初期高温赋予了你“飞”的能力——以一定概率跳到更低的位置接受差解。这个“飞”的落点是随机的可能落到一个更深的谷底但也可能落到另一个更高山峰的山腰上从而为你攀登更高的顶峰创造了可能。随着时间推移温度降低“飞”的能力减弱接受差解概率降低你开始稳步攀登当前所在的山峰。通过这种方式算法大大增强了全局探索能力。3. 算法流程与关键参数详解掌握了核心思想我们来看一个标准模拟退火算法的伪代码流程并深入剖析每一个关键参数和步骤背后的考量。3.1 标准算法流程1. 初始化 - 初始温度 T T0 (足够高) - 初始解 S S0 (可以是随机生成的) - 当前最优解 S_best S - 外循环迭代计数器 k 0 2. while (T T_end) and (未达到其他停止准则): a. for i 1 to L: # 内循环在每个温度下进行L次状态转移尝试 i. 在当前解 S 附近通过某种“扰动”方式产生一个新解 S_new。 ii. 计算目标函数变化 ΔE f(S_new) - f(S)。 iii. 根据 Metropolis 准则判断是否接受 S_new - 如果 ΔE 0接受 S_new令 S S_new。 - 如果 ΔE 0计算 P exp(-ΔE / T)。生成一个 [0,1) 区间的随机数 r。 - 如果 r P接受 S_new令 S S_new否则拒绝S 保持不变。 iv. 如果 f(S) f(S_best)更新 S_best S。 b. 降温按照预定的降温策略更新温度 T例如 T α * T (0 α 1)。 c. k k 1 3. 输出最终找到的最优解 S_best。3.2 关键参数设计与调优经验算法的表现极大程度上依赖于以下几个参数的设置。这里没有“银弹”式的最优值只有基于问题和经验的调优。1. 初始温度T0作用决定算法初期的探索能力。T0越高初期接受差解的概率越大全局探索性越强。如何设置一个常用的经验方法是通过少量实验让算法在初始温度下对随机产生的新解的接受概率大约在 0.7~0.9 之间。可以写一个小程序随机生成一堆新解统计ΔE的均值avg(ΔE)然后根据exp(-avg(ΔE) / T0) ≈ 0.8反推出T0。我的经验对于目标函数值范围在几百到几千的常见问题T0从 100、1000 开始尝试是合理的起点。如果问题规模很大函数值波动也大可能需要设到 1e4 或更高。2. 降温系数α作用控制温度下降的速度是影响算法收敛速度和精度的关键。α越接近1降温越慢搜索越充分但耗时越长α越小降温越快可能收敛迅速但容易陷入局部最优。常用范围α通常在 [0.8, 0.99] 之间。0.95 是一个常用的保守起点。我的经验对于解空间特别复杂、局部最优很多的问题建议使用较大的α如 0.98, 0.99配合较长的内循环次数L进行“慢火细炖”。对于对时间敏感、或者问题相对简单的情况可以用较小的α如 0.85, 0.9加速。3. 内循环次数L作用在每个温度下进行足够次数的状态转移尝试以确保系统在该温度下达到“热平衡”。理论上L应该足够大使得解的概率分布接近当前温度下的玻尔兹曼分布。如何设置有几种常见策略固定次数简单直接设一个数如L 100或L 200。适用于问题规模中等的情况。与问题维度相关L 100 * n其中n是解变量的维度或问题规模。例如旅行商问题中n可以是城市数量。自适应调整更高级的策略。例如当连续多次尝试都被拒绝时可以认为在该温度下已趋于平衡提前结束内循环。我的经验从固定次数开始调试。如果发现算法在高温阶段很快就“稳定”了连续大量拒绝新解可能是L太小还没充分探索就降温了可以适当增大L。4. 终止条件温度条件T T_end。T_end是一个很小的正数如 1e-7, 1e-8。当温度低到这种程度接受差解的概率几乎为零算法已停止优化。迭代次数条件外循环达到最大迭代次数K_max。防止无限循环。解质量条件连续若干次外循环最优解S_best都没有任何改进。我的建议通常将温度条件作为主要终止条件同时设置一个较大的K_max作为安全阀。注意事项参数调优是一个“没有最好只有更好”的过程。强烈建议在正式运行前用一个小规模的问题实例或者简化模型进行参数敏感性测试观察不同参数下最优解的变化趋势和运行时间找到适合你当前问题的“甜点”区间。4. 邻域结构设计如何产生“新解”“扰动”或者说“邻域移动”操作是产生新解S_new的方式这是将模拟退火算法应用到具体问题时最需要你动脑筋、也最具问题特异性的部分。一个设计良好的邻域结构能极大提升算法的搜索效率。4.1 邻域设计的基本原则可达性通过有限步的邻域移动应该能够从解空间中的任何一个解到达任何另一个解。这保证了算法在理论上能够搜索到全局最优解。连通性解空间应该是连通的即从一个解出发通过一系列邻域移动可以遍历所有解。适度性移动的“步长”要合适。步长太大新解可能与当前解差异巨大类似于随机搜索失去了局部启发的意义步长太小搜索过程会变得非常缓慢容易陷入局部最优。通常邻域移动应该是对当前解的一个“微小扰动”。4.2 经典问题的邻域移动示例旅行商问题2-opt交换随机选择路径上不相邻的两条边 (i, i1) 和 (j, j1)将其删除然后重新连接为 (i, j) 和 (i1, j1)并反转中间路径的方向。这是TSP最经典、最有效的邻域操作之一。节点交换随机选择两个城市交换它们在路径中的位置。节点插入随机选择一个城市将其从当前位置取出插入到另一个随机位置。背包问题0-1规划位翻转随机选择一个物品改变其状态从放入背包变为不放入或反之。这是最基本的操作。交换随机选择一个已在背包中的物品和一个不在背包中的物品进行状态交换。贪心扰动以一定概率优先翻转那些单位价值高或重量占比特殊的物品。函数优化连续问题高斯扰动对于解向量X [x1, x2, ..., xn]新解X_new X σ * Z其中Z是一个服从标准正态分布N(0,1)的随机向量σ是控制步长的参数。温度T可以关联到σ实现步长自适应缩小。均匀扰动X_new[i] X[i] U(-δ, δ)其中U是均匀分布δ是扰动幅度。调度问题如车间调度关键路径操作在调度序列的关键路径上交换两个工序的顺序。随机插入将一个工序从当前位置移动到另一个随机位置。区块交换交换两个连续工序区块的位置。4.3 设计邻域结构的经验技巧混合多种邻域操作不要只使用一种移动方式。可以在内循环中以一定概率随机选择不同的邻域操作。例如70%的概率使用2-opt30%的概率使用节点插入。这能增加搜索的多样性。邻域大小与温度关联在高温时可以使用“大刀阔斧”式的移动如大范围的逆转进行广域探索在低温时切换到“精雕细琢”式的移动如相邻元素交换进行局部优化。这被称为“自适应邻域”。利用问题知识设计带有启发式信息的邻域。例如在TSP中优先交换那些距离很长的边在背包问题中优先考虑价值重量比高的物品。这能引导搜索向更有希望的方向进行但要注意不能破坏算法的随机性本质。效率优先邻域操作和随之而来的目标函数值计算是算法中最耗时的部分。设计时要考虑计算效率。例如2-opt操作后不需要重新计算整条路径的长度只需计算被改动边带来的长度变化即可这能极大加速。实操心得在数学建模竞赛中邻域结构的设计往往是拉开差距的地方。不要满足于实现最基本的随机扰动。花时间分析你的问题模型思考什么样的“微小改变”是合理的、高效的。一个好的邻域设计能让你的模拟退火算法效率提升数倍。5. 降温策略不止是指数衰减在基础流程中我们使用了T_{k1} α * T_k的指数降温也称为经典几何降温。这是最常用、最简单的方法。但实际上降温策略的选择也会影响算法性能。5.1 常见降温策略对比策略名称公式特点适用场景经典几何降温T_{k1} α * T_k(0α1)简单降温速度先快后慢。最常用。通用性强大多数问题可作为首选。线性降温T_{k1} T_k - β(β为常数)温度匀速下降。需要更精确控制冷却进度时。对数降温T_k T0 / log(1k)降温非常缓慢理论上能保证以概率1收敛到全局最优但速度极慢。理论研究或对解质量要求极高、不计时间成本的情况。自适应降温根据搜索过程动态调整。例如如果当前接受率太高则加快降温接受率太低则减缓甚至回升温度。智能化能根据搜索状态调整但实现复杂。高级应用当问题特性不明或参数难以手动调优时。5.2 基于接受率的自适应降温实践这里分享一个我常用的、简单有效的自适应降温思路。其核心思想是让算法在每个温度下的接受率维持在一个理想的范围内比如0.3~0.5。步骤设定一个目标接受率范围例如[accept_min, accept_max] [0.3, 0.5]。在每个外循环温度T_k结束时统计该温度下内循环中实际接受新解的次数占总尝试次数L的比例记为当前接受率accept_rate。根据accept_rate调整下一个温度如果accept_rate accept_max说明当前温度还是太高算法太“活跃”接受了很多差解。可以加大降温幅度例如T_{k1} T_k * α_fast其中α_fast较小如0.7。如果accept_rate accept_min说明当前温度可能过低算法太“保守”几乎只接受好解。可以减小降温幅度甚至短暂“回温”以跳出可能陷入的局部最优例如T_{k1} T_k * α_slow其中α_slow较大如0.98或者T_{k1} T_k * 1.05轻微回温。如果accept_rate在目标范围内按正常速度降温T_{k1} T_k * α_normal如0.9。这种方法能让算法在探索和利用之间取得更好的动态平衡尤其适合那些解空间结构未知的问题。6. 编程实现与代码框架以Python为例理论说了这么多我们来看一个具体的、可复用的Python实现框架。这里我们以一个经典的连续函数优化为例寻找Rastrigin函数的最小值。Rastrigin函数是一个多峰函数有大量局部极小点非常适合测试全局优化算法。import math import random import numpy as np def rastrigin(x): Rastrigin函数经典的多峰测试函数全局最小值在原点值为0。 A 10 n len(x) return A * n sum([(xi**2 - A * math.cos(2 * math.pi * xi)) for xi in x]) class SimulatedAnnealing: def __init__(self, func, bounds, T01000, T_end1e-7, alpha0.95, L200, max_iter1000): 初始化模拟退火算法。 :param func: 目标函数求最小值。 :param bounds: 每个变量的取值范围列表例如 [(lb1, ub1), (lb2, ub2), ...]。 :param T0: 初始温度。 :param T_end: 终止温度。 :param alpha: 降温系数。 :param L: 每个温度下的迭代次数马尔可夫链长度。 :param max_iter: 最大外循环迭代次数防止无限循环。 self.func func self.bounds bounds self.dim len(bounds) self.T0 T0 self.T_end T_end self.alpha alpha self.L L self.max_iter max_iter # 记录历史 self.best_solution_history [] self.best_value_history [] self.temperature_history [] def generate_initial_solution(self): 在边界内随机生成一个初始解。 return [random.uniform(low, high) for (low, high) in self.bounds] def generate_neighbor(self, current_solution, temperature): 生成一个邻域解。 采用高斯扰动扰动幅度与当前温度相关温度越高扰动越大。 # 扰动步长与温度平方根成正比这是一个经验性设置 scale 0.1 * math.sqrt(temperature) neighbor [] for i in range(self.dim): low, high self.bounds[i] # 在当前解的基础上添加高斯噪声 new_val current_solution[i] random.gauss(0, scale) # 处理边界溢出反射边界策略 if new_val low: new_val low (low - new_val) if new_val high: new_val low elif new_val high: new_val high - (new_val - high) if new_val low: new_val high neighbor.append(new_val) return neighbor def metropolis_accept(self, delta_e, temperature): Metropolis准则判断是否接受新解。 if delta_e 0: return True else: probability math.exp(-delta_e / temperature) return random.random() probability def solve(self): 执行模拟退火算法主流程。 # 初始化 current_solution self.generate_initial_solution() current_value self.func(current_solution) best_solution current_solution.copy() best_value current_value T self.T0 iteration 0 # 主循环 while T self.T_end and iteration self.max_iter: for _ in range(self.L): # 产生新解 new_solution self.generate_neighbor(current_solution, T) new_value self.func(new_solution) # 计算能量差 delta_e new_value - current_value # 判断是否接受新解 if self.metropolis_accept(delta_e, T): current_solution, current_value new_solution, new_value # 更新历史最优解 if new_value best_value: best_solution, best_value new_solution.copy(), new_value # 记录历史数据用于分析 self.best_solution_history.append(best_solution.copy()) self.best_value_history.append(best_value) self.temperature_history.append(T) # 降温 T * self.alpha iteration 1 # 可选打印进度 if iteration % 100 0: print(fIter {iteration}, T{T:.4e}, Best Value{best_value:.6f}) return best_solution, best_value, iteration # 使用示例优化2维Rastrigin函数搜索范围[-5.12, 5.12] if __name__ __main__: bounds [(-5.12, 5.12), (-5.12, 5.12)] sa SimulatedAnnealing(rastrigin, bounds, T0100, T_end1e-8, alpha0.99, L150, max_iter500) best_sol, best_val, iters sa.solve() print(f\n优化完成共迭代 {iters} 次。) print(f找到的最优解: {best_sol}) print(f对应的最优值: {best_val})代码关键点解析邻域生成 (generate_neighbor)这里采用了与温度相关的自适应高斯扰动。scale 0.1 * sqrt(T)意味着高温时扰动大探索广低温时扰动小精细搜索。同时使用了反射边界处理让超出边界的解“弹回”搜索域内比直接截断到边界更好。历史记录类属性best_solution_history等记录了每一代的最优解和温度便于后续绘制收敛曲线分析算法行为。参数设置对于Rastrigin这种震荡剧烈的函数我使用了相对较高的降温系数 (alpha0.99) 和较多的内循环次数 (L150)以确保充分搜索。初始温度T0100是经过简单测试后选择的。注意事项这个框架是通用的。要解决具体问题如TSP、背包你只需要做两件事1) 替换func为你自己的目标函数2) 重写generate_neighbor函数实现针对你问题特性的邻域移动操作。bounds参数对于离散问题可能不需要可以相应调整初始化部分。7. 实战调优与结果分析运行上面的代码你可能会得到接近0的最优值例如1.23e-5。但模拟退火是随机算法每次运行结果都会有波动。如何评估和提升算法性能7.1 收敛性分析通过绘制迭代过程中历史最优值和温度的变化曲线可以直观看到算法的收敛过程。import matplotlib.pyplot as plt # 假设已经运行了上面的 sa.solve()并保存了历史数据 plt.figure(figsize(12, 4)) # 子图1最优值随迭代次数的变化 plt.subplot(1, 2, 1) plt.plot(sa.best_value_history, b-, linewidth1) plt.xlabel(Iteration) plt.ylabel(Best Function Value) plt.title(Convergence Curve) plt.grid(True, linestyle--, alpha0.5) plt.yscale(log) # 对数坐标可以更清晰地看到后期的微小变化 # 子图2温度随迭代次数的变化 plt.subplot(1, 2, 2) plt.plot(sa.temperature_history, r-, linewidth1) plt.xlabel(Iteration) plt.ylabel(Temperature) plt.title(Temperature Schedule) plt.grid(True, linestyle--, alpha0.5) plt.yscale(log) plt.tight_layout() plt.show()理想的收敛曲线最优值曲线前期快速下降高温探索阶段中期波动中缓慢下降中温平衡阶段后期趋于平稳低温收敛阶段。温度曲线应平滑下降。7.2 性能评估与参数调优实战单次运行的结果具有偶然性。科学的做法是进行多次独立运行统计结果。def run_multiple_trials(trials30): 多次运行SA统计结果。 results [] for i in range(trials): sa SimulatedAnnealing(rastrigin, bounds, T0100, T_end1e-8, alpha0.99, L150, max_iter500) best_sol, best_val, iters sa.solve() results.append(best_val) print(fTrial {i1}: Best Value {best_val:.6f}) results np.array(results) print(f\n--- 统计结果 (共{trials}次) ---) print(f最优值: {results.min():.6e}) print(f最差值: {results.max():.6e}) print(f平均值: {results.mean():.6e}) print(f标准差: {results.std():.6e}) # 找到全局最优0的近似成功率 success_rate np.sum(results 1e-4) / trials * 100 print(f成功率 (1e-4): {success_rate:.1f}%) return results # 执行多次试验 results run_multiple_trials(30)通过分析多次运行的统计量均值、标准差、成功率你可以客观评估当前参数设置下算法的稳定性和可靠性。调优流程建议基准测试用一组默认参数如T0100, alpha0.95, L100运行多次记录结果。单变量分析固定其他参数系统性地改变一个参数如alpha从 0.8 到 0.99观察平均最优值和成功率的变化。找到该参数的敏感区间。组合调优在敏感区间内尝试不同的参数组合。由于参数间可能存在交互可以使用简单的网格搜索或随机搜索。验证用调优后的参数再次进行多次独立运行确认性能提升。7.3 常见问题与排查技巧实录在实际使用中你可能会遇到以下典型问题问题1算法收敛太快结果很差。可能原因1初始温度T0太低。排查查看初始几次迭代的接受率。如果从一开始接受率就低于0.1说明T0太低算法一开始就陷入了贪心搜索。解决增大T0直到初始接受率在0.7-0.9左右。可能原因2降温速度太快α太小。排查观察温度曲线是否在几十次迭代内就降到了极低值最优值曲线是否在早期就变平解决增大α如从0.9调到0.98让降温过程更平缓。可能原因3内循环次数L太少。排查在每个温度下是否只尝试了几次移动就降温了解决增加L确保在每个温度下进行了充分搜索。问题2算法运行时间太长且后期优化不明显。可能原因1T_end设置过小或max_iter过大。排查算法是否在温度已经极低如1e-10后还在运行此时接受差解概率几乎为0继续运行无意义。解决适当提高T_end如从1e-8调到1e-5或增加一个基于解质量停滞的终止条件。可能原因2邻域操作效率低下。排查目标函数计算或邻域生成是否非常耗时用性能分析工具如Python的cProfile定位瓶颈。解决优化目标函数和邻域操作的代码。对于TSP使用增量计算对于复杂问题考虑使用更高效的编程语言如C重写核心部分。问题3结果波动大不稳定。可能原因随机性太强搜索不够充分。排查多次运行的结果方差很大。解决这不是“问题”而是启发式算法的固有特性。为了获得稳定可靠的结果标准做法是独立运行算法多次如20-50次然后取最好的结果或者取多次结果的平均值/中位数作为最终报告的解。在数学建模论文中必须报告多次运行的统计结果以证明算法的鲁棒性。问题4总是找不到理论上的全局最优解。可能原因对于特别复杂的问题模拟退火不能保证100%找到全局最优。解决增加计算资源延长搜索时间增大max_iter,L使用更慢的降温策略。改进邻域结构设计更智能、导向性更强的邻域操作。混合其他策略考虑与其他算法结合如将模拟退火作为局部搜索器嵌入到遗传算法中或者先用模拟退火进行粗搜索再用梯度下降等进行精调。接受现实对于NP-hard问题找到“满意解”而非“最优解”往往是更实际的目标。只要你的解优于常规方法且逻辑自洽在建模竞赛中就是成功的。模拟退火算法将物理世界的退火过程抽象为一种强大的优化工具其核心魅力在于通过引入概率性的“劣化接受”来逃离局部最优。掌握它不在于死记硬背公式而在于理解其“探索-利用”的平衡哲学并能根据具体问题灵活设计邻域结构和调整参数。在数学建模中它常是解决组合优化、函数优化难题的“秘密武器”。多实践多调参多分析你就能逐渐摸清这门“炼丹术”的火候让它为你找到更优的解决方案。
返回列表