改进的粒子群算法(PSO)算法Python代码——线性/非线性递减惯性权重

前一篇链接:使用基础粒子群(PSO)算法求解一元及二元方程的Python代码
基本的速度更新公式为:
v i d = w v i d − 1 + c 1 r 1 ( p b e s t i d − x i d ) + c 2 r 2 ( g b e s t d − x i d ) v_i^d=wv_i^{d-1}+c_1r_1(pbest_i^d-x_i^d)+c_2r_2(gbest^d-x_i^d) vid=wvid1+c1r1(pbestidxid)+c2r2(gbestdxid)
其中,下标 i i i表示第i个粒子,上标 d d d表示第d次循环, v i d v_i^d vid表示第i个粒子第d次循环的速度, w w w为惯性权重, c 1 , c 2 c_1,c_2 c1,c2分别表示个体学习因子和社会学习因子,一般取2, p b e s t i d pbest_i^d pbestid表示第i个粒子在前d-1次循环中出现的最佳位置, g b e s t d gbest^d gbestd表示所有粒子在前d-1次循环中出现的最佳位置, r 1 , r 2 r_1,r_2 r1,r2分别是两个位于 [ 0 , 1 ] [0,1] [0,1]的随机数。
一个较大的惯性权重有利于全局搜索,而一个较小的惯性权重则更利于局部搜索。接下来先对惯性权重进行修改,总共有两种类型:线性递减惯性权重(LDIW)和非线性递减惯性权重。

线性递减惯性权重

将惯性权重 w w w从一个常数变为以下公式:
w d = w s t a r t − ( w s t a r t − w e n d ) × d K w^d=w_{start}-(w_{start}-w_{end})×\frac{d}{K} wd=wstart(wstartwend)×Kd
其中 d d d是循环次数, K K K是总共的循环次数。

非线性递减惯性权重

常见的有以下两种方法:

  1. w d = w s t a r t − ( w s t a r t − w e n d ) × ( d K ) 2 w^d=w_{start}-(w_{start}-w_{end})×{\left(\frac{d}{K}\right)}^2 wd=wstart(wstartwend)×(Kd)2
  2. w d = w s t a r t − ( w s t a r t − w e n d ) × [ 2 d K − ( d K ) 2 ] w^d=w_{start}-(w_{start}-w_{end})×\left[\frac{2d}{K}-{\left(\frac{d}{K}\right)}^2\right] wd=wstart(wstartwend)×[K2d(Kd)2]

绘制出三种惯性权重修改式的曲线

  1. Python代码
import numpy as np
import matplotlib.pyplot as plt

# 初始化PSO参数
x = np.linspace(0,49,50)
w_start = 0.9
w_end = 0.1
K = 50 # 循环次数

w0 = w_start - (w_start-w_end)*(x/K) # 线性递减惯性权重
w1 = w_start - (w_start-w_end)*(x/K)**2 # 非线性递减惯性权重1
w2 = w_start - (w_start-w_end)*(2*(x/K)-(x/K)**2) # 非线性递减惯性权重2

fig,ax = plt.subplots()
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
ax.plot(x,w0,label = '线性递减惯性权重')
ax.plot(x,w1,label = '非线性递减惯性权重1')
ax.plot(x,w2,label = '非线性递减惯性权重2')
ax.legend()
  1. 绘制结果
    在这里插入图片描述

使用线性递减惯性权重求解二元函数的最值问题

  1. 题目
    求函数 y = x 1 2 + x 2 2 − x 1 x 2 − 10 x 1 − 4 x 2 + 60 y =x_1^2+x_2^2-x_1x_2-10x_1-4x_2+60 y=x12+x22x1x210x14x2+60 x 1 , x 2 ∈ [ − 15 , 15 ] x_1,x_2∈[-15,15] x1,x2[15,15]内的最小值。
  2. 流程图
    在这里插入图片描述
  3. 代码实现
# 第一步,绘制函数图像
# 第一步,绘制函数图像
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d

def func(x,y):
    return x**2+y**2-x*y-10*x-4*y+60

x0 = np.linspace(-15,15,100)
y0 = np.linspace(-15,15,100)
x0,y0 = np.meshgrid(x0,y0)
z0 = func(x0,y0)
fig = plt.figure(constrained_layout=True)
ax = fig.add_subplot(projection='3d')
ax.plot_surface(x0,y0,z0,cmap=plt.cm.viridis,alpha=0.7)
#ax.plot_wireframe(x0,y0,z0) # 另一种绘图方式
ax.set_title('$y = x_1^2+x_2^2-x_1x_2-10x_1-4x_2+60$')

# 第二步,设置粒子群算法的参数
n = 30 # 粒子数量
narvs = 2 # 变量个数
c1 = 2 # 个体学习因子
c2 = 2 # 社会学习因子
w_start = 0.9 # 惯性权重
w_end = 0.1
K = 40 # 迭代次数
vxmax = np.array([(15-(-15))*0.2,(15-(-15))*0.2]) # 粒子在x方向的最大速度
x_lb = np.array([-15,-15]) # x和y的下界
x_ub = np.array([15,15]) # x和y的上界

# 第三步,初始化粒子   
x = x_lb + (x_ub-x_lb)*np.random.rand(n,narvs)
v = -vxmax + 2*vxmax*np.random.rand(n,narvs)

# 第四步,计算适应度
fit = func(x[:,0],x[:,1]) # 计算每个粒子的适应度
pbest = x # 初始化这n个例子迄今为止找到的最佳位置
ind = np.argmax(fit) # 找到适应度最大的那个粒子的下标
gbest = x[ind,:]
gbest_total = np.zeros(K)

# 第五步,更新粒子速度和位置
for j in range(K): # 外层循环,共K次
    w = w_start - (w_start-w_end)*(j/K) # 修改后的惯性权重表达式
    for p in range(n):
        v[p,:] = w*v[p,:] + c1*np.random.rand(narvs)*(pbest[p,:]-x[p,:]) + c2*np.random.rand(narvs)*(gbest-x[p,:])
    loc_v = np.where(v<-vxmax)
    v[loc_v] = -vxmax[loc_v[1]] # 速度小于-vmax的元素赋值为-vmax
    loc_v = np.where(v>vxmax)
    v[loc_v] = vxmax[loc_v[1]] # 速度大于vmax的元素赋值为vmax
    x = x + v # 更新第i个粒子的位置
    loc_x = np.where(x<x_lb)
    x[loc_x] = x_lb[loc_x[1]]
    loc_x = np.where(x>x_ub)
    x[loc_x] = x_ub[loc_x[1]]
    
    # 第六步,重新计算适应度并找到最优粒子
    fit = func(x[:,0],x[:,1]) # 重新计算n个粒子的适应度
    for k in range(n): # 更新第k个粒子迄今为止找到的最佳位置
        if fit[k]<func(pbest[k,0],pbest[k,1]):
            pbest[k,:] = x[k,:]
    if np.min(fit)<func(gbest[0],gbest[1]): # 更新所有粒子迄今找到的最佳位置
        gbest = x[np.argmin(fit),:]
    gbest_total[j] = func(gbest[0],gbest[1])

ax.scatter(x[:,0],x[:,1],fit,c='r',marker='x')
fig,ax = plt.subplots()
ax.plot(np.arange(K),gbest_total)
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来显示负号
ax.set_title('算法找到的最小值随优化次数的变化曲线')
ax.set_xlabel('循环次数')
ax.autoscale(axis='x',tight=True)
print('找到的最优解为:',func(gbest[0],gbest[1]))
  1. 绘制结果
  • 打印输出最优解:找到的最优解为: 8.000000006295593
    可以看出,比上一篇原始算法的精度提高了很多。
  • 显示最终粒子所在的位置:
    在这里插入图片描述
    其中红色叉号表示最后一次循环结束后粒子所在的位置。可以看出,相比上一篇的原始算法,粒子最后都收敛到了最优点。
  • 循环过程中最优解随循环次数的变化曲线:
    在这里插入图片描述
    可以看出,收敛速度明显高于前一篇原始算法的速度。

使用非线性递减惯性权重1求解二元函数的最值问题

  1. 权重公式:
    w d = w s t a r t − ( w s t a r t − w e n d ) × ( d K ) 2 w^d=w_{start}-(w_{start}-w_{end})×{\left(\frac{d}{K}\right)}^2 wd=wstart(wstartwend)×(Kd)2
  2. Python代码
# 第一步,绘制函数图像
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d

def func(x,y):
    return x**2+y**2-x*y-10*x-4*y+60

x0 = np.linspace(-15,15,100)
y0 = np.linspace(-15,15,100)
x0,y0 = np.meshgrid(x0,y0)
z0 = func(x0,y0)
fig = plt.figure(constrained_layout=True)
ax = fig.add_subplot(projection='3d')
ax.plot_surface(x0,y0,z0,cmap=plt.cm.viridis,alpha=0.7)
#ax.plot_wireframe(x0,y0,z0) # 另一种绘图方式
ax.set_title('$y = x_1^2+x_2^2-x_1x_2-10x_1-4x_2+60$')

# 第二步,设置粒子群算法的参数
n = 30 # 粒子数量
narvs = 2 # 变量个数
c1 = 2 # 个体学习因子
c2 = 2 # 社会学习因子
w_start = 0.9 # 惯性权重
w_end = 0.1
K = 40 # 迭代次数
vxmax = np.array([(15-(-15))*0.2,(15-(-15))*0.2]) # 粒子在x方向的最大速度
x_lb = np.array([-15,-15]) # x和y的下界
x_ub = np.array([15,15]) # x和y的上界

# 第三步,初始化粒子   
x = x_lb + (x_ub-x_lb)*np.random.rand(n,narvs)
v = -vxmax + 2*vxmax*np.random.rand(n,narvs)

# 第四步,计算适应度
fit = func(x[:,0],x[:,1]) # 计算每个粒子的适应度
pbest = x # 初始化这n个例子迄今为止找到的最佳位置
ind = np.argmax(fit) # 找到适应度最大的那个粒子的下标
gbest = x[ind,:]
gbest_total = np.zeros(K)

# 第五步,更新粒子速度和位置
for j in range(K): # 外层循环,共K次
    w = w_start - (w_start-w_end)*(j/K)**2 # 修改后的惯性权重表达式
    for p in range(n):
        v[p,:] = w*v[p,:] + c1*np.random.rand(narvs)*(pbest[p,:]-x[p,:]) + c2*np.random.rand(narvs)*(gbest-x[p,:])
    loc_v = np.where(v<-vxmax)
    v[loc_v] = -vxmax[loc_v[1]] # 速度小于-vmax的元素赋值为-vmax
    loc_v = np.where(v>vxmax)
    v[loc_v] = vxmax[loc_v[1]] # 速度大于vmax的元素赋值为vmax
    x = x + v # 更新第i个粒子的位置
    loc_x = np.where(x<x_lb)
    x[loc_x] = x_lb[loc_x[1]]
    loc_x = np.where(x>x_ub)
    x[loc_x] = x_ub[loc_x[1]]
    
    # 第六步,重新计算适应度并找到最优粒子
    fit = func(x[:,0],x[:,1]) # 重新计算n个粒子的适应度
    for k in range(n): # 更新第k个粒子迄今为止找到的最佳位置
        if fit[k]<func(pbest[k,0],pbest[k,1]):
            pbest[k,:] = x[k,:]
    if np.min(fit)<func(gbest[0],gbest[1]): # 更新所有粒子迄今找到的最佳位置
        gbest = x[np.argmin(fit),:]
    gbest_total[j] = func(gbest[0],gbest[1])

ax.scatter(x[:,0],x[:,1],fit,c='r',marker='x')
fig.savefig('./program8.png',dpi=500)
fig,ax = plt.subplots()
ax.plot(np.arange(K),gbest_total)
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来显示负号
ax.set_title('算法找到的最小值随优化次数的变化曲线')
ax.set_xlabel('循环次数')
ax.autoscale(axis='x',tight=True)
print('找到的最优解为:',func(gbest[0],gbest[1]))
fig.savefig('./program9.png',dpi=500)
  1. 绘制结果
  • 打印输出最优解:找到的最优解为: 8.000003676735233
    可以看出,比上一篇原始算法的精度提高了很多,与线性递减权重相比,精度少有提高,但基本可以不计。
  • 显示最终粒子所在的位置:
    在这里插入图片描述
    其中红色叉号表示最后一次循环结束后粒子所在的位置。可以看出,相比上一篇的原始算法,粒子最后大多都收敛到了最优点。
  • 循环过程中最优解随循环次数的变化曲线:
    在这里插入图片描述
    可以看出,收敛速度明显高于前一篇原始算法的速度,与线性递减惯性权重相比,速度相当。

使用非线性递减惯性权重2求解二元函数的最值问题

  1. 权重公式:
    w d = w s t a r t − ( w s t a r t − w e n d ) × [ 2 d K − ( d K ) 2 ] w^d=w_{start}-(w_{start}-w_{end})×\left[\frac{2d}{K}-{\left(\frac{d}{K}\right)}^2\right] wd=wstart(wstartwend)×[K2d(Kd)2]
  2. Python代码
# 第一步,绘制函数图像
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d

def func(x,y):
    return x**2+y**2-x*y-10*x-4*y+60

x0 = np.linspace(-15,15,100)
y0 = np.linspace(-15,15,100)
x0,y0 = np.meshgrid(x0,y0)
z0 = func(x0,y0)
fig = plt.figure(constrained_layout=True)
ax = fig.add_subplot(projection='3d')
ax.plot_surface(x0,y0,z0,cmap=plt.cm.viridis,alpha=0.7)
#ax.plot_wireframe(x0,y0,z0) # 另一种绘图方式
ax.set_title('$y = x_1^2+x_2^2-x_1x_2-10x_1-4x_2+60$')

# 第二步,设置粒子群算法的参数
n = 30 # 粒子数量
narvs = 2 # 变量个数
c1 = 2 # 个体学习因子
c2 = 2 # 社会学习因子
w_start = 0.9 # 惯性权重
w_end = 0.1
K = 40 # 迭代次数
vxmax = np.array([(15-(-15))*0.2,(15-(-15))*0.2]) # 粒子在x方向的最大速度
x_lb = np.array([-15,-15]) # x和y的下界
x_ub = np.array([15,15]) # x和y的上界

# 第三步,初始化粒子   
x = x_lb + (x_ub-x_lb)*np.random.rand(n,narvs)
v = -vxmax + 2*vxmax*np.random.rand(n,narvs)

# 第四步,计算适应度
fit = func(x[:,0],x[:,1]) # 计算每个粒子的适应度
pbest = x # 初始化这n个例子迄今为止找到的最佳位置
ind = np.argmax(fit) # 找到适应度最大的那个粒子的下标
gbest = x[ind,:]
gbest_total = np.zeros(K)

# 第五步,更新粒子速度和位置
for j in range(K): # 外层循环,共K次
    w = w_start - (w_start-w_end)*(2*(j/K)-(j/K)**2) # 修改后的惯性权重表达式
    for p in range(n):
        v[p,:] = w*v[p,:] + c1*np.random.rand(narvs)*(pbest[p,:]-x[p,:]) + c2*np.random.rand(narvs)*(gbest-x[p,:])
    loc_v = np.where(v<-vxmax)
    v[loc_v] = -vxmax[loc_v[1]] # 速度小于-vmax的元素赋值为-vmax
    loc_v = np.where(v>vxmax)
    v[loc_v] = vxmax[loc_v[1]] # 速度大于vmax的元素赋值为vmax
    x = x + v # 更新第i个粒子的位置
    loc_x = np.where(x<x_lb)
    x[loc_x] = x_lb[loc_x[1]]
    loc_x = np.where(x>x_ub)
    x[loc_x] = x_ub[loc_x[1]]
    
    # 第六步,重新计算适应度并找到最优粒子
    fit = func(x[:,0],x[:,1]) # 重新计算n个粒子的适应度
    for k in range(n): # 更新第k个粒子迄今为止找到的最佳位置
        if fit[k]<func(pbest[k,0],pbest[k,1]):
            pbest[k,:] = x[k,:]
    if np.min(fit)<func(gbest[0],gbest[1]): # 更新所有粒子迄今找到的最佳位置
        gbest = x[np.argmin(fit),:]
    gbest_total[j] = func(gbest[0],gbest[1])

ax.scatter(x[:,0],x[:,1],fit,c='r',marker='x')
fig,ax = plt.subplots()
ax.plot(np.arange(K),gbest_total)
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来显示负号
ax.set_title('算法找到的最小值随优化次数的变化曲线')
ax.set_xlabel('循环次数')
ax.autoscale(axis='x',tight=True)
print('找到的最优解为:',func(gbest[0],gbest[1]))
  1. 绘制结果
  • 打印输出最优解:找到的最优解为: 8.000000000066308
    可以看出,比上一篇原始算法的精度提高了很多,与线性递减权重和非线性递减权重1相比,精度提高更多。
  • 显示最终粒子所在的位置:
    在这里插入图片描述
    其中红色叉号表示最后一次循环结束后粒子所在的位置。可以看出,相比上一篇的原始算法和线性递减权重及非线性递减权重1,粒子几乎全部收敛到了最优点。
  • 循环过程中最优解随循环次数的变化曲线:
    在这里插入图片描述
    可以看出,收敛速度明显高于前一篇原始算法的速度,与线性递减惯性权重和非线性递减权重1相比,收敛速度也大大加快。
  • 10
    点赞
  • 89
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值