原理
最近在学习演化算法(Evolutionary algorithm),粒子群算法和遗传算法已经很熟悉了,而差分进化算法我还没认真研究过,趁着暑期实训的机会打算把差分进化算法做个总结,最后再将这三种算法做个比较。
差分进化算法是演化算法的一种,它的思想和遗传算法比较像,算法分为以下几个流程:
-
初始化
演化算法的初始化一般都是随机初始化,个体一般表示为一个多维向量 [ x 1 , x 2 , . . . , x n ] , x i ∈ [ l i , h i ] [x_1,x_2,...,x_n], x_i\in [l_i,h_i] [x1,x2,...,xn],xi∈[li,hi], l i l_i li和 h i h_i hi为第i维变量的范围。一般而言,对第i维而言,可以先产生一个0-1的随机数 r r r,然后 x i = l i + r ∗ ( h i − l i ) x_i=l_i+r*(h_i-l_i) xi=li+r∗(hi−li)。 -
评估
演化算法的个体的好坏是通过适应值函数来决定的,对于最小化问题而言,个体的适应值越小,则越有优势;最大化问题可以转换为最小化问题,毕竟 max f ( x ) = − min ( − f ( x ) ) \max f(x)=-\min (-f(x)) maxf(x)=−min(−f(x)) -
成熟&变异
差分进化算法的成熟和变异与遗传算法有些不同,对种群中的每一个个体,选出除该个体之外的另外三个个体a,b,c,新个体的产生方式如下:
m u t a n t = a + m u t ∗ ( b − c ) mutant=a+mut*(b-c) mutant=a+mut∗(b−c) mut为参数,一般取值为0.5-2。
新产生的个体还需要进行变异操作,对新个体的多维向量的每一维,以一定的概率进行变异得到新的值,变异的新数值一般是取[0,1]内的随机数,然后按照初始化中的操作将其变为范围位于 [ l i , h i ] [l_i,h_i] [li,hi]的值。变异之后,需要将该变异值与原个体值进行比较,取其中适应值更好的一个作为新个体值。再将该新个体值与种群中最优的个体进行比较,更新最优的个体值。 -
结束
进化算法的结束一般有两种,其一是设定循环的最大次数,达到最大次数后中止算法;其二是设定阈值,假定相邻的两次循环中最优个体的适应值差在阈值以内,则认为算法收敛并中止算法。最后的最优个体值即是本次算法求得的最优解。
实现
我利用python实现该算法并进行测试。代码如下:
import numpy as np
import matplotlib.pyplot as plt
def de(fobj, bounds, mut=0.8, crossp=0.7, popsize=20, its=1000):
dimensions = len(bounds)
pop = np.random.rand(popsize, dimensions)
min_b, max_b = np.asarray(bounds).T
diff = np.fabs(min_b - max_b)
pop_denorm = min_b + pop * diff
fitness = np.asarray([fobj(ind) for ind in pop_denorm])
best_idx = np.argmin(fitness)
best = pop_denorm[best_idx]
for i in range(its):
for j in range(popsize):
idxs = [idx for idx in range(popsize) if idx != j]
a, b, c = pop[np.random.choice(idxs, 3, replace = False)]
mutant = np.clip(a + mut * (b - c), 0, 1)
cross_points = np.random.rand(dimensions) < crossp
if not np.any(cross_points):
cross_points[np.random.randint(0, dimensions)] = True
trial = np.where(cross_points, mutant, pop[j])
trial_denorm = min_b + trial * diff
f = fobj(trial_denorm)
if f < fitness[j]:
fitness[j] = f
pop[j] = trial
if f < fitness[best_idx]:
best_idx = j
best = trial_denorm
yield best, fitness[best_idx]
def fitness(x):
return np.sum(x**2)/len(x)
bound=[(-100,100)]*20
results=de(fitness,bound)
x,f=zip(*results)
plt.plot(f)
plt.show()
上述代码参考自这位大佬,他的代码写的很简洁,我没有改动就直接拿上来了。
最后的效果如下:
效果
一般而言,当问题的维度比较小时,大概几十维的时候,演化算法的效果还是很可以的;当问题的维度变得很大,一般是几百维甚至上千维德时候,演化算法的效果就变得比较差了,需要较多的迭代次数才能得到一个较好的解,这也是"维数灾难"的一种体现。