目录
1 引入
遗传算法(Genetic Algorithm,GA)是模拟生物在自然环境中的遗传和进化过程而形成的自适应全局优化搜索算法。它最早由美国的J.H.Holland教授提出,起源于20世纪60年代对自然和人工自适应系统的研究;70年代,K.A.De Jong基于遗传算法的思想,在计算机上进行了大量的纯数值函数优化计算试验80年代,遗传算法由D.J.Goldberg在一系列研究工作的基础上归纳总结而成。遗传算法是通过模仿自然界生物进化机制而发展起来的随机全局搜索和优化方法。它借鉴了达尔文的进化论和孟德尔的遗传学说,本质上是一种并行、高效、全局搜索的方法,它能在搜索过程中自动获取和积累有关搜索空间的知识,并自适应地控制搜索过程以求得最优解。遗传算法操作:使用“适者生存”的原则,在潜在的解决方案种群中逐次产生一个近似最优的方案。在每一代中,根据个体在问题域中的适应度值和从自然遗传学中借鉴来的再造方法进行个体选择,产生一个新的近似解。这个过程导致种群中个体的进化,得到的新个体比原个体更能适应环境。
自然选择学说认为适者生存,生物要存活下去,就必须进行生存斗争。生存斗争包括种内斗争、种间斗争以及生物跟环境之间的斗争三个方面。在生存斗争中,具有有利变异的个体容易存活下来,并且有更多的机会将有利变异传给后代;具有不利变异的个体就容易被淘汰,产生后代的机会也将少得多。因此,凡是在生存斗争中获胜的个体都是对环境适应性比较强的个体。达尔文把这种在生存斗争中适者生存、不适者淘汰的过程叫作自然选择。达尔文的自然选择学说表明,遗传和变异是决定生物进化的内在因素。遗传是指父代与子代之间,在性状上存在的相似现象;变异是指父代与子代之间,以及子代的个体之间,在性状上存在的差异现象。在生物体内,遗传和变异的关系十分密切。一个生物体的遗传性状往往会发生变异,而变异的性状有的可以遗传。遗传能使生物的性状不断地传送给后代,因此保持了物种的特性;变异能够使生物的性状发生改变,从而适应新的环境而不断地向前发展。生物的各项生命活动都有它的物质基础,生物的遗传与变异也是这样。根据现代细胞学和遗传学的研究得知,遗传物质的主要载体是染色体,基因是有遗传效应的片段,它储存着遗传信息,可以准确地复制,也能够发生突变。生物体自身通过对基因的复制和交叉,使其性状的遗传得到选择和控制。同时,通过基因重组、基因变异和染色体在结构和数目上的变异产生丰富多彩的变异现象。生物的遗传特性,使生物界的物种能够保持相对的稳定;生物的变异特性,使生物个体产生新的性状,以至形成了新的物种,推动了生物的进化和发展。由于生物在繁殖中可能发生基因交叉和变异,引起了生物性状的连续微弱改变,为外界环境的定向选择提供了物质条件和基础,使生h2物的进化成为可能。
2 遗传算法
2.1 遗传算法的基本概念
简单而言,遗传算法使用群体搜索技术,将种群代表一组问题解,通过对当前种群施加选择、交叉和变异等一系列遗传操作来产生新一代的种群,并逐步使种群进化到包含近似最优解的状态。由于遗传算法是自然遗传学与计算机科学相互渗透而形成的计算方法,所以遗传算法中经常会使用一些有关自然进化的基础术语,其中的术语对应关系如表2.1所示。
遗传学术语
遗传算法术语
群体
可行解集
个体
可行解
染色体
可行解的编码
基因
可行解编码的分量
基因形式
遗传编码
适应度
目标函数值
选择
选择操作
交叉
交叉操作
变异
变异操作
2.2 遗传算法的特点
遗传算法是模拟生物在自然环境中的遗传和进化的过程而形成的一种并行、高效、全局搜索的方法,它主要有以下特点:
(1)遗传算法以决策变量的编码作为运算对象。这种对决策变量的编码处理方式,使得在优化计算过程中可以借鉴生物学中染色体和基因等概念,模仿自然界中生物的遗传和进化等的机理,方便地应用遗传操作算子。特别是对一些只有代码概念而无数值概念或很难有数值概念的优化问题,编码处理方式更显示出了其独特的优越性。
(2)遗传算法直接以目标函数值作为搜索信息。它仅使用由目标函数值变换来的适应度函数值,就可确定进一步的搜索方向和搜索范围,而不需要目标函数的导数值等其他一些辅助信息。实际应用中很多函数无法或很难求导,甚至根本不存在导数,对于这类目标函数的优化和组合优化问题,遗传算法就显示了其高度的优越性,因为它避开了函数求导这个障碍。
(3)遗传算法同时使用多个搜索点的搜索信息。遗传算法对最优解的搜索过程,是从一个由很多个体所组成的初始群体开始的,而不是从单一的个体开始的。
对这个群体所进行的选择、交叉、变异等运算,产生出新一代的群体,其中包括了很多群体信息。这些信息可以避免搜索一些不必搜索的点,相当于搜索了更多的点,这是遗传算法所特有的一种隐含并行性。
(4)遗传算法是一种基于概率的搜索技术。遗传算法属于自适应概率搜索技术,其选择、交叉、变异等运算都是以一种概率的方式来进行的,从而增加了其搜索过程的灵活性。虽然这种概率特性也会使群体中产生一些适应度不高的个体,但随着进化过程的进行,新的群体中总会更多地产生出优良的个体。与其他一些算法相比,遗传算法的鲁棒性使得参数对其搜索效果的影响尽可能小。(5)遗传算法具有自组织、自适应和自学习等特性。当遗传算法利用进化过程获得信息自行组织搜索时,适应度大的个体具有较高的生存概率,并获得更适应环境的基因结构。同时,遗传算法具有可扩展性,易于同别的算法相结合,生成综合双方优势的混合算法。
2.3 程序框图
遗传算法的运算流程如图2.1所示。具体步骤如下:
(1)初始化。设置进化代数计数器g=0,设置最大进化代数G,随机生成NP个个体作为初始群体P(0)。
(2)个体评价。计算群体P()中各个个体的适应度。
(3)选择运算。将选择算子作用于群体,根据个体的适应度,按照一定的规则或方法,选择一些优良个体遗传到下一代群体
(4)交叉运算。将交叉算子作用于群体,对选中的成对个体,以某一概率交换它们之间的部分染色体,产生新的个体。
(5)变异运算。将变异算子作用于群体,对选中的个体,以某一概率改变某一个或某一些基因值为其他的等位基因。群体P(i)经过选择、交叉和变异运算之后得到下一代群体P(t+1)。计算其适应度值,并根据适应度值进行排序,准备进行下一次遗传操作。
(6)终止条件判断:若g<G,则g=g+1,转到步骤(2);若g>G,则此进化过程中所得到的具有最大适应度的个体作为最优解输出,终止计算。
3 Python代码实现
3.1 源代码实现
# -*- coding: utf-8 -*-
#========导入相关包============
import numpy as np
import matplotlib.pyplot as plt
#=====适应度函数,求取最大值======
def fitness(x):
return x + 16 * np.sin(5 * x) + 10 * np.cos(4 * x)
#=====个体类============
class indivdual:
def __init__(self):
self.x = 0 # 染色体编码
self.fitness = 0 # 个体适应度值
def __eq__(self, other):
self.x = other.x
self.fitness = other.fitness
#=====初始化种群===========
#pop为种群适应度存储数组,N为个体数
def initPopulation(pop, N):
for i in range(N):
ind = indivdual()#个体初始化
ind.x = np.random.uniform(-10, 10)# 个体编码。-10,10的正态分布,可以自己设定限定边界
ind.fitness = fitness(ind.x)#计算个体适应度函数值
pop.append(ind)#将个体适应度函数值添加进种群适应度数组pop
#======选择过程==============
def selection(N):
# 种群中随机选择2个个体进行变异(这里没有用轮盘赌,直接用的随机选择)
return np.random.choice(N, 2)
#===结合/交叉过程=============
def crossover(parent1, parent2):
child1, child2 = indivdual(), indivdual()#父亲,母亲初始化
child1.x = 0.9 * parent1.x + 0.1 * parent2.x #交叉0.9,0.1,可以设置其他系数
child2.x = 0.1 * parent1.x + 0.9 * parent2.x
child1.fitness = fitness(child1.x)#子1适应度函数值
child2.fitness = fitness(child2.x)#子2适应度函数值
return child1, child2
#=====变异过程==========
def mutation(pop):
# 种群中随机选择一个进行变异
ind = np.random.choice(pop)
# 用随机赋值的方式进行变异
ind.x = np.random.uniform(-10, 10)
ind.fitness = fitness(ind.x)
#======最终执行===========
def implement():
#===种群中个体数量====
N = 40
# 种群
POP = []
# 迭代次数
iter_N = 400
# 初始化种群
initPopulation(POP, N)
#====进化过程======
for it in range(iter_N):#遍历每一代
a, b = selection(N)#随机选择两个个体
if np.random.random() < 0.65: # 以0.65的概率进行交叉结合
child1, child2 = crossover(POP[a], POP[b])
new = sorted([POP[a], POP[b], child1, child2], key=lambda ind: ind.fitness, reverse=True)#将父母亲和子代进行比较,保留最好的两个
POP[a], POP[b] = new[0], new[1]
if np.random.random() < 0.1: # 以0.1的概率进行变异
mutation(POP)
POP.sort(key=lambda ind: ind.fitness, reverse=True)
return POP
if __name__ =='__main__':
POP = implement()
#=======绘图代码============
def func(x):
return x + 16 * np.sin(5 * x) + 10 * np.cos(4 * x)
x = np.linspace(-10, 10, 100000)
y = func(x)
scatter_x = np.array([ind.x for ind in POP])
scatter_y = np.array([ind.fitness for ind in POP])
best=sorted(POP,key=lambda POP:POP.fitness,reverse=True)[0]#最佳点
print('best_x',best.x)
print('best_y',best.fitness)
plt.plot(x, y)
#plt.scatter(scatter_x, scatter_y, c='r')
plt.scatter(best.x,best.fitness,c='g',label='best point')
plt.legend()
plt.show()
结果:
best_x 7.821235839598371
best_y 33.52159669619314
3.2 遗传算法包sko.GA
3.2.1 案例1
#=======导入相关库=========
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sko.GA import GA
#=====目标函数========
def fun(p):
#该函数具有大量的局部极小值,具有强冲击值为0的(0,0)处的全局最小值
#GA要求输入维度2维及其以上,所以我传入2个参数x1,x2
x1, x2 = p
x = np.square(x1) + np.square(x2)
return 0.5 + (np.square(np.sin(x)) - 0.5) / np.square(1 + 0.001 * x)
#=====调用遗传算法工具箱========
ga = GA(func=fun, n_dim=2, size_pop=50, max_iter=800, prob_mut=0.001, lb=[-1, -1], ub=[1, 1], precision=1e-7)
best_x, best_y = ga.run()
print('best_x:', best_x, '\n', 'best_y:', best_y)
#========可视化===============
Y_history = pd.DataFrame(ga.all_history_Y)
fig, ax = plt.subplots(2, 1)
ax[0].plot(Y_history.index, Y_history.values, marker='.', color='red')
Y_history.min(axis=1).cummin().plot(kind='line')
plt.show()
结果:
best_x: [-2.98023233e-08 8.94069698e-08]
best_y: [0.]
3.2.2 案例2
与源代码进行比较:
#======导入相关包=========
import numpy as np
import matplotlib.pyplot as plt
from sko.GA import GA #遗传算法工具包
#=========适应度函数,求取最大值=========
#因为GA函数是求最小值,所以我在适应度函数上加一个负号
#GA要求输入维度2维及其以上,所以我传入2个参数,第二维x2不用
def fitness(x):
x1, x2 = x
#x1=x
return -(x1 + 16 * np.sin(5 * x1) + 10 * np.cos(4 * x1))
#==========遗传算法包应用==================
ga = GA(func=fitness, n_dim=2, size_pop=50, max_iter=800, lb=[-10,0], ub=[10,0],precision=1e-7)
best_x, best_y = ga.run()
print('best_x:', best_x[0], '\n', 'best_y:', -best_y)
#==========可视化================
def func(x):
return x + 16 * np.sin(5 * x) + 10 * np.cos(4 * x)
x = np.linspace(-10, 10, 100000)
y = func(x)
plt.plot(x, y)
plt.scatter(best_x[0], -best_y, c='r',label='best point')
plt.legend()
plt.show()
best_x: 7.855767376183596
best_y: [33.8548745]
4 参考
————————————————
https://blog.csdn.net/kobeyu652453/article/details/109527260