背景介绍
BenchMark
1. Sphere 2. Schwefel’s P2.22 3. Quadric 4.Schwefel’s P2.21
5. step 6. noise 7. Rosenbrock 9. Schwefel
10.Rastrigin 11. Ackley 12.Griewank
经典适应度函数集合
import numpy as np
import copy
"""
Author : Robin_Hua
update time : 2021.10.27
version : 2.0
"""
class Sphere:
def __init__(self, x):
self.x = x
def getvalue(self):
res = np.sum(self.x**2)
return res
class Schwefel2_22:
def __init__(self, x):
self.x = x
def getvalue(self):
res = np.sum(np.abs(self.x)) + np.prod(np.abs(self.x))
return res
class Quadric:
def __init__(self, x):
self.x = x
def getvalue(self):
d = self.x.shape[0]
res = 0
for i in range(d):
res = res + np.sum(self.x[:i+1])**2
return res
class Noise:
def __init__(self,x):
self.x = x
def getvalue(self):
d = self.x.shape[0]
res = np.sum(np.arange(1, d + 1) * self.x ** 4) + np.random.random()
return res
class Schwefel2_21:
def __init__(self,x):
self.x = x
def getvalue(self):
res = np.max(np.abs(self.x))
return res
class Step:
def __init__(self,x):
self.x = x
def getvalue(self):
res = np.sum(np.floor(self.x + 0.5) ** 2)
return res
class Rosenbrock:
def __init__(self,x):
self.x = x
def getvalue(self):
d = self.x.shape[0]
res = np.sum(np.abs(100*(self.x[1:] - self.x[:-1]**2)**2 + (1 - self.x[:-1])**2))
return res
class Schwefel:
def __init__(self,x):
self.x = x
def getvalue(self):
d = self.x.shape[0]
res = 418.9829*d - np.sum(self.x * np.sin(np.sqrt(np.abs(self.x))))
return res
class Rastrigin:
def __init__(self,x):
self.x = x
def getvalue(self):
d = self.x.shape[0]
res = 10 * d + np.sum(self.x ** 2 - 10 * np.cos(2 * np.pi * self.x))
return res
class Ackley:
def __init__(self,x):
self.x = x
def getvalue(self):
d = self.x.shape[0]
res = - 20 * np.exp(-0.2 * np.sqrt(np.mean(self.x ** 2)))
res = res - np.exp(np.mean(np.cos(2 * np.pi * self.x))) + 20 + np.exp(1)
return res
class Griewank:
def __init__(self,x):
self.x = x
def getvalue(self):
d = self.x.shape[0]
i = np.arange(1, d + 1)
res = 1 + np.sum(self.x ** 2) / 4000 - np.prod(np.cos(self.x / np.sqrt(i)))
return res
class Generalized_Penalized:
def __init__(self,x):
self.x = x
def u(self,a,k,m):
temp = copy.deepcopy(self.x)
temp[-a <= temp.any() <= a] = 0
temp[temp > a] = k*(temp[temp > a]-a)**m
temp[temp < -a] = k * (-temp[temp < -a] - a) ** m
"""
temp = np.zeros_like(self.x)
d = self.x.shape[0]
for i in range(d):
if self.x[i]>a:
temp[i] = k*(self.x[i]-a)**m
elif self.x[i]<-a:
temp[i] = k * (-self.x[i] - a) ** m
else:
pass
"""
return temp
def getvalue(self):
d = self.x.shape[0]
y = 1+1/4*(self.x+1)
res = np.pi/d*(10*np.sin(np.pi*y[0])**2+np.sum((y[:-1]-1)**2*(1+10*np.sin(np.pi*y[1:])**2))+(y[-1]-1)**2)+np.sum(self.u(10,100,4))
return res
def benchmark_func(x,func_num):
func = func_list[func_num]
res = func(x)
return res
def benchmark_range(idex):
rangelist = [[-100,100], [-10,10],[-100,100],[-100, 100], [-100, 100], [-1.28,1.28],[-10,10],[-500,500],[-5.12,5.12],[-32,32],[-600,600],[-50,50]]
return rangelist[idex]
def get_minmax(func_num):
x_max = benchmark_range(func_num)[1]
x_min = benchmark_range(func_num)[0]
return x_min,x_max
func_list = [Sphere,Schwefel2_22,Quadric,Schwefel2_21,Step,Noise,Rosenbrock,Schwefel,Rastrigin,Ackley,Griewank,Generalized_Penalized]
算法介绍
GA遗传算法
原理
遗传算法以一种群体中的所有个体为对象, 并利用随机化技术指导对一个被编 码的参数空间进行高效搜索 。其中,选择 、交叉和变异构成了遗传算法的遗传 操作;参数编码 、初始群体的设定 、适应度函数的设计 、遗传操作设计 、控制 参数设定五个要素组成了遗传算法的核心内容。
遗传算法中每一条染色体,对应着遗传算法的一个解决方案,一般我们用适应性函数( fitness function) 来衡量这个解决方案的优劣 。所以从一个基因组到 其解的适应度形成一个映射 。遗传算法的实现过程实际上就像自然界的进化过程那样。
遗传算法求解时首先寻找一种对问题潜在解进行“ 数字化 ”编码的方案, 即建立表现型和基因型的映射关系; 然后随机初始化一个种群,种群里面的个体就是这些数字化的编码 。接下来,通过适当的解码过程之后 。用适应性函数对每一 个基因个体作一次适应度评估 。用选择函数按照某种规定择优选择 。而后进行后代的繁殖过程,也就是最为重要的交叉变异环节,此环节也是 GA 算法与其他算法最为显著的区别之一。
具体地繁殖后代过程包括交叉和变异两步 。交叉是指每一个个体是由父亲和母 亲两个个体繁殖产生,子代个体的 DNA( 二进制串)获得了一半父亲的 DNA, 一半母亲的 DNA,但是这里的一半并不是真正的一半,这个位置叫做交配点, 是随机产生的,可以是染色体的任意位置 。通过交叉子代获得了一半来自父亲 一半来自母亲的 DNA,但是子代自身可能发生变异,使得其 DNA 即不来自父亲 ,也不来自母亲,在某个位置上发生随机改变,通常就是改变 DNA 的一个二进制位(0变到1,或者1变到 0)。
需要说明的是交叉和变异不是必然发生,而是有一定概率发生 。先考虑交叉, 最坏情况,交叉产生的子代的 DNA 都比父代要差(这样算法有可能朝着优化的 反方向进行,不收敛), 如果交叉是有一定概率不发生,那么就能保证子代有一 部分基因和当前这一代基因水平一样;而变异本质上是让算法跳出局部最优解, 如果变异时常发生,或发生概率太大,那么算法到了最优解时还会不稳定 。交叉概率,范围一般是0.6~1,突变常数(又称为变异概率),通常是 0.1 或者更小。
公式
代码
定义GA:
from random import uniform
from random import random
from random import randint
import numpy as np
from benchmark import benchmark_func, benchmark_range
class GA():
def __init__(self,dim,NP,funcnumber,select_rate,mutation_rate):
self.dim = dim
self.populationumber = NP
self.select_rate=select_rate
self.mutation_rate=mutation_rate
self.Lbound,self.Ubound = benchmark_range(funcnumber)
self.benchmark_func =benchmark_func(funcnumber)
self.population = np.random.uniform(self.Lbound, self.Ubound, (NP, dim))
def evolve(self,retain_rate = 0.15):
parents = self.selection(retain_rate,self.select_rate)
self.crossover(parents)
self.mutation(self.mutation_rate)
def selection(self,retian_rate,random_select_rate):
self.graded = [(self.benchmark_func(chromosome).getvalue(),chromosome) for chromosome in self.population]
self.graded.sort(key=lambda x:x[0])
parents =[i[1] for i in self.graded[:int(retian_rate*self.populationumber)]]
for chromosome in self.graded[int(retian_rate*len(self.graded)):]:
if random_select_rate > random():
parents.append(chromosome[1])
return parents
def crossover(self,parents):
children =[]
target = len(self.population)-len(parents)
while len(children)<target:
male = randint(0,len(parents)-1)
female = randint(0, len(parents) - 1)
if male != female:
cross_point = randint(0,self.dim-1)
child = np.hstack((parents[male][:cross_point],parents[female][cross_point:]))
children.append(child)
if len(parents)==len(self.population):
parents=parents[:-1]
target = len(self.population) - len(parents)
while len(children) < target:
male = randint(0, len(parents) - 1)
female = randint(0, len(parents) - 1)
if male != female:
cross_point = randint(0, self.dim - 1)
child = np.hstack((parents[male][:cross_point], parents[female][cross_point:]))
children.append(child)
try:
self.population = np.vstack((parents,children))
except:
print(len(parents),len(children))
# self.population = np.vstack((parents,children))
def mutation(self,rate):
for i in range(len(self.population)):
if random() <rate:
j = randint(0,self.dim-1)
self.population[i][j]=uniform(self.Lbound,self.Ubound)
研究不同参数对结果的影响(主函数):
if __name__ =="__main__":
for dim in [30,50,100]: # [30, 50, 100]
for NP in [10,30,50,70,110]:
for select_rate in [0.5,0.6,0.7,0.8]: # [0.5,0.6,0.7,0.8]
for mutation_rate in [0.01,0.03,0.06,0.09,0.12]: # [0.01,0.05,0.10,0.15,0.2]
for funcnumber in range(12):
file = open('ga_result/dim{}NP{}SR{}MR{}.txt'.format(dim, NP, select_rate, mutation_rate), 'a+')
ga = GA(30, 50,funcnumber,select_rate,mutation_rate)# 首个参数为基因的长度
t=0
for i in range(dim*10000//NP):
t+=1
ga.evolve()
if t>dim*10000//NP/20:
t-=dim*10000//NP/20
file.write("{:.3e}\t".format(ga.graded[0][0]))
file.close()
PSO粒子群算法
原理
鸟群觅食与粒子群算法类比(概念解释)
粒子群算法流程
公式
位置更新公式:
代码
from benchmark import benchmark_func,benchmark_range
class PSO:
def __init__(self, pop, gengeration, x_min, x_max, fitnessFunction, c1=0.1, c2=0.1, w=1):
self.c1 = c1
self.c2 = c2
self.w = w # 惯性因子
self.pop = pop # 种群大小
self.x_min = np.array(x_min) # 约束
self.x_max = np.array(x_max)
self.generation = generation
self.max_v = (self.x_max - self.x_min) * 0.05
self.min_v = -(self.x_max - self.x_min) * 0.05
self.fitnessFunction = fitnessFunction
self.particals = [Partical(self.x_min, self.x_max, self.max_v, self.min_v, self.fitnessFunction)
for i in range(self.pop)]
self.gbest = np.zeros(len(x_min))
self.gbestFit = float('Inf')
self.fitness_list = [] # 每次的最佳适应值
def init_gbest(self):
for part in self.particals:
if part.getBestFit() < self.gbestFit:
self.gbestFit = part.getBestFit()
self.gbest = part.getPbest
def done(self):
for i in range(self.generation):
for part in self.particals:
part.update(self.w, self.c1, self.c2, self.gbest)
if part.getBestFit() < self.gbestFit:
self.gbestFit = part.getBestFit()
self.gbest = part.getPbest()
self.fitness_list.append(self.gbest)
return self.fitness_list, self.gbest
定义速度和位置更新系统:
import numpy as np
class Partical:
def __init__(self, x_min, x_max, max_v, min_v, fitness):
self.dim = len(x_min) # 获得变量数
self.max_v = max_v
self.min_v = min_v
self.x_min = x_min
self.x_max = x_max
# self.pos=np.random.uniform(x_min,x_max,dim)
self.pos = np.zeros(self.dim)
self.pbest = np.zeros(self.dim)
self.initPos(x_min, x_max)
self._v = np.zeros(self.dim)
self.initV(min_v, max_v) # 初始化速度
self.fitness = fitness
self.bestFitness = fitness(self.pos)
def _updateFit(self):
if self.fitness(self.pos) < self.bestFitness:
self.bestFitness = self.fitness(self.pos)
self.pbest = self.pos
def _updatePos(self):
self.pos = self.pos + self._v
for i in range(self.dim):
self.pos[i] = min(self.pos[i], self.x_max[i])
self.pos[i] = max(self.pos[i], self.x_min[i])
def _updateV(self, w, c1, c2, gbest):
self._v = w * self._v + c1 * np.random.random() * (self.pbest - self.pos) + c2 * np.random.random() * (gbest - self.pos)
for i in range(self.dim):
self._v[i] = min(self._v[i], self.max_v[i])
self._v[i] = max(self._v[i], self.min_v[i])
def initPos(self, x_min, x_max):
for i in range(self.dim):
self.pos[i] = np.random.uniform(x_min[i], x_max[i])
self.pbest[i] = self.pos[i]
def initV(self, min_v, max_v):
for i in range(self.dim):
self._v[i] = np.random.uniform(min_v[i], max_v[i])
def getPbest(self):
return self.pbest
def getBestFit(self):
return self.bestFitness
def update(self, w, c1, c2, gbest):
self._updateV(w, c1, c2, gbest)
self._updatePos()
self._updateFit()
研究不同参数对结果的影响(主函数):
if __name__ =="__main__":
for dim in [30,50,100]: # [30, 50, 100]
pop = 300
generation = 500
for func_num in range(12):
def fit_fun(pos):
x,y = pos
return benchmark_func(func_num)
x_range = benchmark_range(func_num)
x_min = [x_range[0],x_range[0]]
x_max = [x_range[1],x_range[1]]
file = open('pso_result/dim{}FN{}.txt'.format(dim,func_num), 'a+')
pso = PSO(pop, generation, x_min, x_max, fit_fun)
fit_list, best_pos = pso.done()
t=0
for i in range(pop*10000//generation):
t+=1
m = pso.done()
if t>pop*10000//generation/20:
t-=pop*10000//generation/20
file.write("{:.e}\t".format(best_pos))
file.close()
DE差分算法
原理
公式
其中,xi(0)是第i个个体,j表示第j维。
其中,和分别为第j维的下界和上界,rand(0,1)表示在区间[0, 1]上的随机数。
变异:
其中r1,r2,r3 是三个随机数,区间为[1,NP],F称为缩放因子,为一个确定的常数。g表示第g代。
交叉:
其中CR称为交叉概率。通过概率的方式随机生成新的个体。
选择:
代码
import numpy as np
import random as rd
import matplotlib.pyplot as plt
import copy
from benchmark import benchmark_func, benchmark_range
from math import *
class DE:
def __init__(self, size, dim, maxgen, bound, param):
self.size = size
self.maxgen = maxgen
self.dim = dim
self.bound = bound
self.param = param
self.p = np.zeros((size, dim))
self.m = np.zeros((size, dim)) # 变异坐标
self.c = np.zeros((size, dim)) # 交叉坐标
self.bp = [0.0 for i in range(dim)] # bp 为最优解的 坐标 1 行
self.f = np.zeros((size, 1)) # f 为初始点的适应度函数值 n 行 1 列 数组
self.trace = [] # trace 为轨迹 记录 每次迭代的最佳适应度函数 列表类型
def begin(self):
for i in range(0, self.size):
# for j in range(0,self.dim):
self.p[i] = self.bound[0] + rd.uniform(0, 1) * (self.bound[1] - self.bound[0])
self.f[i] = self.fitness(self.p[i])
# print(self.p)
# print(self.f)
def fitness(self, x):
# 用的是病态函数
f = benchmark_fun(i)
return f
def bianyi(self, t):
for i in range(t):
'''z=exp(1-(self.maxgen/(self.maxgen+1-t)))
self.param[0]=0.25*2**z'''
self.param[0] = 0.8 - t * (0.8 - 0.4) / self.maxgen
for i in range(self.size):
r1 = r2 = r3 = 0
while r1 == i or r2 == i or r3 == i or r2 == r1 or r3 == r1 or r3 == r2:
r1 = rd.randint(0, self.size - 1) # 随机数范围为[0,size-1]的整数
r2 = rd.randint(0, self.size - 1)
r3 = rd.randint(0, self.size - 1)
self.m[i] = self.p[r1] + (self.p[r2] - self.p[r3]) * self.param[0]
for j in range(self.dim):
if self.m[i, j] < self.bound[0]:
self.m[i, j] = self.bound[0]
if self.m[i, j] > self.bound[1]:
self.m[i, j] = self.bound[1]
def crossover(self):
for i in range(self.size):
for j in range(self.dim):
if rd.uniform(0, 1) <= self.param[1] or j == rd.randint(0, self.size - 1):
self.c[i, j] = self.m[i, j]
else:
self.c[i, j] = self.p[i, j]
def selection(self):
for i in range(self.size):
if self.fitness(self.c[i]) <= self.fitness(self.p[i]):
self.bp = self.c[i] #
self.p[i] = self.c[i]
else:
self.bp = self.bp
self.p[i] = self.p[i]
def run(self):
self.begin()
# print((self.p))
bestindex = np.where(self.f == np.min(self.f))
# print('最佳索引为:',bestindex)
self.bp = self.p[bestindex] # 初始化后默认的最佳一行的位置
'''for i in range(self.size):
if self.fitness(self.p[i])<=self.fitness(self.bp):
self.bp=self.p[i]
print((self.bp))'''
for t in range(self.maxgen):
self.bianyi(t)
self.crossover()
self.selection()
if self.fitness(self.bp) < 1e-2:
print('达到最佳的迭代次数:', t)
'''if self.fitness(self.bp)+19.2085<1e-4:
print('达到最佳的迭代次数:',t)'''
self.trace.append(self.fitness(self.bp))
# print(self.trace)
# print(self.flist)
# print(self.fitness(self.mm))
return self.trace
def main():
for i in range(12):
de = DE(size=50, dim=30, maxgen=1000, bound=benchmark_range(i), param=[0.8, 0.6])
de.run()
#
print('=' * 40)
print('= Optimal solution:')
print('= x=', de.bp[0])
print('= y=', de.bp[1])
print('= Function value:')
print('= f(x,y)=', de.fitness(de.bp))
# print(np.shape(de.bp))
print('=' * 40)
# print(de.flist)
# print(de.bp)
#
plt.plot(de.trace, 'r')
title = 'MIN: ' + str(de.fitness(de.bp))
plt.title(title)
plt.xlabel("Number of iterations")
plt.ylabel("Function values")
plt.show()
if __name__ == "__main__":
main()
结论
性能对比
实验结果
(1 )编码标准:GA 采用二进制编码, PSO 、 DE 采用实数编码 。近年来许多 学者通过整数编码将 GA 算法 、 PSO 算法应用与求解离散型问题,特别是 0-1 非线性优化为题, 整数规划问题 、混合整数规划问题, 而离散的 DE 算法则研 究的比较少,而采用混合编码技术的 DE 算法则研究更少。
(2)参数设置问题:GA 和 PSO 算法的参数过多,不同的参数设置对最终结果影响也比较大 。 DE 算法主要有两个参数要调整,参数设置对结果影响不太明显, 因此更容易使用。
(3)高维问题:GA 对高维问题收敛速度很慢甚至很难收敛,但是 PSO 和 DE 则能很好解决。尤其是 DE算法,收敛速度很快而且结果很精确。高维问题在实际问题中, 由于转化为个体的向量维数非常高, 因此算法对高维问题的处理将是很重要的 。只有很好的处理高维问题,算法才能很好的应用于实际问题。
(4)收敛性能:对于优化问题,相对 GA,DE 和 PSO 算法收敛速度比较快, 但 PSO易陷入局部最优解,且算法不稳定。
(5)应用广泛性:由于 GA 算法发明比较早, 因此应用领域比较广泛; PSO 自从发明以来, 已成为研究热点问题,应用较多; 而 DE 算法近几年才引起人 们的关注而且算法性能好,因此应用领域即将增多。