遗传算法是一种启发式搜索算法,其基本原理是模拟自然界物竞天择的演化法则。(说的这么高大上,其实就是比较高级的穷举法,不过穷举的时候给出穷举方向而已)。遗传算法具体步骤见下图(来自百度百科):
名词介绍
染色体,个体,基因
这3个概念基本等价,遗传算法中每个个体都含有一个染色体\基因,遗传算法将在种群中选择优秀个体,将他们的染色体交叉、变异后产生新的下一代种群。
一下说了这么多新概念,你可能会有些晕,说到底,染色体就相当于自变量x
,而我们的优化目标就相当于因变量y
,他们之间有函数关系y=f(x)
,这个函数f(x)
就是评价一个染色体好坏、能否适应环境的适应度函数。
那么我们如何得到染色体呢?简单的方式是通过二进制编码。
编码、解码
编码的作用就是将解空间的解转换为染色体编码,比如你想花100块钱去吃东西,那解空间便是[0,100]
,然后我们将此空间转化为一个8位的染色体编码
$$00000000 \rightarrow 11111111$$
$$0 \rightarrow 100$$
将染色体在解空间上平均分布,这样每个染色体与解空间的解都将存在一一对应的关系,如 $00000000 \rightarrow 0$ ,这也就是解码的过程。
总结下,对于二进制编码来说,编码是将实数转化二进制数,解码是将二进制数转化为实数。
遗传操作
选择
选择需要选择出更适应环境的个体,用于之后的交叉操作,适应环境的个体将更加容易被选中,产生更多后代。适应环境与否可以通过适应度函数,适应度函数类似目标函数,根据你优化求解的问题确定,本博选用的适应度函数为:
$$f(x)=x+5\sin(5x)+2\cos(3x)$$
函数图像如下所示:
根据适应度函数可以确定每次选择每个个体被选中的概率,计算公式如下:选中概率=个体适应度/总体适应度。可见,适应度越高,被选中概率越大
$$p_i = \frac{f(x_i)}{\sum\limits^n_{j=1}f(x_j)}$$
从上式可知,所有个体被选中的概率为1,假设p为概率数组,包含了每个个体被选中的概率,因此将个体被选中概率累加:
for i in range(len(p)): p[i]=p[i-1]+p[i]
然后随机生成0-1之间的随机数,依次在概率数组中比较,就可以找到被选中的个体。如下图所示:
交叉
对选择出的多个适应性优良个体,通过交叉操作产生下一代优良个体。如下所示:
$$A_{father}B_{father}+A_{mather}B_{mather}\rightarrow A_{father}B_{mother}(child_1)+A_{mother}B_{father}(child_2)$$
对二进制编码的基因而言,假设基因长度为8,父辈母辈各取长度4的基因,有:
$$\color{red}{11110000}\color{black}+\color{blue}{00001111}\color{black}\rightarrow \color{red}{1111}\color{blue}{1111}\color{black}{+}\color{blue}{0000}\color{red}{0000}$$
交叉将父辈、母辈的基因交叉产生子代,子代将继承上一代的优良基因。
变异
为了避免收敛到局部最优解,在遗传算法中加入变异属性,使优化程序有一定几率跳出局部最优解。群体中的每个个体都有一定几率发生变异,变异时任取个体的一段基因并将其改变,如:
$$\color{red}{11111111}\color{black}\rightarrow \color{red}{1111}\color{blue}{0}\color{red}{111}$$
代码实现
代码用到numpy以及matplotlib库。
详见Github
|
|
结果分析
本文的算例相对比较简单,优化迭代图如图所示,算法最终收敛到12.9-13.05都是有可能的:
种群示意图如下图:
可以发现,初始种群随机分布在整个设计域内,随着迭代次数的增加,劣势种群逐步被淘汰,优势种群不断繁衍,造成剩下的种群逐步向目标函数极大值处集中。另外也有少部分种群发生变异,随机出现在图中的某一位置。