想象一下铁匠锻造宝剑的过程:先将金属加热至通红,然后缓慢冷却,金属内部的粒子会从无序状态逐渐排列成有序的晶体结构,最终形成强韧的宝剑。这一古老的工艺背后,隐藏着一种强大的数学优化思想——模拟退火算法。
模拟退火算法(Simulated Annealing, SA)是受固体退火过程启发的一种通用概率优化算法,由Kirkpatrick等人于1983年提出。它能够有效地在大规模搜索空间中寻找近似全局最优解,特别适用于传统方法难以处理的复杂优化问题。
算法核心思想:接受不完美,追求全局最优1. 物理过程的数学抽象在金属退火过程中,温度扮演着关键角色:
高温阶段:粒子活动剧烈,系统处于高能量状态缓慢冷却:粒子逐渐找到更低能量的排列方式低温状态:系统达到稳定的低能量状态模拟退火算法将这一过程抽象为三个核心要素:
目标函数(能量函数):需要最小化的函数温度参数:控制搜索过程的参数邻域搜索:生成新解的方式2. 核心机制:Metropolis准则算法的精髓在于以一定概率接受劣解,这使得算法能够跳出局部最优解。这一机制由Metropolis准则实现:
P = exp(-ΔE/T)
其中:
ΔE = 新解能量 - 当前解能量T = 当前温度P = 接受劣解的概率当ΔE < 0(新解更优)时,总是接受新解;当ΔE ≥ 0(新解更差)时,以概率P接受新解。
3. 温度调度:控制搜索过程温度T随时间逐渐降低的策略称为退火计划(Annealing Schedule),常见的有:
线性降温:T(t) = T₀ - α×t指数降温:T(t) = T₀ × αᵗ (0 < α < 1)对数降温:T(t) = T₀ / log(1+t)降温速度对算法性能至关重要:
过快降温:容易陷入局部最优过慢降温:计算成本高,收敛速度慢算法步骤详解初始化:随机生成初始解,设置初始温度T₀和终止温度T_min生成新解:在当前解的邻域内随机产生一个新解计算能量差:ΔE = E(新解) - E(当前解)Metropolis准则:如果ΔE < 0,接受新解作为当前解如果ΔE ≥ 0,以概率exp(-ΔE/T)接受新解降温:按照预定策略降低温度T终止判断:如果T < T_min,输出当前解;否则返回步骤2Python实现与可视化下面我们实现一个完整的模拟退火算法,解决Rastrigin函数优化问题(这是一个多峰函数,有大量局部极小值点)。
import numpy as npimport matplotlib.pyplot as pltimport mathimport randomfrom matplotlib.animation import FuncAnimationfrom mpl_toolkits.mplot3d import Axes3D# 设置中文字体plt.rcParams['font.sans-serif'] = ['SimHei']plt.rcParams['axes.unicode_minus'] = False# 定义目标函数(以Rastrigin函数为例,这是一个多峰函数)def rastrigin(x, y): """Rastrigin函数,用于测试优化算法""" return 20 + (x**2 - 10 * np.cos(2 * np.pi * x)) + (y**2 - 10 * np.cos(2 * np.pi * y))class SimulatedAnnealing: def __init__(self, objective_func, bounds, initial_temp=1000, cooling_rate=0.95, max_iter=1000): self.objective_func = objective_func self.bounds = bounds # 搜索边界 [(x_min, x_max), (y_min, y_max)] self.initial_temp = initial_temp self.cooling_rate = cooling_rate self.max_iter = max_iter self.history = [] # 记录搜索历史 def run(self): # 随机生成初始解 current_solution = np.array([random.uniform(self.bounds[0][0], self.bounds[0][1]), random.uniform(self.bounds[1][0], self.bounds[1][1])]) current_energy = self.objective_func(current_solution[0], current_solution[1]) best_solution = current_solution.copy() best_energy = current_energy temperature = self.initial_temp for i in range(self.max_iter): # 生成新解 new_solution = current_solution + np.random.randn(2) * temperature / self.initial_temp # 确保新解在边界内 new_solution[0] = np.clip(new_solution[0], self.bounds[0][0], self.bounds[0][1]) new_solution[1] = np.clip(new_solution[1], self.bounds[1][0], self.bounds[1][1]) new_energy = self.objective_func(new_solution[0], new_solution[1]) # 计算能量差 energy_delta = new_energy - current_energy # 决定是否接受新解 if energy_delta < 0 or random.random() < math.exp(-energy_delta / temperature): current_solution = new_solution current_energy = new_energy # 更新最优解 if current_energy < best_energy: best_solution = current_solution best_energy = current_energy # 降低温度 temperature *= self.cooling_rate # 记录历史 self.history.append({ 'iteration': i, 'temperature': temperature, 'current_solution': current_solution.copy(), 'current_energy': current_energy, 'best_solution': best_solution.copy(), 'best_energy': best_energy }) return best_solution, best_energy# 运行退火算法bounds = [(-5.12, 5.12), (-5.12, 5.12)] # Rastrigin函数的常用边界sa = SimulatedAnnealing(rastrigin, bounds, initial_temp=1000, cooling_rate=0.99, max_iter=2000)best_solution, best_energy = sa.run()print(f"最优解: x={best_solution[0]:.4f}, y={best_solution[1]:.4f}")print(f"最优值: {best_energy:.4f}")# 可视化结果def visualize_results(sa): # 创建图形 fig = plt.figure(figsize=(15, 10)) # 1. 3D曲面图 ax1 = fig.add_subplot(2, 2, 1, projection='3d') x = np.linspace(bounds[0][0], bounds[0][1], 100) y = np.linspace(bounds[1][0], bounds[1][1], 100) X, Y = np.meshgrid(x, y) Z = rastrigin(X, Y) ax1.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8) ax1.scatter(best_solution[0], best_solution[1], best_energy, color='red', s=100, label='最优解') ax1.set_title('目标函数曲面与最优解') ax1.set_xlabel('X') ax1.set_ylabel('Y') ax1.set_zlabel('Z') ax1.legend() # 2. 能量变化曲线 ax2 = fig.add_subplot(2, 2, 2) energies = [h['current_energy'] for h in sa.history] best_energies = [h['best_energy'] for h in sa.history] ax2.plot(energies, label='当前能量', alpha=0.6) ax2.plot(best_energies, label='最优能量', linewidth=2) ax2.set_title('能量变化曲线') ax2.set_xlabel('迭代次数') ax2.set_ylabel('能量值') ax2.legend() ax2.grid(True) # 3. 温度变化曲线 ax3 = fig.add_subplot(2, 2, 3) temperatures = [h['temperature'] for h in sa.history] ax3.plot(temperatures, color='orange') ax3.set_title('温度衰减曲线') ax3.set_xlabel('迭代次数') ax3.set_ylabel('温度') ax3.grid(True) # 4. 搜索路径 ax4 = fig.add_subplot(2, 2, 4) contour = ax4.contour(X, Y, Z, 50, cmap='viridis') plt.colorbar(contour, ax=ax4) # 绘制搜索路径 path = np.array([h['current_solution'] for h in sa.history]) ax4.plot(path[:, 0], path[:, 1], 'r-', alpha=0.5, label='搜索路径') ax4.scatter(path[0, 0], path[0, 1], color='green', s=100, label='起始点') ax4.scatter(best_solution[0], best_solution[1], color='red', s=100, label='最优解') ax4.set_title('搜索路径') ax4.set_xlabel('X') ax4.set_ylabel('Y') ax4.legend() ax4.grid(True) plt.tight_layout() plt.savefig('simulated_annealing_results.png', dpi=300, bbox_inches='tight') plt.show()# 生成可视化结果visualize_results(sa)退火算法的性能很大程度上取决于参数设置:
初始温度:太高会增加计算时间,太低可能无法跳出局部最优冷却速率:决定温度下降的速度,通常选择0.8-0.99迭代次数:足够的迭代次数是找到优质解的关键实际应用场景退火算法在许多领域都有广泛应用:
旅行商问题:寻找最短路径调度问题:作业车间调度、车辆路径规划机器学习:神经网络参数优化VLSI设计:芯片布局优化金融领域:投资组合优化转载请注明来自海坡下载,本文标题:《全局优化算法(妙趣横生的退火算法用Python轻松实现全局优化)》
京公网安备11000000000001号
京ICP备11000001号
还没有评论,来说两句吧...