局部搜索:爬山算法与模型退火算法的python案例

爬山算法

爬山算法的思路很简单,就是在邻居解空间中选择最优解,直到达到局部最优解,这个算法往往会造成找不到更好的解。废话不多说,先看代码:
这是代码的公共部分

# f(x,y)=e^-(x^2+y^2)+2*e^-((x-1.7)^2+(y-1.7)^2), x:[-2,4], y:[-2,4]
from matplotlib import pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

from random import random, randint
import operator

def func(X, Y):
    def mul(X, Y):
        return np.exp(-(X * X + Y * Y))
    return mul(X, Y) + 2*mul(X - 1.7, Y - 1.7)

def drawPath(X, Y, Z, px, py, pz):
    fig = plt.figure()
    ax = Axes3D(fig)
    plt.title("Local Search")
    ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='viridis')
    ax.set_xlabel('x label', color='r')
    ax.set_ylabel('y label', color='g')
    ax.set_zlabel('z label', color='b')
    ax.plot(px, py, pz, 'r.')
    plt.show()

def show(X, Y, Z):
    fig = plt.figure()
    ax = Axes3D(fig)
    plt.title("F(X, Y)")
    ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='viridis')
    ax.set_xlabel('x label', color='r')
    ax.set_ylabel('y label', color='g')
    ax.set_zlabel('z label', color='b')
    plt.show()
#########################
if __name__ == '__main__':
    X = np.arange(-2, 4, 0.1)
    Y = np.arange(-2, 4, 0.1)
    X, Y = np.meshgrid(X, Y)
    Z = func(X, Y)
    #px, py = max_hill_climb(X, Y)
    px, py = sim_Anneal(X, Y)
    drawPath(X, Y, Z, px, py, func(np.array(px), np.array(py)))

python实现

#最陡爬山算法
def max_hill_climb(X, Y):
    path_x = []
    path_y = []
    len_x, len_y = len(X), len(Y)
    st_x, st_y = randint(0, len_x - 1), randint(0, len_y - 1)
    alis = [[0, 0], [1, 0], [0, 1], [-1, 0], [0, -1]]
    while True:
        hill_v = [
            func(X[0][st_x + 0], Y[st_y + 0][0]),
            func(X[0][st_x + 1], Y[st_y + 0][0]) if st_x<len_x else -99999,
            func(X[0][st_x + 0], Y[st_y + 1][0]) if st_y<len_y else -99999,
            func(X[0][st_x - 1], Y[st_y + 0][0]) if st_x>0 else -99999,
            func(X[0][st_x + 0], Y[st_y - 1][0]) if st_y>0 else -99999,
        ]
        #寻找最大的值和索引
        index, max_v = max(enumerate(hill_v), key=operator.itemgetter(1))
        if index == 0:
            break
        st_x = st_x + alis[index][0]
        st_y = st_y + alis[index][1]
        path_x.append(X[0][st_x])
        path_y.append(Y[st_y][0])
    return path_x, path_y

在寻找四周最优解的过程中,使用了max,enumerate等函数的特性,为了了解max函数如何返回最大值及其索引,我们看个例子:
在这里插入图片描述
enumerate()函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标。operator.itemgetter()函数用于获取对象的哪些维的数据,参数为想要取的一些维度序号。

算法结果

在这里插入图片描述
在这里插入图片描述
可以看到,爬山算法很容易陷入局部最优解,很大程度上取决于“爬山”的起始点位置,而且对于有平台的情况,不总是有效的。

模拟退火算法

在这里插入图片描述
模拟退火算法其实就是在求解过程中,随机的产生新的解,按照一定条件和概率决定是否使用这个解,这样可以有效的避免陷入局部最优,随机产生新的解的范围和当前温度有关,而当新的解并不比当前解更好时,会已一定的概率跳出当前解。温度会在迭代过程中慢下降。
对于最大优化的问题,概率为:
在这里插入图片描述
而温度降低的方式一般有:
在这里插入图片描述

python实现

#经典模拟退火算法
def sim_Anneal(X, Y):
    T_0 = T = 1000
    Tmin = 10
    t = 0
    n = 10 #内层迭代次数
    path_x = []
    path_y = []
    x, y = random()*6-2, random()*6-2
    while T > Tmin:
        for i in range(n):
            value = func(x, y)
            #print(value)
            x_new = x + (random()*2-1)*T*0.1
            y_new = y + (random()*2-1)*T*0.1
            if -2<=x_new<=4 and -2<=y_new<=4:
                if func(x_new, y_new) > value:
                    x, y = x_new, y_new
                    path_x.append(x)
                    path_y.append(y)
                else:
                    if random() < np.exp((func(x_new, y_new) - value) / (0.01*T)):
                        x, y = x_new, y_new
                        path_x.append(x)
                        path_y.append(y)
        t = t + 1
        T = T_0/(1+t) #降温函数
    return path_x, path_y

算法结果

在这里插入图片描述
需要注意的是,这里面的几个参数很重要,直接影响到优化结果
在这里插入图片描述
第一个地方的参数是产生新的随机解的范围,如果新解与旧解的偏差比较大,很容易在下面的过程中不被接受。而第二个参数决定了如果产生的新解不够好,会有多大的概率接受它,那么如果系数很大或者没有,而温度T又很高时,就会造成指数里面的值接近0,而整个值接近1,那么你的解就会跳来跳去,不能收敛到最优。可以在运行过程中打印查看概率的变化。

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

糖醋奶茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值