文章目录
一、理论基础
1、非线性规划
非线性规划是20世纪50年代形成的一门新兴学科。1951年库恩和塔克发表的关于最优性条件(后来称为库恩·塔克条件)的论文是非线性规划诞生的标志。非线性规划研究一 n n n元实函数在一组等式或不等式的约束条件下的极值问题,非线性规划的理论来源于1951年库恩·塔克建立的最优条件。20世纪50年代,非线性规划的研究主要注重对梯度法和牛顿法的研究,以Davidon(1959)、Fletcher和Powell(1963)提出的DFP方法为代表。20世纪60年代侧重于对牛顿方法和共轭梯度法的研究,其中以由Broyden、Fletcher,Goldfarb 和Shanno从不同角度共同提出的BFGS方法为代表。20世纪70年代是非线性规划飞速发展时期,约束变尺度(SQP)方法(Han和Powell为代表)和Lagrange乘子法(代表人物是Powell和Hestenes)为这一时期的主要研究成果。20世纪80年代以来,随着计算机技术的快速发展,非线性规划方法取得了长足进步,在信赖域法、稀疏拟牛顿法、并行计算、内点法和有限储存法等领域取得了丰硕的研究成果。
2、非线性规划函数
函数fmincon是MATLAB最优化工具箱中求解非线性规划问题的函数,它从一个预估
值出发,搜索约束条件下非线性多元函数的最小值。
函数fmincon的约束条件为
min
f
(
x
)
=
{
c
(
x
)
≤
0
c
e
q
(
x
)
=
0
A
⋅
x
≤
b
A
e
q
⋅
x
=
b
e
q
l
b
≤
x
≤
u
b
(2-1)
\min f(x) = \begin{cases} c(x) \leq 0 \\ ceq(x) = 0 \\ A·x \leq b \\ Aeq·x = beq \\ lb \leq x \leq ub \\ \end{cases}\tag{2-1}
minf(x)=⎩
⎨
⎧c(x)≤0ceq(x)=0A⋅x≤bAeq⋅x=beqlb≤x≤ub(2-1)
其中,
x
、
b
、
b
e
q
、
I
b
和
u
b
是矢量;
A
和
A
e
q
为矩阵;
c
(
x
)
和
c
e
q
(
x
)
返回矢量的函数;
f
(
x
)
、
c
(
x
)
和
c
e
q
(
x
)
是非线性函数。
其中,x、b、beq、Ib和ub是矢量;A和Aeq为矩阵;c(x)和ceq(x)返回矢量的函数;f(x)、c(x)和ceq(x)是非线性函数。
其中,x、b、beq、Ib和ub是矢量;A和Aeq为矩阵;c(x)和ceq(x)返回矢量的函数;f(x)、c(x)和ceq(x)是非线性函数。
函数fmincon的基本用法为
x
=
f
m
i
n
c
o
n
(
f
u
n
,
x
0
,
A
,
b
)
x= fmincon(fun,x0,A,b)
x=fmincon(fun,x0,A,b)
x
=
f
m
i
n
c
o
n
(
f
u
n
,
x
0
,
A
,
b
,
A
e
q
,
b
e
q
)
x = fmincon(fun, x0, A,b, Aeq, beq)
x=fmincon(fun,x0,A,b,Aeq,beq)
x
=
f
m
i
n
c
o
n
(
f
u
n
,
x
0
,
A
,
b
,
A
e
q
,
b
e
q
,
l
b
,
u
b
)
x = fmincon(fun, x0, A,b, Aeq, beq, lb, ub)
x=fmincon(fun,x0,A,b,Aeq,beq,lb,ub)
x
=
f
m
i
n
c
o
n
(
f
u
n
,
x
o
,
A
,
b
,
A
e
q
,
b
e
q
,
l
b
,
u
b
,
n
o
n
l
c
o
n
)
x= fmincon(fun, xo,A, b, Aeq, beq, lb, ub, nonlcon)
x=fmincon(fun,xo,A,b,Aeq,beq,lb,ub,nonlcon)
x
=
f
m
i
n
c
o
n
(
f
u
n
,
x
o
,
A
,
b
,
A
e
q
,
b
e
q
,
l
b
,
u
b
,
n
o
n
l
c
o
n
,
o
p
t
i
o
n
s
)
x = fmincon(fun, xo, A,b, Aeq, beq, lb, ub, nonlcon, options)
x=fmincon(fun,xo,A,b,Aeq,beq,lb,ub,nonlcon,options)
其中,
n
o
n
l
c
o
n
nonlcon
nonlcon为非线性约束条件;
I
b
Ib
Ib和
u
b
ub
ub分别为
x
x
x的下界和上界。当函数输人参数不包括
A
、
b
、
A
e
q
、
b
e
q
时
,
默认
A
=
0
、
b
=
0
,
A
e
q
=
[
]
、
b
e
q
=
[
]
A、b、Aeq、beq时,默认A=0、b=0,Aeq=[]、beq=[]
A、b、Aeq、beq时,默认A=0、b=0,Aeq=[]、beq=[]。
x
0
x0
x0为
x
x
x的初设值。
3、遗传算法基本思想
1)、算法介绍
一些介绍可以不看
遗传算法是一类借鉴生物界自然选择和自然遗传机制的随机搜索算法,非常适用于处理传统搜索算法难以解决的复杂和非线性优化问题。目前,遗传算法已被广泛应用于组合优化、机器学习、信号处理、自适应控制和人工生命等领域,并在这些领域中取得了良好的成果。与传统搜索算法不同,遗传算法从随机产生的初始解开始搜索,通过一定的选择、交叉、变异操作逐步迭代以产生新的解。群体中的每个个体代表问题的一个解,称为染色体,染色体的好坏用适应度值来衡量,根据适应度的好坏从上一代中选择一定数量的优秀个体,通过交叉、变异形成下一代群体。经过若干代的进化之后,算法收敛于最好的染色体,它即是问题的最优解或次优解。
遗传算法提供了求解非线性规划的通用框架,它不依赖于问题的具体领域。遗传算法的优点是将问题参数编码成染色体后进行优化,而不针对参数本身,从而不受函数约束条件的限制;搜索过程从问题解的一个集合开始,而不是单个个体,具有隐含并行搜索特性,可大大减少陷人局部最小的可能性。而且优化计算时算法不依赖于梯度信息,且不要求目标函数连续及可导,使其适于求解传统搜索方法难以解决的大规模、非线性组合优化问题。
2)、算法执行过程
遗传算法是从代表问题可能潜在的解集的一个种群(population)开始的,而一个种群则由经过基因(gene)编码的一定数目的个体(individual)组成。每个个体实际上是染色体(chromosome)带有特征的实体。
染色体作为遗传物质的主要载体,即多个基因的集合,其内部表现(即基因型)是某种基因组合,它决定了个体的形状的外部表现,如黑头发的特征是由染色体中控制这一特征的某种基因组合决定的。因此,在一开始需要实现从表现型到基因型的映射即编码工作。由于仿照基因编码的工作很复杂,我们往往进行简化,如二进制编码。
初代种群产生之后,按照适者生存和优胜劣汰的原理,逐代(generation)演化产生出越来越好的近似解,在每一代,根据问题域中个体的适应度(fitness)大小选择(selection)个体,并借助于自然遗传学的遗传算子(genetic operators)进行组合交叉(crossover)和变异(mutation),产生出代表新的解集的种群。
这个过程将导致种群像自然进化一样的后生代种群比前代更加适应于环境,末代种群中的最优个体经过解码(decoding),可以作为问题近似最优解。
遗传算法过程图解:
3)、 相关生物学术语
为了大家更好了解遗传算法,在此之前先简单介绍一下相关生物学术语,大家了解一下即可。
生物遗传概念 | 遗传算法中的作用 |
---|---|
适者生存 | 算法停止时,最优目标值的可行解有最大的可能被留住 |
个体 | 可行解 |
染色体 | 可行解的编码 |
基因 | 可行解中每一分量的特征 |
适应性 | 适应度函数值 |
种群 | 根据适应度函数值选取的一组可行解 |
优胜劣汰 | 从种群中选择若干个个体。是一种基于适应度的优胜劣汰的过程。(选择) |
交配 | 通过交配原则产生一组新可行解的过程(交叉) |
变异 | 编码的某一分量发生变化的过程 |
4) 、实现流程
遗传算法中每一条染色体,对应着遗传算法的一个解决方案,一般我们用适应性函数(fitness function)来衡量这个解决方案的优劣。所以从一个基因组到其解的适应度形成一个映射。遗传算法的实现过程实际上就像自然界的进化过程那样。
下面我们用袋鼠跳中的步骤一一对应解释,以方便大家理解:
- 首先寻找一种对问题潜在解进行"数字化"编码的方案。(建立表现型和基因型的映射关系)
- 随机初始化一个种群(那么第一批袋鼠就被随意地分散在山脉上),种群里面的个体就是这些数字化的编码。
- 接下来,通过适当的解码过程之后(得到袋鼠的位置坐标)。
- 用适应度函数对每一个基因个体作一次适应度评估(袋鼠爬得越高当然就越好,所以适应度相应越高)
- 用选择函数按照某种规定择优选择(每隔一段时间,射杀一些所在海拔较低的袋鼠,以保证袋鼠总体数目持平)。
- 让个体基因变异(让袋鼠随机地跳一跳)。
- 然后产生子代(希望存活下来的袋鼠是多产的,并在那里生儿育女)。
遗传算法并不保证你能获得问题的最优解,但是使用遗传算法的最大优点在于你不必去了解和操心如何去“找"最优解。(你不必去指导袋鼠向那边跳,跳多远)。而只要简单的“否定"一些表现不好的个体就行了。(把那些总是爱走下坡路的袋鼠射杀,这就是遗传算法的精粹!)
由此我们可以得出遗传算法的一般步骤: - 随机产生种群。
- 根据策略判断个体的适应度,是否符合优化准则,若符合,输出最佳个体及其最优解,结束。否则,进行下一步。
- 依据适应度选择父母,适应度高的个体被选中的概率高,适应度低的个体被淘汰。
- 用父母的染色体按照一定的方法进行交叉,生成子代。
- 对子代染色体进行变异。
- 由交叉和变异产生新一代种群,返回步骤2,直到最优解产生。
5)、 实现细节
5.1、编码
编码是应用遗传算法时要解决的首要问题,也是设计遗传算法时的一个关键步骤。编码方法影响到交叉算子、变异算子等遗传算子的运算方法,很大程度上决定了遗传进化的效率。迄今为止人们已经提出了许多种不同的编码方法。总的来说,这些编码方法可以分为三大类:二进制编码法、浮点编码法、符号编码法。下面分别进行介绍。
5.1.1、二进制编码
就像人类的基因有AGCT4种碱基序列一样。不过在这里我们只用了0和1两种碱基,然后将它们串成一条链形成染色体。一个位能表示出2种状态的信息量,因此足够长的二进制染色体便能表示所有的特征。这便是二进制编码。如下:
它由二进制符号0和1所组成的二值符号集。它有以下一些优点:
- 编码、解码操作简单易行
- 交叉、变异等遗传操作便于实现
- 符合最小字符集编码原则
二进制编码的缺点是:对于一些连续函数的优化问题,由于其随机性使得其局部搜索能力较差,如对于一些高精度的问题,当解迫近于最优解后,由于其变异后表现型变化很大,不连续,所以会远离最优解,达不到稳定。例如:63和64对应的二进制分别为0111111和1000000。对应的7位数值均不同,因此该编码方式会导致变异后可能会出现远离最优解的情况,表现型不稳定。
5.1.2、浮点数编码
二进制编码虽然简单直观,一目了然。但是存在着连续函数离散化时的映射误差。个体长度较短时,可能达不到精度要求,而个体编码长度较长时,虽然能提高精度,但增加了解码的难度,使遗传算法的搜索空间急剧扩大。
所谓浮点法,是指个体的每个基因值用某一范围内的一个浮点数来表示。在浮点数编码方法中,必须保证基因值在给定的区间限制范围内,遗传算法中所使用的交叉、变异等遗传算子也必须保证其运算结果所产生的新个休的基因值也在这个区间限制范围内。如下所示:
它有以下一些优点:
- 精度高,是用于连续性问题。避免了汉明悬崖问题
- 适用于表示范围比较大的数值,适合空间较大的遗传搜索
- 降低了计算的复杂度,提高了效率
- 便于遗传算法与经典优化算法的混合使用
- 便于设计针对问题的专门知识的知识型遗传算子
- 便于处理复杂的决策变量约束条件,适用于组合优化问题。
举例展示:
如果有5个变量,其取值范围分别为[1,5]、[2,8]、[6,15]、[8,21]、[9,26]([]中前面的数表示该变量的最小值,后面的数表示该变量的最大值)。其编码方式为:
b
o
u
n
d
=
[
1
5
2
8
6
15
8
21
9
26
]
bound = \left[ \begin{matrix} 1&5 \\ 2&8 \\ 6&15 \\ 8&21 \\ 9&26 \\ \end{matrix} \right]
bound=⎣
⎡1268958152126⎦
⎤
r
e
t
=
b
o
u
n
d
(
:
,
1
)
′
+
(
b
o
u
n
d
(
:
,
2
)
−
b
o
u
n
d
(
:
,
1
)
)
′
.
∗
p
i
c
k
;
ret=bound(:,1)'+(bound(:,2)-bound(:,1))'.*pick;
ret=bound(:,1)′+(bound(:,2)−bound(:,1))′.∗pick;
此处采用了线性插值的方法,其中
b
o
u
n
d
bound
bound是数据范围矩阵,
b
o
u
n
d
(
:
,
1
)
bound(:,1)
bound(:,1)表示第一列的所有行,后面解释相同。
b
o
u
n
d
(
:
,
1
)
′
bound(:,1)'
bound(:,1)′表示对其进行转置,后面解释相同。
p
i
c
k
pick
pick表示一个
1
×
n
1 \times n
1×n的矩阵(n为变量长度,此例便为5),矩阵中的每一个值均在0-1之间。
r
e
t
ret
ret便为该问题的一个染色体编码。
对应的MATLAB代码如下:
pick=rand(1,length(lenchrom));%lenchrom为基因个数,即变量个数
ret=bound(:,1)'+(bound(:,2)-bound(:,1))'.*pick; %bound是数据范围矩阵线性插值,
%编码结果以实数向量存入ret中
5.2、评价个体的适应度
通过我们刚才的编码,将编码值反解,得出对应变量的值,将其带入适应度函数中(求目标函数最大值和最小值有所不同,求最大值时,一般适应度函数为目标函数;求最小值时,一般可以把目标函数取倒数当成其的适应度函数),适应度函数的值便为该个体的适应度。
5.3、选择(selection)
选择操作也叫复制操作,从群体中按个体的适应度函数值选择出较适应环境的个体。一般地说,选择将使适应度高的个体繁殖下一代的数目较多,而适应度较小的个体,繁殖下一代的数目较少,甚至被淘汰。下面介绍常用法的选择算子:
-
轮盘赌选择(Roulette Wheel Selection):是一种回放式随机采样方法。每个个体进入下一代的概率等于它的适应度值与整个种群中个体适应度值和的比例。选择误差较大。(最常用,下面将用此方法进行说明)
-
随机竞争选择(Stochastic Tournament):每次按轮盘赌选择一对个体,然后让这两个个体进行竞争,适应度高的被选中,如此反复,直到选满为止。
-
最佳保留选择:首先按轮盘赌选择方法执行遗传算法的选择操作,然后将当前群体中适应度最高的个体结构完整地复制到下一代群体中。
-
无回放随机选择(也叫期望值选择Excepted Value Selection):根据每个个体在下一代群体中的生存期望来进行随机选择运算。方法如下:
(1) 计算群体中每个个体在下一代群体中的生存期望数目N。
(2) 若某一个体被选中参与交叉运算,则它在下一代中的生存期望数目减去 0.5,若某一个体未被选中参与交叉运算,则它在下一代中的生存期望数目减去1.0。
(3) 随着选择过程的进行,若某一个体的生存期望数目小于0时,则该个体就不再有机会被选中。 -
确定式选择:按照一种确定的方式来进行选择操作。具体操作过程如下:
(1)计算群体中各个个体在下一代群体中的期望生存数目N。
(2)用N的整数部分确定各个对应个体在下一代群体中的生存数目。
(3)用N的小数部分对个体进行降序排列,顺序取前M个个体加入到下一代群体中。至此可完全确定出下一代群体中M个个体。 -
无回放余数随机选择:可确保适应度比平均适应度大的一些个体能够被遗传到下一代群体中,因而选择误差比较小。
-
均匀排序:对群体中的所有个体按期适应度大小进行排序,基于这个排序来分配各个个体被选中的概率。
-
最佳保存策略:当前群体中适应度最高的个体不参与交叉运算和变异运算,而是用它来代替掉本代群体中经过交叉、变异等操作后所产生的适应度最低的个体。
-
随机联赛选择:每次选取几个个体中适应度最高的一个个体遗传到下一代群体中。
-
排挤选择:新生成的子代将代替或排挤相似的旧父代个体,提高群体的多样性。
最通常的实现方法是轮盘赌(roulette wheel)模型。(其他选择算子大家可以自行上网找资料查看。)令 ∑ f i \sum f_i ∑fi表示群体的适应度值之总和, f i f_i fi表示种群中第 i i i个染色体的适应度值,它被选择的概率正好为其适应度值所占份额 f i f_i fi/ ∑ f i \sum f_i ∑fi。
P i = f i / ∑ i = 1 n f i P_i = f_i/\sum^{n}_{i=1}f_i Pi=fi/i=1∑nfi
如果适应度为 存在负数的话,我们可以采用下面的公式:
f i ′ = f i − min f i max f i − min f i f'_i = \frac {f_i-\min f_i}{\max f_i-\min f_i} fi′=maxfi−minfifi−minfi
再计算概率和累计概率。
设想群体全部个体的适应性分数分别由一张饼图代表,如下所示:
群体中每一染色体指定饼图中一个小块。块的大小与染色体的适应性分数成比例,适应性分数愈高,它在饼图中对应的小块所占面积也愈大。为了选取一个染色体,要做的就是旋转这个轮子,直到轮盘停止时,看指针停止在哪一块上,就选中与它对应的那个染色体。例如:
个体 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
---|---|---|---|---|---|---|---|---|---|---|---|
适应度 | 2 | 1.8 | 1.6 | 1.4 | 1.2 | 1.0 | 0.8 | 1.6 | 0.4 | 0.2 | 0.1 |
选择概率 | 0.18 | 0.16 | 0.15 | 0.13 | 0.11 | 0.09 | 0.07 | 0.06 | 0.03 | 0.02 | 0.009 |
累计概率 | 0.18 | 0.34 | 0.19 | 0.62 | 0.73 | 0.82 | 0.89 | 0.95 | 0.98 | 0.991 | 1 |
若产生的随机数为0.88,0.82<0.88<0.89,则7号个体被选中。
由表格可以看到:“11号个体选中概率最小,但却因为其编号最大导致其累积概率为1,那么会有人疑惑是否11号个体会被选中的事件较大概率出现呢?为了验证结果,在Matlab中进行实验。进行1000次模拟,并记录每次模拟的结果,得到结果如下表所示:
个体 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
---|---|---|---|---|---|---|---|---|---|---|---|
选中次数 | 183 | 168 | 151 | 131 | 112 | 81 | 69 | 57 | 33 | 9 | 6 |
选中概率 | 0.183 | 0.168 | 0.151 | 0.131 | 0.112 | 0.081 | 0.069 | 0.057 | 0.033 | 0.009 | 0.006 |
从这里可以看出适应度大的选中次数多,选中概率大。适应度小的则相反。说明轮盘赌选择法是切实可行的。
5.4、交叉(crossover)
交叉算子将被选中的两个个体的基因链按一定概率pc进行交叉,从而生成两个新的个体,交叉位置pc是随机的。其中pc是一个系统参数。根据问题的不同,交叉又分为了单点交叉算子(Single Point Crossover)、双点交叉算子(Two Point Crossover)、均匀交叉算子 (Uniform Crossover),在此我们只讨论单点交叉的情况。
单点交叉操作的简单方式是将被选择出的两个个体S1和S2作为父母个体,将两者的部分基因码值进行交换。下面我们分别以二进制编码和浮点数数编码为例:
二进制编码:
假设如下两个8位的个体:
S1:1000 1111
S2:1110 1100
产生一个在1到7之间的随机数c,假如现在产生的是2,将S1和S2的低二位交换,后代P1为1100 1111,P2为10101100。在这里高二位与低二位是按从左到右的顺序还是从右到左的顺序,自我感觉效果是一样的,在这篇文章中我们定义为从左到右的。
浮点数编码(不同于上面,这里采用了单点交换(实数交叉法)):
假设有两个染色体
a
k
,
a
j
a_k,a_j
ak,aj,随机产生一个交叉位置
k
k
k,则交叉法则如下:
{
a
k
j
=
a
i
j
(
1
−
b
)
+
a
i
j
b
a
i
j
=
a
k
j
(
1
−
b
)
+
a
k
j
b
\begin{cases} a_{kj} = a_{ij}(1-b) + a_{ij}b \\ a_{ij} = a_{kj}(1-b) + a_{kj}b \\ \end{cases}
{akj=aij(1−b)+aijbaij=akj(1−b)+akjb
其中
b
b
b时0-1之间的随机数。
5.5、变异(Mutation)
这是在选中的个体中,将新个体的基因链的各位按概率pm进行异向转化,最简单方式是改变串上某个位置数值。对二进制编码来说将0与1互换:0变异为1,1变异为0。
以下变异算子适用于二进制编码和浮点数编码的个体:
- 基本位变异(Simple Mutation):对个体编码串中以变异概率、随机指定的某一位或某几位仅因座上的值做变异运算。
- 均匀变异(Uniform Mutation):分别用符合某一范围内均匀分布的随机数,以某一较小的概率来替换个体编码串中各个基因座上的原有基因值。(特别适用于在算法的初级运行阶段)
- 边界变异(Boundary Mutation):随机的取基因座上的两个对应边界基因值之一去替代原有基因值。特别适用于最优点位于或接近于可行解的边界时的一类问题。
- 非均匀变异:对原有的基因值做一随机扰动,以扰动后的结果作为变异后的新基因值。对每个基因座都以相同的概率进行变异运算之后,相当于整个解向量在解空间中作了一次轻微的变动。
- 高斯近似变异:进行变异操作时用符号均值为P的平均值,方差为P**2的正态分布的一个随机数来替换原有的基因值。
下面我们分别以二进制编码和浮点数数编码为例用基本位变异:
二进制编码:
如下8位二进制编码:
1 1 1 0 1 1 0 0
随机产生一个1至8之间的数i,假如现在k=6,对从左往右的第6位进行变异操作,将原来的1变为0,得到如下串:
1 1 1 0 1 0 0 0
浮点数编码:
第
i
i
i个染色体的第
j
j
j个基因
a
i
j
a_{ij}
aij变异法则如下:
a
i
j
=
{
a
i
j
+
(
a
i
j
−
a
m
a
x
)
∗
f
(
g
)
r
≥
0.5
a
i
j
+
(
a
m
i
n
−
a
i
j
)
∗
f
(
g
)
r
<
0.5
a_ij = \begin{cases} a_{ij} + (a_{ij}-a_{max})*f(g) & r \geq 0.5 \\ a_{ij} + (a_{min}-a_{ij})*f(g) & r < 0.5 \\ \end{cases}
aij={aij+(aij−amax)∗f(g)aij+(amin−aij)∗f(g)r≥0.5r<0.5
其中
a
m
a
x
a_{max}
amax时基因
a
i
j
a_{ij}
aij的上界;
a
m
i
n
a_{min}
amin时基因
a
i
j
a_{ij}
aij的下界;
f
(
g
)
=
r
2
(
1
−
g
/
G
m
a
x
)
2
f(g) = r_2(1-g/G{max})^2
f(g)=r2(1−g/Gmax)2,
r
2
r_2
r2是一个0-1之间的随机数,
g
g
g时当前的迭代次数,
G
m
a
x
G_{max}
Gmax时最大进化次数,
r
r
r是0-1之间的随机数。
5.6、精英主义(Elitism)
仅仅从产生的子代中选择基因去构造新的种群可能会丢失掉上一代种群中的很多信息。也就是说当利用交叉和变异产生新的一代时,我们有很大的可能把在某个中间步骤中得到的最优解丢失。在此我们使用精英主义(Elitism)方法,在每一次产生新的一代时,我们首先把当前最优解原封不动的复制到新的一代中,其他步骤不变。这样任何时刻产生的一个最优解都可以存活到遗传算法结束。
4、算法结合
经典非线性规划算法大多采用梯度下降的方法求解,局部搜索能力较强,但是全局搜素能力较弱。遗传算法采用选择、交叉和变异算子进行搜索,全局搜索能力较强,但是局部搜索能力较弱,一般只能得到问题的次优解,而不足最优解。因此,本文结合了两种算法的优点,一方面采用遗传算法进行全局搜索,一方面采用非线性规划算法进行局部搜索,以得到问题的全局最优解。
二、举例说明
😂😂😂前面花了很多功夫介绍了一下遗传算法,现在终于到了本文的主要内容了。都看到这里了,希望小伙伴们能给个赞鼓励一下😘😘
1、问题描述
采用遗传算法和非线性规划的方法求解如下函数的最小值。
f
(
x
)
=
−
5
s
i
n
x
1
s
i
n
x
2
s
i
n
x
3
s
i
n
x
4
s
i
n
x
5
−
s
i
n
5
x
1
s
i
n
5
x
2
s
i
n
5
x
3
s
i
n
5
x
4
s
i
n
5
x
5
+
8
f(x) = -5sinx_1sinx_2sinx_3sinx_4sinx_5-sin5x_1sin5x_2sin5x_3sin5x_4sin5x_5+8
f(x)=−5sinx1sinx2sinx3sinx4sinx5−sin5x1sin5x2sin5x3sin5x4sin5x5+8
其中
x
1
、
x
2
、
x
3
、
x
4
、
x
5
x_1、x_2、x_3、x_4、x_5
x1、x2、x3、x4、x5是0~0.9
π
\pi
π之间的实数
容易看出该问题的解为(
π
\pi
π/2,
π
\pi
π/2,
π
\pi
π/2,
π
\pi
π/2,
π
\pi
π/2),最小值为2。
下面通过算法求解答案。
2、算法流程
非线性规划遗传算法流程图如下图所示:
其中,种群初始化模块根据求解问题初始化种群,适应度值计算模块根据适应度函数计算种群中染色体的适应度值,选择、交叉和变异为遗传算法的搜索算子,N 为固定值,当进化次数为 N 的倍数时,则采用非线性寻优的方法加快进化,非线性寻优利用当前染色体值采用函数 fmincon 寻找问题的局部最优值。
三、MATLAB程序实现
1、编码方式
采用浮点数编码
function ret=Code(lenchrom,bound)
%本函数将变量编码成染色体,用于随机初始化一个种群
% lenchrom input : 染色体长度
% bound input : 变量的取值范围
% ret output: 染色体的编码值
flag=0;
while flag==0
pick=rand(1,length(lenchrom));
ret=bound(:,1)'+(bound(:,2)-bound(:,1))'.*pick; %线性插值
flag=test(lenchrom,bound,ret); %检验染色体的可行性
2、适应度函数
本例是求解最小值,因此我们将目标函数当作我们的适应度函数,而个体适应度为适应度函数的倒数。
function y = fun(x)
y=-5*sin(x(1))*sin(x(2))*sin(x(3))*sin(x(4))*sin(x(5))-sin(5*x(1))*sin(5*x(2))*sin(5*x(3))*sin(5*x(4))*sin(5*x(5))+8;
3、选择操作
选择操作采用轮盘赌的方法从种群中选择适应度好的个体组成新种群。
function ret=Select(individuals,sizepop)
% 本函数对每一代种群中的染色体进行选择,以进行后面的交叉和变异
% individuals input : 种群信息
% sizepop input : 种群规模
% opts input : 选择方法的选择
% ret output : 经过选择后的种群
individuals.fitness= 1./(individuals.fitness);
sumfitness=sum(individuals.fitness);
sumf=individuals.fitness./sumfitness;
index=[];
for i=1:sizepop %转sizepop次轮盘
pick=rand;
while pick==0
pick=rand;
end
for j=1:sizepop
pick=pick-sumf(j);
if pick<0
index=[index j];
break; %寻找落入的区间,此次转轮盘选中了染色体i,注意:在转sizepop次轮盘的过程中,有可能会重复选择某些染色体
end
end
end
individuals.chrom=individuals.chrom(index,:);
individuals.fitness=individuals.fitness(index);
ret=individuals;
4、交叉操作
交叉操作是从种群中选取两个个体,按一定概率交叉得到新个体。
function ret=Cross(pcross,lenchrom,chrom,sizepop,bound)
%本函数完成交叉操作
% pcorss input : 交叉概率
% lenchrom input : 染色体的长度
% chrom input : 染色体群
% sizepop input : 种群规模
% ret output : 交叉后的染色体
for i=1:sizepop
% 随机选择两个染色体进行交叉
pick=rand(1,2);
while prod(pick)==0
pick=rand(1,2);
end
index=ceil(pick.*sizepop);
% 交叉概率决定是否进行交叉
pick=rand;
while pick==0
pick=rand;
end
if pick>pcross
continue;
end
flag=0;
while flag==0
% 随机选择交叉位置
pick=rand;
while pick==0
pick=rand;
end
pos=ceil(pick.*sum(lenchrom)); %随机选择进行交叉的位置,即选择第几个变量进行交叉,注意:两个染色体交叉的位置相同
pick=rand; %交叉开始
v1=chrom(index(1),pos);
v2=chrom(index(2),pos);
chrom(index(1),pos)=pick*v2+(1-pick)*v1;
chrom(index(2),pos)=pick*v1+(1-pick)*v2; %交叉结束
flag1=test(lenchrom,bound,chrom(index(1),:)); %检验染色体1的可行性
flag2=test(lenchrom,bound,chrom(index(2),:)); %检验染色体2的可行性
if flag1*flag2==0
flag=0;
else flag=1;
end %如果两个染色体不是都可行,则重新交叉
end
end
ret=chrom;
5、变异操作
变异操作是从种群中选取一个个体,按一定概率变异得到新个体。
function ret=Mutation(pmutation,lenchrom,chrom,sizepop,pop,bound)
% 本函数完成变异操作
% pcorss input : 变异概率
% lenchrom input : 染色体长度
% chrom input : 染色体群
% sizepop input : 种群规模
% pop input : 当前种群的进化代数和最大的进化代数信息
% ret output : 变异后的染色体
for i=1:sizepop
% 随机选择一个染色体进行变异
pick=rand;
while pick==0
pick=rand;
end
index=ceil(pick*sizepop);
% 变异概率决定该轮循环是否进行变异
pick=rand;
if pick>pmutation
continue;
end
flag=0;
while flag==0
% 变异位置
pick=rand;
while pick==0
pick=rand;
end
pos=ceil(pick*sum(lenchrom)); %随机选择了染色体变异的位置,即选择了第pos个变量进行变异
v=chrom(i,pos);
v1=v-bound(pos,1);
v2=bound(pos,2)-v;
pick=rand; %变异开始
if pick>0.5
delta=v2*(1-pick^((1-pop(1)/pop(2))^2));
chrom(i,pos)=v+delta;
else
delta=v1*(1-pick^((1-pop(1)/pop(2))^2));
chrom(i,pos)=v-delta;
end %变异结束
flag=test(lenchrom,bound,chrom(i,:)); %检验染色体的可行性
end
end
ret=chrom;
5、算法主函数
遗传算法主函数流程如下:
- 随机初始化种群
- 计算种群的适应度值,从中选取最有个体
- 选择操作
- 交叉操作
- 变异操作
- 非线性寻优
- 判断进化是否结束,若否则返回步骤2
%% 清空环境
clc
clear
warning off
%% 遗传算法参数
maxgen=30; %进化代数
sizepop=100; %种群规模
pcross=[0.6]; %交叉概率
pmutation=[0.01]; %变异概率
lenchrom=[1 1 1 1 1]; %变量字串长度
bound=[0 0.9*pi;0 0.9*pi;0 0.9*pi;0 0.9*pi;0 0.9*pi]; %变量范围
%% 个体初始化
individuals=struct('fitness',zeros(1,sizepop), 'chrom',[]); %种群结构体
avgfitness=[]; %种群平均适应度
bestfitness=[]; %种群最佳适应度
bestchrom=[]; %适应度最好染色体
% 初始化种群
for i=1:sizepop
individuals.chrom(i,:)=Code(lenchrom,bound); %随机产生个体
x=individuals.chrom(i,:);
individuals.fitness(i)=fun(x); %个体适应度
end
%找最好的染色体
[bestfitness bestindex]=min(individuals.fitness);
bestchrom=individuals.chrom(bestindex,:); %最好的染色体
avgfitness=sum(individuals.fitness)/sizepop; %染色体的平均适应度
% 记录每一代进化中最好的适应度和平均适应度
trace=[];
%% 进化开始
for i=1:maxgen
% 选择操作
individuals=Select(individuals,sizepop);
avgfitness=sum(individuals.fitness)/sizepop;
% 交叉操作
individuals.chrom=Cross(pcross,lenchrom,individuals.chrom,sizepop,bound);
% 变异操作
individuals.chrom=Mutation(pmutation,lenchrom,individuals.chrom,sizepop,[i maxgen],bound);
if mod(i,10)==0
individuals.chrom=nonlinear(individuals.chrom,sizepop);
end
% 计算适应度
for j=1:sizepop
x=individuals.chrom(j,:);
individuals.fitness(j)=fun(x);
end
%找到最小和最大适应度的染色体及它们在种群中的位置
[newbestfitness,newbestindex]=min(individuals.fitness);
[worestfitness,worestindex]=max(individuals.fitness);
% 代替上一次进化中最好的染色体
if bestfitness>newbestfitness
bestfitness=newbestfitness;
bestchrom=individuals.chrom(newbestindex,:);
end
individuals.chrom(worestindex,:)=bestchrom;
individuals.fitness(worestindex)=bestfitness;
avgfitness=sum(individuals.fitness)/sizepop;
trace=[trace;avgfitness bestfitness]; %记录每一代进化中最好的适应度和平均适应度
end
%进化结束
%% 结果显示
figure
[r c]=size(trace);
plot([1:r]',trace(:,1),'r-',[1:r]',trace(:,2),'b--');
title(['函数值曲线 ' '终止代数=' num2str(maxgen)],'fontsize',12);
xlabel('进化代数','fontsize',12);ylabel('函数值','fontsize',12);
legend('各代平均值','各代最佳值','fontsize',12);
ylim([1.5 8])
disp('函数值 变量');
% 窗口显示
disp([bestfitness x]);
grid on
6、非线性寻优
调用MATLAB工具箱中的fmincon进行非线性求解,代码如下:
function ret = nonlinear(chrom,sizepop)
for i=1:sizepop
x=fmincon(inline('-5*sin(x(1))*sin(x(2))*sin(x(3))*sin(x(4))*sin(x(5))-sin(5*x(1))*sin(5*x(2))*sin(5*x(3))*sin(5*x(4))*sin(5*x(5))')...
,chrom(i,:)',[],[],[],[],[0 0 0 0 0],[2.8274 2.8274 2.8274 2.8274 2.8274]);
ret(i,:)=x';
end
7、结果分析
根据遗传算法理论,在MATLAB 软件中编程实现利用基本遗传算法寻找该兩数最优解。遗传算法参数设置为:种群规模 100,进化次数 30,交叉概率为0.6,变异概率为 0.1。
基本遗传算法优化过程中各代平均两数值和最优个体两数值变化如下图所示:
当种群进化到30 代时,函数值收敛到2.0174,在
x
1
、
x
2
、
x
3
、
x
4
、
x
5
x_1、x_2、x_3、x_4、x_5
x1、x2、x3、x4、x5,分别取1.6225 1.6165 1.5890 1.6089 1.5814时到达。
在MATLAB软件中编程实现基于遗传算法和非线性规划的函数寻优求解该问题。遗传算法参数设置同前,即种群规模 100,进化次数 30,交叉概率为0.6,变异概率为 0.1。
算法优化过程中各代平均函数值和最优个体函数值变化如下图所示:
当种群进化到30 代时,函数值收敛到2.0000,在
x
1
、
x
2
、
x
3
、
x
4
、
x
5
x_1、x_2、x_3、x_4、x_5
x1、x2、x3、x4、x5,分别取1.5708 1.5708 1.5708 1.5708 1.5708时到达。
比较两图可见,在同等条件下,基于遗传算法和非线性规划的函数寻优算法在收敛速度和求解结果上优于基本的遗传算法。可见,将非线性规划方法同遗传算法相结合,提高了遗传算法的搜索能力。
四、总结
本文采用的基本遗传算法和基于遗传算法和非线性规划的函数寻优算法的代码均会在下面给出。其中基本遗传算法只需要根据需求设置适应度函数,变量取值范围以及各个参数便可以用于其他问题。
链接:https://pan.baidu.com/s/18HlTtFe9ytPHqXq3dkmsFA
提取码:6666
喜欢的小伙伴麻烦点个赞奥,谢谢啦🙏🙏
参考:
MATLAB智能算法30案例分析(第二版)
https://www.jianshu.com/p/b0e08ca9a529