整体框架

基本解、可行解、最优解、局部最优解

适应度函数

分类:
一般来说,智能优化算法可以分为四大类

  • 进化类算法(Evolutionary Algorithms):由生物进化机制启发得到,包含交叉、变异、选择等操作,典型代表为遗传算法(Genetic Algorithm)和差分进化算法(Differential Evolution)。

  • 群智能类算法(Swarm-based Algorithms):通过使用单个生物个体作为搜索代理来模仿生物集群的集体行为,典型代表为蚁群算法(Ant Colony Optimization)、粒子群算法(Particle Swarm Optimization)和人工蜂群算法(Artificial Bee Colony Optimization)。

  • 物理法则类算法(Physics-based Algorithms):由物理法则启发得到,将客观规律转化为算法流程,典型代表为模拟退火算法(Simulated Annealing)和引力搜索算法(Gravitational Search Algorithm)。

  • 其他类算法(Others):由自然现象、数学原理、人造活动等启发得到,例如禁忌搜索(Tabu Search )、和声搜索(Harmony Search )、免疫算法(Immune Algorithm)。

遗传算法

遗传算法(Genetic Algorithm,GA)在20世纪六七十年代由美国密歇根大学的 Holland教授创立。是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型。是一种通过模拟自然进化过程搜索最优解的方法。

原理与步骤

生物在其延续生存的过程中,逐渐适应其生存环境,使得其品质不断得到改良,这种生命现象称为进化(Evolution)。生物的进化是以团体的形式共同进行的,这样的一个团体称为群体(Population),组成群体的单个生物称为个体(Individual),每个个体对其生存环境都有不同的适应能力,这种适应能力称为个体的适应度(Fitness)。

此外,每个生物/个体还有的遗传和变异:

生物从其亲代继承特性或性状,这种生命现象就称为遗传(Heredity)。控制生物遗传的物质单元称为基因(Gene),它是有遗传效应的DNA片段。细胞在分裂时,遗传物质DNA通过复制(Reproduction)而转移到新产生的细胞中,新细胞就继承了旧细胞的基因。

有性生物在繁殖下一代时,两个同源染色体之间遗过 交叉(Crossover) 而重组。

在进行复制时,可能以很小的概率产生某些差错,从而使DNA发生某种变异(Mutation),产生出新的染色体。

最终,按照达尔文的讲化论,那些具有较强适应环境变化能力的生物个体具有更高的生存能力容易存活下来,并有较多的机会产生后代;相反,具有较低生存能力的个体则被淘汰,或者产生后代的机会越来越少,直至消亡。即 自然选择,适者生存


遗传算法的一般步骤总结如下:

  1. 随机产生种群。
  2. 根据策略判断个体的适应度,是否符合优化准则,若符合,输出最佳个体及其最优解,结束。否则,进行下一步。
  3. 依据适应度选择父母,适应度高的个体被选中的概率高,适应度低的个体被淘汰。
  4. 用父母的染色体按照一定的方法进行交叉,生成子代。
  5. 对子代染色体进行变异
  6. 由交叉和变异产生新一代种群,返回步骤2,直到最优解产生。

遗传算法的基本流程

编码、交叉与变异

编码方式

在遗传算法中,染色体是解空间中的解的表示形式。从问题的解到基因型的映射称为编码即把一个问题的可行解从其解空间转换到遗传算法的搜索空间的转换方法

常见的编码方法有二进制编码格雷码编码、 浮点数编码各参数级联编码多参数交叉编码等。

  • 二进制编码:即组成染色体的基因序列是由二进制数表示,具有编码解码简单易用,交叉变异易于程序实现等特点。

  • 格雷编码:两个相邻的数用格雷码表示,其对应的码位只有一个不相同,从而可以提高算法的局部搜索能力。这是格雷码相比二进制码而言所具备的优势。

  • 浮点数编码:是指将个体范围映射到对应浮点数区间范围,精度可以随浮点数区间大小而改变。

交叉策略

交叉操作是指从种群中随机选择两个个体,通过两个染色体的交换组合,把父串的优秀特征遗传给子串,从而产生新的优秀个体。

变异策略

为了防止遗传算法在优化过程中陷入局部最优解,在搜索过程中,需要对个体进行变异。遗传算法中的个体变异运算是指个体染色体编码串中的某些基因座上的基因值用该基因座上的其余等位基因来替换,从而构成新的个体。基因位突变(Simple Mutation)、(非)均匀变异(Uniform Mutation)、边界变异(Boundary Mutation)和高斯近似变异等几种变异方法是针对二进制编码的个体基因突变的首选。

在实际应用中,主要采用单点变异,也叫位变异,即只需要对基因序列中某一个位进行变异,以二进制编码为例,即0变为1,而1变为0

轮盘赌选择

轮盘赌选择(Roulette Wheel Selection)、随机竞争选择(Stochastic Tournament)、最佳保留选择(Best Retention Selection)、无回放随机选择以及一些排序方式一般作为选择函数的选项。

完整代码模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
 # 设置种群类
class Population:
# 初始化种群
def __init__(self,num,cross_p,mutation_p,max_gen):
self.num = num
self.cp = cross_p
self.mp = mutation_p
self.gennum = max_gen
self.oldsol = []
self.newsol = []
self.sp = [[] for i in range(self.num)]
self.fitness = [[] for i in range(self.num)]
self.init_sol()
self.bestsol = self.oldsol[0]
self.bestf = 0

pass
# 随机产生初始解
def init_sol(self):
pass

# 对旧个体和新个体进行比较得出适应度函数值
def compare(self):
for i in range(0,self.num):
self.f[i] = func(self.oldsol[i]))
f_sum = sum(self.f)
for i in range(0,self.num):
self.sp[i] = self.f[i]/f_sum

# 个体选择
def select(self):
point = 0;
t = random.random()
for p in self.sp:
if p > t:
break
point += 1
if point >= self.num:
point = 0
return point

# 交叉操作
def cross(self,sol1,sol2):
p = random.random()
if sol1 != sol2 and p < self.cp:
pass
return sol1,sol2

# 变异操作
def mutate(self,sol0):
p = random.random()
if p < self.mp:
pass
return sol0

# 种群进化
def run(self):
# 适应度
self.compare()
for i in range(0,self.num,2):
# 选择
sol1 = self.oldsol[self.select()]
sol2 = self.oldsol[self.select()]
# 交叉和变异
sol1,sol2 = self.cross(sol1,sol2)
sol1 = self.mutate(sol1)
sol2 = self.mutate(sol2)
# 加入新个体
self.newsol[i] = sol1
self.newsol[i+1] = sol2

# 保存当前最优解
flag = -1
for i in range(0,self.num):
if self.bestf < self.f[i]:
flag = i
self.bestf = self.f[i]
if flag >= 0:
self.bestsol = self.oldsol[flag]

# 更换个体
for i in range(0,self.num):
self.oldsol[i] = self.newsol[i]

# 获取最终解
def get_sol(self):
for i in range(0,self.gennum):
self.run()
return self.bestsol

if __name__ == '__main__':

start=timeit.default_timer()
pop = Population(2,0.8,0.1,200)
ans = pop.get_sol()
end=timeit.default_timer()
print(ans)
print('运行时间: %s Seconds'%(end-start))

粒子群算法

1995年,受到鸟群觅食行为的规律性启发,James Kennedy 和 Russell Eberhart 建立了一个简化算法模型,经过多年改进最终形成了粒子群优化算法 (Particle Swarm Optimization, PSO)。

基本思想

设想这样一个场景:鸟群在森林中随机搜索食物,它们想要找到食物量最多的位置(全局最优解)。但是所有的鸟都不知道食物具体在哪个位置,只能感受到食物大概在哪个方向
每只鸟沿着自己判定的方向进行搜索,并在搜索的过程中记录自己曾经找到过食物且量最多的位置,同时所有的鸟都共享自己每一次发现食物的位置以及食物的量,这样鸟群就知道当前在哪个位置食物的量最多。
在搜索的过程中每只鸟都会根据自己记忆中食物量最多的位置和当前鸟群记录的食物量最多的位置调整自己接下来搜索的方向。鸟群经过一段时间的搜索后就可以找到森林中哪个位置的食物量最多。

粒子抽象

粒子群算法通过设计一种无质量的粒子来模拟鸟群中的鸟,粒子的位置可以延伸到NN 维空间。

粒子仅具有两个属性:速度和位置,速度代表移动的快慢,位置代表移动的方向。

规定第ii 个粒子的位置为Xi=(xi1,..,xiD)X_i=(x_{i1},..,x_{iD}),飞行速度为ViV_i 以及它搜索到的最优位置(个体最优解)PiP_i 和对应的适应值fif_i 。而群体最优解记为PgP_g,使适应值fg=maxi=1,..,Nfif_g=\max\limits_{i=1,..,N}f_i。其中NN 为粒子总数。max\max 根据最优化情况也可以是min\min

值得注意的是,虽然遵循着鸟群的物理特性将ViV_i 称为速度,但实际上这个变量是一个位置增量,即下一步第ii 个粒子的位置被更新为:

Xi(k+1)=Xi(k)+Vi(k+1)X^{(k+1)}_i=X^{(k)}_i+V^{(k+1)}_i

其中,速度的迭代公式被设计如下:

Vi(k+1)=ωVi(k)+c1r1(Pi(k)Xi(k))+c2r2(Pg(k)Xi(k))V^{(k+1)}_i=\omega V^{(k)}_i+c_1r_1\left(P^{(k)}_i-X^{(k)}_i\right)+c_2r_2\left(P^{(k)}_g-X^{(k)}_i\right)

通常,我们把迭代更新的

  • 第一项称为惯性部分ω\omega 称为惯性权重,一般初始化为0.90.9 然后线性递减到0.40.4
  • 第二项称为认知部分、第三项称为社会部分c1,c2c_1,c_2 称为加速系数,也叫学习因子,一般取2.02.0。最后r1,r2r1,r2 是取值范围在[0,1][0,1] 的相互独立的随机数。
  • 一般用户需要手动设置VmaxV_{\max} 来限制速度的范围,一般根据每一维的取值范围取 10% 到 20%。

最终,整个粒子群算法的流程如下图所示:

粒子群算法流程图

Python实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# coding: utf-8
import numpy as np
import random
import matplotlib.pyplot as plt

# ----------------------PSO参数设置---------------------------------
class PSO():
def __init__(self, pN, dim, max_iter):
self.w = 0.8
self.c1 = 2
self.c2 = 2
self.r1 = 0.6
self.r2 = 0.3
self.pN = pN # 粒子数量
self.dim = dim # 搜索维度
self.max_iter = max_iter # 迭代次数
self.X = np.zeros((self.pN, self.dim)) # 所有粒子的位置和速度
self.V = np.zeros((self.pN, self.dim))
self.pbest = np.zeros((self.pN, self.dim)) # 个体经历的最佳位置和全局最佳位置
self.gbest = np.zeros((1, self.dim))
self.p_fit = np.zeros(self.pN) # 每个个体的历史最佳适应值
self.fit = 1e10 # 全局最佳适应值

# ---------------------目标函数-----------------------------
def function(self, X):
return X**2-4*X+3

# ---------------------初始化种群----------------------------------
def init_Population(self):
for i in range(self.pN):
for j in range(self.dim):
self.X[i][j] = random.uniform(0, 1)
self.V[i][j] = random.uniform(0, 1)
self.pbest[i] = self.X[i]
tmp = self.function(self.X[i])
self.p_fit[i] = tmp
if tmp < self.fit:
self.fit = tmp
self.gbest = self.X[i]


# ----------------------更新粒子位置----------------------------------
def iterator(self):
fitness = []
for t in range(self.max_iter):
for i in range(self.pN): # 更新gbest\pbest
temp = self.function(self.X[i])
if temp < self.p_fit[i]: # 更新个体最优
self.p_fit[i] = temp
self.pbest[i] = self.X[i]
if self.p_fit[i] < self.fit: # 更新全局最优
self.gbest = self.X[i]
self.fit = self.p_fit[i]
for i in range(self.pN):
self.V[i] = self.w * self.V[i] + self.c1 * self.r1 * (self.pbest[i] - self.X[i]) + \
self.c2 * self.r2 * (self.gbest - self.X[i])
self.X[i] = self.X[i] + self.V[i]
fitness.append(self.fit)
print(self.X[0], end=" ")
print(self.fit) # 输出最优值
return fitness


# ----------------------程序执行-----------------------

my_pso = PSO(pN=30, dim=1, max_iter=100)
my_pso.init_Population()
fitness = my_pso.iterator()

# -------------------画图--------------------
plt.figure(1)
plt.title("Figure1")
plt.xlabel("iterators", size=14)
plt.ylabel("fitness", size=14)
t = np.array([t for t in range(0, 100)])
fitness = np.array(fitness)
plt.plot(t, fitness, color='b', linewidth=3)
plt.show()

蚁群算法

生物学家发现,自然界中的蚁群觅食是一种群体性行为,并非单只蚂蚁自行寻找食物源。蚂蚁在寻找食物源时,会在其经过的路径上释放一种信息素(Pheromone),并能够感知其它蚂蚁释放的信息素。信息素浓度的大小表征到食物源路径的远近,信息素浓度越高,表示对应的路径距离越短。

通常,蚂蚁会以较大的概率优先选择信息素浓度较高的路径,并释放一定量的信息素,以增强该条路径上的信息素浓度,这样会形成一个正反馈。最终,蚂蚁能够找到一条从巢穴到食物源的最佳路径,即最短距离。值得一提的是,生物学家同时发现,路径上的信息素浓度会随着时间的推进而逐渐衰减。如下图所示:

蚁群算法示意图

20世纪90年代初,意大利学者 M.Dorigo 等人就是通过对这种群体智能行为的抽象建模,提出了蚁群优化算法(Ant Colony Optimization, ACO),为最优化问题、尤其是组合优化问题的求解提供了一强有力的手段。

路径创建

M.Dorigo 等人最早将蚁群算法用于解决旅行商问题(Traveling Salesman Problem,TSP),并取得了较好的实验结果。为此,这里也以 TSP 为主要问题来对ACO的基本流程和数学模型进行阐述。

设整个蚂蚁群体中蚂蚁的数量为 mm ,城市的数量为nn ,城市与城市之间的距离为dijd_{ij},在tt 时刻城市ii 与城市jj 连接路径上的信息素浓度为τij(t)\tau_{ij}(t)。初始时刻,各个城市间连接路径上的信息素浓度相同,不妨设τij(0)=τ0,  i,j=1,..,n\tau_{ij}(0)=\tau_0,\;\forall i,j=1,..,n

Pij(k)(t)P_{ij}^{(k)}(t) 表示tt 时刻蚂蚁k(k=1,..,m)k\quad(k=1,..,m) 从城市ii 转移到城市jj转移概率,定义:

Pij(k)(t)={[τij(t)]α×[ηij(t)]βsallowedk[τis(t)]α×[ηis(t)]βif jallowedk0otherwiseP_{ij}^{(k)}(t)=\begin{cases} \begin{aligned}&\frac{[\tau_{ij}(t)]^\alpha\times[\eta_{ij}(t)]^\beta}{\sum\limits_{s\in allowed_k}[\tau_{is}(t)]^\alpha\times[\eta_{is}(t)]^\beta}&\text{if }j\in allowed_k\\ &0&\text{otherwise}\end{aligned} \end{cases}

其中,

  • ηij(t)=1/dij\eta_{ij}(t)=1/d_{ij} 为启发式函数,表示蚂蚁从城市转移到城市的期望程度;
  • allowedkallowed_k 为蚂蚁kk 待访问城市的集合,开始时,allowedkallowed_k 中有(n1)(n-1) 个元素,即除了蚂蚁kk 出发城市的所有其它城市,随着时间的推进,allowedkallowed_k 中的元素不断减少,直至为空,即表示所有的城市均访问完毕;
  • α\alpha 为信息素重要程度因子,其值越大,表示信息素的浓度在转移中起的作用越大;
  • β\beta 为启发函数重要程度因子,其值越大,表示启发函数在转移中的作用越大,即蚂蚁会以较大的概率转移到距离短的城市。

α=0\alpha=0 时,算法就是传统的贪心算法,最邻近城市被选中的概率最大。
β=0\beta=0 时,就成了纯粹的正反馈的启发式算法,蚂蚁完全只根据信息素浓度确定路径,算法将快速收敛,这样构建出的最优路径往往与实际目标有着较大的差异,算法的性能比较糟糕。

最终经过nn 个时刻,蚂蚁可以走完所有的城市,完成一次迭代,每只蚂蚁所走过的路径就是一个解。

信息素挥发

由于蚂蚁释放信息素的同时,各个城市间连接路径上的信息素逐渐消失,设参数ρ(0,1)\rho\in(0,1) 表示信息素的挥发程度。则当所有蚂蚁完成一次循环后,各个城市间连接路径上的信息素浓度需进行实时更新,即:

τij(t+1)=(1ρ)τij(t)+ΔτijΔτij=k=1mΔτij(k)\begin{aligned} \tau_{ij}(t + 1) &= (1 - \rho ) \cdot \tau_{ij}(t) + \Delta \tau_{ij}\\ \Delta \tau_{ij} &= \sum_{k = 1}^{m}\Delta \tau_{ij}^{(k)} \end{aligned}

其中,Δτij(k)\Delta\tau_{ij}^{(k)} 表示第kk 只蚂蚁在城市ii 与城市jj 连接路径上释放的信息素浓度;Δτij\Delta\tau_{ij} 表示在城市ii 与城市jj 连接路径上所释放的总的信息素浓度。

而针对Δτij(k)\Delta\tau_{ij}^{(k)} 的公式,即蚂蚁释放多大的信息素问题,M.Dorigo 等人曾给出 3 种不同的模型,分别是Ant Cycle System、Ant Quantity System和Ant Density System。

  1. Ant Cycle System模型利用蚂蚁经过路径的整体信息(经过路径的总长)计算释放的信息素浓度;
  2. Ant Quantity System模型利用蚂蚁经过路径的局部信息(经过各个城市间的距离)计算释放的信息素浓度;
  3. Ant Density System模型更为简单地将信息素释放的浓度取为恒值,并没有考虑不同蚂蚁经过路径长度的影响。

通常,选用Ant Cycle System计算释放的信息素浓度,即蚂蚁经过的路径越短,释放的信息素浓度越高。其公式为:

Δτij(k)(t)={1Ckif the k-th ant traverses (i,j)0otherwise\Delta\tau_{ij}^{(k)}(t)=\left\{\begin{aligned} &\frac{1}{C_k}&\text{if the $k$-th ant traverses }(i, j)\\ &0&\text{otherwise} \end{aligned}\right.

其中,CkC_k 表示第kk 只蚂蚁走完整条路经后所得到的总路径长度。相当于每只蚂蚁将11 单位的信息素均匀撒在它所经过的路径上。这个单位信息素的量可以改成其他常数QQ ,即分子换成QQ

算法流程

Note: 在选择下一个访问的城市时,与遗传算法一样,同样根据轮盘赌的方法选择

人工蜂群算法

人工蜂群算法(Artificial Bee Colony,ABC) 是由土耳其学者Karaboga 于 2005 年提出的一种新颖的基于群智能的全局优化算法,其直观背景来源于蜂群的采蜜行为,蜜蜂根据各自的分工进行不同的活动,并实现蜂群信息的共享和交流,从而找到问题的最优解。

生物背景

蜜蜂是一种群居昆虫,自然界中的蜜蜂总能在任何环境下以极高的效率找到优质蜜源,且能适应环境的改变。蜜蜂群的采蜜系统主要由蜜源、采蜜蜂、跟随蜂、侦查蜂等组成(某些资料有雇佣蜂、非雇佣蜂之分):

  • 蜜源:蜜源的位置(假设有DD 维,共有SNSN 个)就是待求优化问题的可行解,是人工蜂群算法中所要处理的基本对象。蜜源的优劣用实际问题的适应度(目标函数)来评价。
  • 采蜜蜂:采蜜蜂(假设有SNSN 个)采用贪婪准则,比较记忆中的最优解和邻域搜索解,当搜索解优于记忆最优解时,替换记忆解;反之,保持不变。在所有的采蜜蜂完成邻域搜索后,采蜜蜂回蜂房跳摆尾舞与跟随蜂共享蜜源信息
  • 跟随蜂:跟随峰的数量与采蜜蜂相同(假设有SNSN 个),根据采蜜蜂的蜜源信息以一定概率选择采蜜源,蜜量大的采蜜蜂吸引跟随蜂的概率大于蜜量小的采蜜蜂。同样,跟随蜂在采蜜源附近邻域搜索,采用贪婪准则,比较跟随蜂搜索解与原采蜜蜂的解,当搜索解优于原采蜜蜂的解时,替换原采蜜蜂的解,完成角色互换;反之,保持不变。
  • 侦查蜂:若某处蜜源陷入局部最优,采用侦查蜂大步长搜索的方式探索最优解。

基本原理

蜂群初始化

根据第j  (j=1,2,..,D)j\;(j=1,2,..,D) 个维度(蜜源的位置分量)的上下界xmax,jx_{\max,j}xmin,jx_{\min,j} 随机生成SNSN 个蜜源,其中第ii 个蜜源可以表示为Xi={xi1,xi2,..,xiD}X_i=\{x_{i1},x_{i2},..,x_{iD}\},且:

xij=xmin,j+rand[0,1](xmax,jxmax,j),i=1,2,..,SNx_{ij}=x_{\min,j}+\text{rand}[0,1](x_{\max,j}-x_{\max,j}),\quad i=1,2,..,SN

rand[0,1]\text{rand}[0,1] 为介于[0,1][0,1] 的随机数。

采蜜蜂阶段

开始搜索时,第ii 只采蜜蜂在第ii 个蜜源周围进行作业,具体来说是随机选取第k,  kik,\;k\neq i 个蜜源,然后产生新蜜源Vi={vi1,vi2,..,viD}V_i=\{v_{i1},v_{i2},..,v_{iD}\},其中:

vij=xij+ϕij(xijxkj),j=1,2,..,Dv_{ij}=x_{ij}+\phi_{ij}(x_{ij}-x_{kj}),\quad j=1,2,..,D

ϕij\phi_{ij} 为介于[1,1][-1,1] 的随机数。

比较ViV_iXiX_i 的适应度,将较大的一方代替原来的蜜源成为新的XiX_i

跟随蜂阶段

跟随峰阶段,采蜜蜂在舞蹈区分享蜜源信息。跟随蜂分析这些信息,采用轮盘赌策略来选择蜜源跟踪开采,以保证适应值更高的蜜源开采的概率更大。

pi=fitii=jSNfitjp_i=\frac{\text{fit}_i}{\sum_{i=j}^{SN}\text{fit}_j}

跟随蜂开采过程与雇佣蜂一样,生成新蜜源并留下更优适应度的蜜源。

蜜源拥有参数trial\text{trial},用于统计当前蜜源未被更新的次数。当蜜源更新被保留时,trial=0\text{trial}=0;反之,trialtrial+1\text{trial}\leftarrow\text{trial}+1。当trial\text{trial} 的值超过预先设置的阈值limit\text{limit} 时,需要抛弃该蜜源,来到侦查蜂阶段。

侦查蜂阶段

侦查蜂阶段,第ii 个蜜源迟迟没有得到更新,则原来的采蜜蜂化身侦查蜂在搜索空间重新生成一个蜜源,随机生成蜜源的方式与初始化相同:

xij=xmin,j+rand[0,1](xmax,jxmax,j),i=1,2,..,SNx_{ij}=x_{\min,j}+\text{rand}[0,1](x_{\max,j}-x_{\max,j}),\quad i=1,2,..,SN

Python实现

整个算法流程就是上述三个阶段的不断迭代。下面以计算函数

y=14000i=1nxi2i=1ncos(xii)+1y=\frac1{4000}\sum_{i=1}^nx_i^2-\prod_{i=1}^n\cos\left(\frac{x_i}{\sqrt i}\right)+1

的最小值为例,进行人工蜂群算法的优化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import numpy as np
import random, math, copy
import matplotlib.pyplot as plt

def GrieFunc(data): # 目标函数
s1 = 0.
s2 = 1.
for k, x in enumerate(data):
s1 = s1 + x ** 2
s2 = s2 * math.cos(x / math.sqrt(k+1))
y = (1. / 4000.) * s1 - s2 + 1
return 1. / (1. + y)

class ABSIndividual:
def __init__(self, bound):
self.score = 0.
self.invalidCount = 0 # 无效次数(成绩没有更新的累积次数)
self.chrom = [random.uniform(a,b) for a,b in zip(bound[0,:],bound[1,:])] #随机初始化

self.calculateFitness()

def calculateFitness(self):
self.score = GrieFunc(self.chrom) #计算当前成绩

class ArtificialBeeSwarm:
def __init__(self, foodCount, onlookerCount, bound, maxIterCount=1000, maxInvalidCount=200):
self.foodCount = foodCount #蜜源个数,等同于雇佣蜂数目
self.onlookerCount = onlookerCount #观察蜂个数
self.bound = bound #各参数上下界
self.maxIterCount = maxIterCount #迭代次数
self.maxInvalidCount = maxInvalidCount #最大无效次数
self.foodList = [ABSIndividual(self.bound) for k in range(self.foodCount)] #初始化各蜜源
self.foodScore = [d.score for d in self.foodList] #各蜜源最佳成绩
self.bestFood = self.foodList[np.argmax(self.foodScore)] #全局最佳蜜源

def updateFood(self, i): #更新第i个蜜源
k = random.randint(0, self.bound.shape[1] - 1) #随机选择调整参数的维度
j = random.choice([d for d in range(self.bound.shape[1]) if d !=i]) #随机选择另一蜜源作参考,j是其索引号
vi = copy.deepcopy(self.foodList[i])
vi.chrom[k] += random.uniform(-1.0, 1.0) * (vi.chrom[k] - self.foodList[j].chrom[k]) #调整参数
vi.chrom[k] = np.clip(vi.chrom[k], self.bound[0, k], self.bound[1, k]) #参数不能越界
vi.calculateFitness()
if vi.score > self.foodList[i].score: #如果成绩比当前蜜源好
self.foodList[i] = vi
if vi.score > self.foodScore[i]: #如果成绩比历史成绩好(如重新初始化,当前成绩可能低于历史成绩)
self.foodScore[i] = vi.score
if vi.score > self.bestFood.score: #如果成绩全局最优
self.bestFood = vi
self.foodList[i].invalidCount = 0
else:
self.foodList[i].invalidCount += 1

def employedBeePhase(self):
for i in xrange(0, self.foodCount): #各蜜源依次更新
self.updateFood(i)

def onlookerBeePhase(self):
foodScore = [d.score for d in self.foodList]
maxScore = np.max(foodScore)
accuFitness = [(0.9*d/maxScore+0.1, k) for k,d in enumerate(foodScore)] #得到各蜜源的 相对分数和索引号
for k in xrange(0, self.onlookerCount):
i = random.choice([d[1] for d in accuFitness if d[0] >= random.random()]) #随机从相对分数大于随机门限的蜜源中选择跟随
self.updateFood(i)

def scoutBeePhase(self):
for i in xrange(0, self.foodCount):
if self.foodList[i].invalidCount > self.maxInvalidCount: #如果该蜜源没有更新的次数超过指定门限,则重新初始化
self.foodList[i] = ABSIndividual(self.bound)
self.foodScore[i] = max(self.foodScore[i], self.foodList[i].score)

def solve(self):
trace = []
trace.append((self.bestFood.score, np.mean(self.foodScore)))
for k in range(self.maxIterCount):
self.employedBeePhase()
self.onlookerBeePhase()
self.scoutBeePhase()
trace.append((self.bestFood.score, np.mean(self.foodScore)))
print(self.bestFood.score)
self.printResult(np.array(trace))

def printResult(self, trace):
x = np.arange(0, trace.shape[0])
plt.plot(x, [(1-d)/d for d in trace[:, 0]], 'r', label='optimal value')
plt.plot(x, [(1-d)/d for d in trace[:, 1]], 'g', label='average value')
plt.xlabel("Iteration")
plt.ylabel("function value")
plt.title("Artificial Bee Swarm algorithm for function optimization")
plt.legend()
plt.show()

if __name__ == "__main__":
random.seed()
vardim = 25
bound = np.tile([[-600], [600]], vardim)
abs = ArtificialBeeSwarm(30, 30, bound, 1000, 200)
abs.solve()

灰狼优化算法

灰狼优化(Grey Wolf Optimization, GWO)算法于 2014 年由 Mirjalili 等人提出的一种基于群体智能的随机优化算法,该算法源于灰狼群中严格的社会层级制度和社会行为。通过模拟灰狼群体的寻食过程,实现对复杂优化问题的求解。通过灵活地调整群体中每只“狼”的位置和行为,以最小化或最大化给定问题的目标函数。

自然界的灰狼

自然界中灰狼常以群体的形式出没,每个群体的灰狼个数在5125\sim12 之间。
群体中,不同层级的灰狼负责不同的角色和职责,构成一种金字塔的结构。其中,αα 狼作为灰狼群体的领袖,具有主导作用,主要负责捕猎和休息地点的确认等活动。ββ 狼为领袖αα 狼的最佳候选者,及时协助αα 狼做出相关决定。当αα 狼出现空缺的情况时,ββ 狼将进行替补,代替αα 狼。δδ 狼处于第三层级,处于该层级的灰狼必须听从于αα 狼和ββ 狼的安排,但该层个体领导着处于最底层的ωω 狼。

如果在寻优过程中,αα 狼 和ββ 狼的适应度值(对应的目标函数值)较低,那么它们有几率会降为δδ 狼。ωω 狼必须服从以上三个层级灰狼个体的领导。

狩猎/寻优过程

在狩猎过程中,灰狼会采用跟踪包围的策略来追捕猎物。而这个行为正是 GWO 算法的核心思想。该行为可以用下式表示:

D=CXp(t)X(t)X(t+1)=Xp(t)AD\begin{aligned} D&=\vert C\odot X_p^{(t)}-X^{(t)}\vert\\ X^{(t+1)}&=X_p^{(t)}-A\odot D \end{aligned}

其中,上标(t),  (t+1)(t),\;(t+1) 分别表示第t,  t+1t,\;t+1 次迭代。XpX_p 表示猎物(prey)的位置,XX 表示灰狼的位置。A,  DA,\;D 被称为协同系数向量,由线性参数aa 和两个[0,1][0,1] 之间的随机数r1,  r2r_1,\;r_2构成:

A=2ar1aC=2r2a=22ttmax\begin{aligned} A&=2a\odot r_1-a\\ C&=2r_2\\ a&=2-\frac{2t}{t_{\max}} \end{aligned}

其中,aa 的数值随着迭代次数的增加从22 线性减小00


GWO算法正是利用这个数学模型。在每次迭代过程中,保留当前种群中的最好三只灰狼( α,β,δ\alpha,\beta,\delta ),然后根据它们的位置信息来更新其它狼(包括 ωω )的位置,继而在下一次迭代时找寻新的最好三只灰狼( α,β,δ\alpha,\beta,\delta )。

上述行为的数学表达如下:

  • 先计算三个最好狼与其他狼之间的距离

Dα=C1Xα(t)X(t)Dβ=C2Xβ(t)X(t)Dδ=C3Xδ(t)X(t)\begin{aligned} D_{\alpha}&=\vert C_1\odot X_{\alpha}^{(t)}-X^{(t)}\vert\\ D_{\beta}&=\vert C_2\odot X_{\beta}^{(t)}-X^{(t)}\vert\\ D_{\delta}&=\vert C_3\odot X_{\delta}^{(t)}-X^{(t)}\vert\\ \end{aligned}

  • 根据距离更新其他狼的位置

X1(t+1)=Xα(t)A1DαX2(t+1)=Xβ(t)A2DβX3(t+1)=Xδ(t)A3DδX(t+1)=13(X1(t+1)+X2(t+1)+X3(t+1))\begin{aligned} X_1^{(t+1)}&=X^{(t)}_{\alpha}-A_1\odot D_{\alpha}\\ X_2^{(t+1)}&=X^{(t)}_{\beta}-A_2\odot D_{\beta}\\ X_3^{(t+1)}&=X^{(t)}_{\delta}-A_3\odot D_{\delta}\\\\ X^{(t+1)}&=\frac13\left(X_1^{(t+1)}+X_2^{(t+1)}+X_3^{(t+1)}\right) \end{aligned}

整个算法流程如下图所示:
GWO算法流程图

收敛性分析

直观简单的收敛性分析:见优化算法笔记(十八)灰狼算法 - 简书
复杂且理论性的分析:待更

Python实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import random
import numpy

def GWO(objf, lb, ub, dim, SearchAgents_no, Max_iter):
'''objf: 目标函数;
lb, ub: 上下限;
dim: 维度;
SerchAgents_no: 搜索代理/狼的个数;
Max_iter: 最大迭代数;
'''

# 初始化 alpha, beta, and delta_pos
Alpha_pos = numpy.zeros(dim)
Alpha_score = float("inf")

Beta_pos = numpy.zeros(dim)
Beta_score = float("inf")

Delta_pos = numpy.zeros(dim)
Delta_score = float("inf")

# 将 lb, ub 扩展成 dim维 的向量
if not isinstance(lb, list):
lb = [lb] * dim
if not isinstance(ub, list):
ub = [ub] * dim

# 随机初始化所有狼的位置
Positions = numpy.zeros((SearchAgents_no, dim))
for i in range(dim):
Positions[:, i] = numpy.random.uniform(0, 1, SearchAgents_no) * (ub[i] - lb[i]) + lb[i]
Convergence_curve = numpy.zeros(Max_iter) # 存储每一次迭代在最优结果

#迭代寻优
for l in range(0, Max_iter):
for i in range(0, SearchAgents_no):

# 将迭代后超出搜索空间边界的位置规正
for j in range(dim):
Positions[i, j] = numpy.clip(Positions[i, j], lb[j], ub[
j]) # clip函数使大于a_max的元素值等于a_max,小于a_min的等于a_min

# 计算每个狼的目标函数
fitness = objf(Positions[i, :]) # 把某行数据带入函数计算

# 更新得到当前最优三匹狼 Update Alpha, Beta, and Delta
if fitness < Alpha_score:
Alpha_score = fitness # Update alpha
Alpha_pos = Positions[i, :].copy()

if (fitness > Alpha_score and fitness < Beta_score):
Beta_score = fitness # Update beta
Beta_pos = Positions[i, :].copy()

if (fitness > Alpha_score and fitness > Beta_score and fitness < Delta_score):
Delta_score = fitness # Update delta
Delta_pos = Positions[i, :].copy()


a = 2 - l * ((2) / Max_iter); # a从2线性减少到0

for i in range(0, SearchAgents_no):
for j in range(0, dim):
r1 = random.random() # r1 is a random number in [0,1]
r2 = random.random() # r2 is a random number in [0,1]

A1 = 2 * a * r1 - a;
C1 = 2 * r2;

# D_alpha表示候选狼与Alpha狼的距离
D_alpha = abs(C1 * Alpha_pos[j] - Positions[
i, j]);
X1 = Alpha_pos[j] - A1 * D_alpha;

r1 = random.random()
r2 = random.random()

A2 = 2 * a * r1 - a; #
C2 = 2 * r2;

D_beta = abs(C2 * Beta_pos[j] - Positions[i, j]);
X2 = Beta_pos[j] - A2 * D_beta;

r1 = random.random()
r2 = random.random()

A3 = 2 * a * r1 - a;
C3 = 2 * r2;

D_delta = abs(C3 * Delta_pos[j] - Positions[i, j]);
X3 = Delta_pos[j] - A3 * D_delta;

Positions[i, j] = (X1 + X2 + X3) / 3 # 候选狼的位置更新为根据Alpha、Beta、Delta得出的下一代灰狼位置。

Convergence_curve[l] = Alpha_score;

if (l % 1 == 0):
print(['迭代次数为' + str(l) + ' 的迭代结果' + str(Alpha_score)]); # 每一次的迭代结果


''' 下面用于测试 '''

#目标函数
def F1(x):
s=numpy.sum(x**2);
return s

#主程序
Max_iter = 1000
lb = -100
ub = 100
dim = 30
SearchAgents_no = 5
x = GWO(F1, lb, ub, dim, SearchAgents_no, Max_iter)

参考资料

  1. 全文六万字《计算智能》智能优化算法 张军【Python】-CSDN博客
  2. 遗传算法入门详解 - 知乎
  3. 遗传算法GA原理详解及实例应用 附Python代码_应用实例遗传算法-CSDN博客
  4. 用Python实现遗传算法 - 知乎
  5. Python粒子群优化算法实现PSO -CSDN博客
  6. 【优秀作业】蚁群优化算法 (qq.com)
  7. 蚁群优化算法(Ant Colony Optimization, ACO) - 知乎
  8. 优化算法——人工蜂群算法(ABC)(转发) - 知乎
  9. 人工蜂群算法代码实现 python_人工蜂群算法python代码-CSDN博客
  10. 【优化算法】简述灰狼优化算法(GWO)原理_灰狼算法-CSDN博客