前言

差分进化算法(Differential Evolution Algorithm,DE)是一种高效的全局优化算法。它也是基于群体的启发式搜索算法,群中的每个个体对应一个解向量。差分进化算法的进化流程则与遗传算法非常类似,都包括变异、杂交和选择操作,但这些操作的具体定义与遗传算法有所不同。

差分进化算法

差分进化算法和遗传算法有些相似,但是比遗传算法简单好实现,但是差分进化算法的变种,或者说变形有很多,大家可以根据具体情况选择。
差分进化算法也是在现有的解上面,根据一定的办法选择几个解,根据变异公式把这几个解融合成一个变异解,这个过程称为变异;把第i个变异解和第i个旧解的每个参数,跟据一定概率选择选择新解或者旧解的值,称为交叉,形成交叉解;把第i个交叉解和第i个旧解比较,选择较优的解保存作为下一次循环的解,这也是差分进化算法最不同于遗传算法的地方。

差分进化算法流程

  1. 初始化
    随机初始化数目为NP的D维参数向量 $x$ ,$x(i)$ 表示第i个解,每个解参数可以表示为 $x(i,j)$ ,$i=1,2,…,NP,j=1,2,…,D$
    解数目NP根据情况选择,一般选取[50,200]。

  2. 变异
    对于每个解向量$x(i)$,对应的变异向量$v$可以表示为:
    $$
    \begin{aligned}
    v(i)=x(r_{0})+F*(x(r_{1})-x(r_{2}))
    \end{aligned}
    $$
    $r0,r1,r2$为属于[1,…,NP]的三个随机数,并且 $i,r0,r1,r2$ 都不相同,这要求NP必须大于等于4。
    变异算子 $F$ 取值范围为[0,2],F过小可能陷入局部最优,F过大则不容易收敛,一般去[0.4,1]居多。
    边界问题:如果变异以后的值$v(i,j)$超出了边界,可以随机再选择一个数,或者直接去边界值都是可以的。

  3. 交叉
    接下来求交叉向量u,对于u的每个值,有:
    如果 $rand()<=CR: u(i,j)=v(i,j)$
    如果 $rand()>CR : u(i,j)=x(i,j)$
    $rand()$是一个随机数,CR是交叉算子,[0,1],用来控制选择变异向量值还是原来的向量值。

  4. 选择
    把交叉向量和原向量对比,选择较优的那个,这里交叉向量之和对应的原向量对比,也就是对比$u(i)$和$x(i)$哪个更优,就选择哪个作为新的解向量,更新向量$x$,进行下一步。

  5. 终止条件
    当最后的解满足条件,或者遍历次数达到最大,则结束,否则重复2到4。

总结

差分进化算法比较简答,容易实现,可修改的地方也不少,比如变异向量选择上面,可以选择x(best)+F*(x(r1)-x(r2)),x(best)是全局最优解。甚至说F*(…)里面的随机参数,可以是四个六个,这些都是差分进化算法的变形。

代码

题目:

'''
作者:LP
时间:2022年4月24日
题目:差分进化算法
参考:https://blog.csdn.net/Goldboys/article/details/112913122
题目:f ( x , y ) = 3 c o s ( x y ) + x + y
x属于[-4, 4]
y属于[-4, 4]

参考使用面向过程思想编写 本人使用面向对象思想实现
'''
import numpy as np
import random

def func_fitness(x):
value = 3*np.cos(x[0] * x[1] + x[0] + x[1])
return value

class DE:
'''
初始化种群
参数: time 迭代次数
F 变异算子
CR 交叉算子
'''
def __init__(self, time, F, CR):
self.X = 0 # 最终迭代后的最优变量x
self.Y = 0 # 最终迭代后的最优变量y
self.time = time
self.F = F
self.CR = CR
self.Ob=[] #种群列表中的适应度
self.trace = [] #最小适应度
self.x = np.zeros((2,50))
self.v = np.zeros((2,50)) #变异种群
self.u = np.zeros((2,50)) #选择种群
self.x = np.random.rand(2, 50) * 8 - 4 #将随机取值限制在区间范围内
for m in range(0,50):
self.Ob.append(func_fitness(self.x[:,[m]]))
self.trace.append(min(self.Ob))


'''
变异操作:随机选择三个不相同的值使用差分公式
'''
def variation(self):
for m in range(0, 50):
r1=random.randint(0, 50-1)
while (r1==m):
r1 = random.randint(0, 50-1)
r2 = random.randint(0, 50-1)
while (r2 == m) and (r2 == r1):
r2 = random.randint(0, 50-1)
r3 = random.randint(0, 50-1)
while (r3 == m) and (r3 == r2) and (r3 == r1):
r3 = random.randint(0, 50-1)
#差分
self.v[:,m] = self.x[:,r1]+self.F*(self.x[:,r2]-self.x[:,r3])

'''
交叉操作:概率小于CR值时,将变异种群的行向量赋给选择种群,否则使用初始化的种群
'''
def cross(self):
r=np.random.randint(1, 3)
for n in range(0, 2):
cr=np.random.rand()
if (cr <= self.CR and n == r):
self.u[n, :] = self.v[n, :]
else:
self.u[n, :] = self.x[n, :]

'''
边界条件处理:交叉操作后,将选择种群中的值限制在取值范围内
'''
def set_bound(self):
for n in range(0,2):
for m in range(0,50):
if self.u[n,m] < -4:
self.u[n,m] = -4
if self.u[n,m] > 4:
self.u[n,m] = 4

'''
获得索引
'''
def get_index(self, Ob, sort_Ob):
sort_Ob_index=[]
for i in range(0,len(Ob)):
qt=Ob.index(sort_Ob[i])
sort_Ob_index.append(qt)
return sort_Ob_index

'''
选择操作:对选择矩阵排序
'''
def select(self):
Ob1=[]
for m in range(0, 50):
Ob1.append(func_fitness(self.u[:,m]))
for m in range(0, 50):
if Ob1[m] < self.Ob[m]:
self.x[:,m] = self.u[:,m]
for m in range(0, 50):
self.Ob[m] = func_fitness(self.x[:,m])
self.trace.append(min(self.Ob))
sort_Ob = sorted(self.Ob)
sort_Ob_index = self.get_index(self.Ob,sort_Ob)
self.x=self.x[:,sort_Ob_index]
self.X=self.x[:,0]
# print(self.Ob)
self.Y=min(self.Ob)

'''
循环次数:主循环,按照循环次数
'''
def main_loop(self):
for time in range(self.time):
self.variation()
self.cross()
self.set_bound()
self.select()
print(self.X, self.Y)

if __name__ == "__main__":
cout = DE(100, 0.9, 0.5)
cout.main_loop()