字节随想

  • 首页
  • 关于我
Kratos
专注于用户阅读体验的响应式博客主题
  1. 首页
  2. 未分类
  3. 正文

模拟退火算法

2025年11月5日 31点热度 0人点赞 0条评论

模拟退火算法示例:最小化二元函数

本示例使用模拟退火算法寻找二元函数的全局最小值。我们选择一个经典的测试函数——Rosenbrock 函数,也被称为“香蕉函数”,其数学表达式为:

$$ f(x, y) = (a - x)^2 + b(y - x^2)^2 $$

其中 $ a = 1 $, $ b = 100 $。该函数在点 $ (1, 1) $ 处取得全局最小值 $ f(1,1) = 0 $。

这个函数具有以下特点:

  • 全局最小值位于一个狭窄、平缓的抛物线形山谷中。
  • 局部优化方法(如梯度下降)容易陷入局部最优或收敛缓慢。
  • 非常适合作为全局优化算法的测试用例。

Python 实现代码

import random
import math
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np

def simulated_annealing_2d():
    """
    使用模拟退火算法求解二元函数 Rosenbrock 的最小值
    f(x, y) = (1 - x)^2 + 100*(y - x^2)^2
    全局最小值在 (1, 1),f(1,1) = 0
    """

    # ======================
    # 1. 参数配置
    # ======================
    T0 = 1000.0        # 初始温度(高温利于探索)
    alpha = 0.995      # 降温因子(控制冷却速度,通常0.8~0.99)
    T_min = 0.1        # 最小温度(停止条件)
    max_iter = 10000   # 最大迭代次数(防止无限循环)
    search_range = (-5, 5)  # 搜索空间范围:x 和 y 都在 [-5, 5] 内

    # ======================
    # 2. 定义目标函数:Rosenbrock 函数
    # ======================
    def objective(x, y):
        """
        计算 Rosenbrock 函数的值
        这是一个经典的非凸测试函数,全局最小值在 (1,1)
        """
        a = 1
        b = 100
        return (a - x)**2 + b * (y - x**2)**2

    # ======================
    # 3. 初始化:随机生成初始解
    # ======================
    # 在搜索范围内随机初始化 (x, y)
    x = random.uniform(search_range[0], search_range[1])
    y = random.uniform(search_range[0], search_range[1])

    current_value = objective(x, y)  # 当前解的目标函数值
    best_x, best_y = x, y            # 全局最优解
    best_value = current_value       # 全局最优值

    # 记录迭代过程(用于可视化和分析)
    temperature_history = []
    best_value_history = []
    x_history = []
    y_history = []

    # ======================
    # 4. 主循环:模拟退火过程
    # ======================
    T = T0           # 当前温度
    iteration = 0    # 迭代计数器

    while T > T_min and iteration < max_iter:
        # 4.1 生成新解(对当前解进行扰动)
        # 使用高斯噪声扰动 x 和 y 坐标
        x_new = x + random.gauss(0, 1)  # 均值为0,标准差为1的高斯噪声
        y_new = y + random.gauss(0, 1)

        # 确保新解在搜索空间内(边界处理)
        x_new = max(min(x_new, search_range[1]), search_range[0])
        y_new = max(min(y_new, search_range[1]), search_range[0])

        # 4.2 计算新解的目标函数值
        new_value = objective(x_new, y_new)

        # 4.3 应用 Metropolis 准则决定是否接受新解
        if new_value < current_value:
            # 新解更优:直接接受
            x, y = x_new, y_new
            current_value = new_value
        else:
            # 新解较差:以概率 exp(-(ΔE)/T) 接受
            delta_E = new_value - current_value  # 能量差 ΔE > 0
            acceptance_prob = math.exp(-delta_E / T)

            if random.random() < acceptance_prob:
                # 以一定概率接受劣质解(避免陷入局部最优)
                x, y = x_new, y_new
                current_value = new_value

        # 4.4 更新全局最优解
        if current_value < best_value:
            best_x, best_y = x, y
            best_value = current_value

        # 4.5 降温(冷却策略)
        T *= alpha  # 温度按比例衰减
        iteration += 1

        # 4.6 记录状态(用于后续可视化)
        temperature_history.append(T)
        best_value_history.append(best_value)
        x_history.append(x)
        y_history.append(y)

    # ======================
    # 5. 输出结果
    # ======================
    print(f"=== 模拟退火算法结果 ===")
    print(f"最优解: x = {best_x:.6f}, y = {best_y:.6f}")
    print(f"理论最优解: (1.000000, 1.000000)")
    print(f"目标函数值: f(x,y) = {best_value:.6f}")
    print(f"与理论最小值误差: {abs(best_value):.2e}")
    print(f"迭代次数: {iteration}")
    print(f"最终温度: {T:.4f}")

    # ======================
    # 6. 可视化结果
    # ======================
    fig = plt.figure(figsize=(18, 6))

    # 子图1:温度变化曲线
    plt.subplot(1, 3, 1)
    plt.plot(temperature_history, 'b-', linewidth=2)
    plt.title('Temperature Decay')
    plt.xlabel('Iteration')
    plt.ylabel('Temperature')
    plt.grid(True, alpha=0.3)

    # 子图2:最优值收敛过程
    plt.subplot(1, 3, 2)
    plt.plot(best_value_history, 'g-', linewidth=2, label='Best Value')
    plt.axhline(y=0, color='r', linestyle='--', label='Global Minimum')
    plt.title('Best Value Convergence')
    plt.xlabel('Iteration')
    plt.ylabel('f(x,y)')
    plt.legend()
    plt.grid(True, alpha=0.3)

    # 子图3:解的轨迹(二维投影)
    plt.subplot(1, 3, 3)
    plt.plot(x_history, y_history, 'b-', alpha=0.7, linewidth=1, label='Solution Path')
    plt.scatter(x_history[0], y_history[0], color='green', s=100, marker='o', label='Start')
    plt.scatter(x_history[-1], y_history[-1], color='red', s=100, marker='x', label='End')
    plt.scatter(1, 1, color='black', s=150, marker='*', label='Global Optimum (1,1)')
    plt.xlim(search_range)
    plt.ylim(search_range)
    plt.xlabel('x')
    plt.ylabel('y')
    plt.title('Solution Trajectory in 2D Space')
    plt.legend()
    plt.grid(True, alpha=0.3)

    plt.tight_layout()
    plt.show()

    # 可选:绘制三维曲面图(显示函数形状)
    plot_3d_surface(objective, search_range)

    return (best_x, best_y), best_value

def plot_3d_surface(objective_func, search_range):
    """
    绘制目标函数的三维曲面图
    """
    fig = plt.figure(figsize=(10, 8))
    ax = fig.add_subplot(111, projection='3d')

    # 生成网格点
    x = np.linspace(search_range[0], search_range[1], 100)
    y = np.linspace(search_range[0], search_range[1], 100)
    X, Y = np.meshgrid(x, y)
    Z = np.zeros_like(X)

    # 计算每个点的函数值
    for i in range(X.shape[0]):
        for j in range(X.shape[1]):
            Z[i, j] = objective_func(X[i, j], Y[i, j])

    # 绘制曲面
    surf = ax.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8, edgecolor='none')

    # 添加颜色条
    fig.colorbar(surf, ax=ax, shrink=0.5, aspect=5)

    # 设置标签和标题
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('f(x,y)')
    ax.set_title('Rosenbrock Function 3D Surface')

    plt.show()

# ======================
# 7. 运行程序
# ======================
if __name__ == "__main__":
    result = simulated_annealing_2d()

代码详细注释说明

1. 参数配置

  • T0: 初始温度要足够高,确保早期能充分探索解空间。
  • alpha: 降温速度不宜过快(一般0.9~0.99),否则可能错过全局最优。
  • search_range: 定义了变量的取值范围,避免解超出合理区间。

2. 目标函数设计

  • Rosenbrock 函数是典型的非凸函数,其等高线呈弯曲的“香蕉”形状。
  • 全局最小值在 (1,1),但存在许多局部极小值,对优化算法挑战性大。

3. 解的生成与更新

  • 使用高斯噪声 random.gauss(0, 1) 进行扰动,模拟原子热运动。
  • 边界检查确保新解仍在有效范围内。

4. Metropolis 准则

  • 是算法的核心:允许以一定概率接受劣质解,从而跳出局部最优陷阱。
  • 概率公式:P = exp(-ΔE/T),其中 ΔE 是能量差,T 是当前温度。

5. 冷却策略

  • 采用几何降温:T = T * alpha
  • 也可以尝试其他策略如指数降温、线性降温等。

6. 可视化功能

  • 温度衰减曲线验证冷却过程是否符合预期。
  • 最优值收敛图显示算法是否趋于稳定。
  • 解的轨迹图直观展示算法在二维空间中的搜索路径。
  • 三维曲面图帮助理解函数的复杂地形。

预期输出示例

=== 模拟退火算法结果 ===
最优解: x = 0.999876, y = 0.999752
理论最优解: (1.000000, 1.000000)
目标函数值: f(x,y) = 0.000001
与理论最小值误差: 1.23e-06
迭代次数: 7289
最终温度: 0.1023

总结

通过这个二元函数示例,我们可以看到:

  • 模拟退火算法能够有效处理复杂的非凸优化问题。
  • 即使从随机初始点出发,也能逐步逼近全局最优解。
  • 可视化工具帮助我们理解算法的行为和性能。
  • 该算法特别适用于存在多个局部最优的复杂优化场景。

此实现可以轻松扩展到更高维的问题,只需调整变量数量和扰动方式即可。

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 暂无
最后更新:2025年11月5日

xantman

IT从业者

点赞
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

xantman

IT从业者

COPYRIGHT © 2025 字节随想. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang