遗传算法是群智能优化算法计算中应用最为广泛、最为成功、最具代表性的智能优化方法。以达尔文的生物进化论和孟德尔的遗传变异理论为基础,模拟生物界的进化过程,以对种群进行优化搜索的方法。
首先生成一个种群,然后种群中每一个个体对于环境具有不同的生存能力,即适应度,而根据每个个体不同的适应度,每个个体能够存活并繁殖后代的概率有所不同,一般遵循正比选择策略,即较高适应度产生后代的概率较高,较低适应度产生后代概率较低。而在产生后代时决定每个个体适应度(即表现型)的基因会以一定概率发生交叉和变异,得到的个体会得到较高或者较低的适应度,并重复繁殖过程若干代,结束选出最高适应度的一个或多个个体作为优化结果。
在遗传算法中主要存在基因编码方式、评估适应环境能力的适应度函数、选择繁殖后代个体的选择方式、繁殖后代时可能会发生的交叉和变异方式、以及停止算法方式。
遗传算法中编码方式往往决定了个体的基因型表达方式,以及交叉和变异操作,是影响算法执行效率的重要因素之一。主要包括二进制编码、实数编码、顺序编码等。
适应度函数一般应准确表示个体在环境下的适应能力,是一代一代筛选优秀个体的重要影响因素之一,一般由目标函数变换而成。常见的变换方式有线性变换、幂律变换、指数变化、对数变换。需要根据实际问题需要选择变换方式,如放大或缩小目标函数值的差别。
变换名称 | 变换描述 | 备注 |
---|---|---|
线性变换 | F ( x ) = a k f ( x ) + b k F(x)=a^kf(x)+b^k F(x)=akf(x)+bk | k一般随着进化次数的增加而变化,k=1时为静态型变换, k ≠ 1 k\neq1 k=1时为动态型 |
幂律变换 | F ( x ) = f ( x ) a F(x)=f(x)^a F(x)=f(x)a | |
指数变换 | F ( x ) = a ⋅ e b f ( x ) + c F(x)=a\cdot e^bf(x)+c F(x)=a⋅ebf(x)+c | 一般用于扩大目标函数的差别 |
对数变换 | F ( x ) = a ⋅ l n f ( x ) + b F(x)=a\cdot lnf(x)+b F(x)=a⋅lnf(x)+b | 一般用于缩小目标函数的差别 |
注: f ( x ) f(x) f(x)、 F ( x ) F(x) F(x)分别是目标函数和适应度函数
选择过程是在父代中依概率选择若干个体进行遗传和交叉操作,选择过程应遵循优胜劣汰的法则,使适应度函数值较大的个体拥有更多繁殖的机会,以保证后代的优秀,而适应度函数值较小的个体也不应直接去除,仍需保持小概率可繁殖以保证种群的多样性,即解向量的全局分布情况。
常见的选择方式是轮盘赌(旋轮法),即将一个圆盘以各个体的适应度函数值为比重分为若干份,然后随机生成指针选择可繁殖的后代,一般保持种群数量不变。每个个体被选中的概率可以表示为 P i = F i ∑ i = 1 n F i , i = 1 , 2 , 3 , . . . , n P_i = \frac{F_i}{\sum_{i=1}^nFi},i=1,2,3,...,n Pi=∑i=1nFiFi,i=1,2,3,...,n (这里我也不知道为什么累加的参数部分变成了上下标)。
交叉和变异两种运算是遗传算法搜索最优解的关键也是根本办法所在,也是遗传算法改进最多的地方。
交叉算子:一般由两个父代个体的基因的部分交换,得到两个子代的基因,常用的交叉方式有单点交叉和双点交叉。
两点交叉:同时选择两个父代基因上的两个交叉点,并交换两个交叉点之间的基因。而交叉点的增加可推广为多点交叉,两点交叉的操作如下:
父 代 基 因 串 1 1010 1111 1111 ⟶ 1010 1001 1111 父 代 基 因 串 2 0100 1001 1001 ⟶ 0100 1111 1001 父代基因串1 \ \ \ \ \ \boxed{1010}\ \boxed{1111} \ \boxed{1111} \longrightarrow \boxed{1010}\ \boxed{1001} \ \boxed{1111} \\ 父代基因串2 \ \ \ \ \ \boxed{0100}\ \boxed{1001} \ \boxed{1001}\longrightarrow \boxed{0100}\ \boxed{1111} \ \boxed{1001} 父代基因串1 1010 1111 1111⟶1010 1001 1111父代基因串2 0100 1001 1001⟶0100 1111 1001
变异算子:在交叉后的子代个体中~~(不能直接变异吗)~~ ,某基因位有可能发生突变转换成同位基因,一般突变的方式一样(二进制编码取反或者加上一个服从均匀分布、指数分布、正态分布的数值)
算法停止的准则一般采用设定最大迭代次数或者适应值函数评估次数,或者是规定的搜索精度。
遗传算法(genetic algorithm, GA)、种群(population)、个体(individual)、适应度(fitness)、交叉(crossover)、变异(mutation)、基因(gene)、父代(parent)、子代(offspring)、模式定理(schema theory)、积木块(building block)、多目标优化问题(multi-objective optimization problem,MOP)
模板属性 | 定义 | 以 ∗ 0 ∗ 10 ∗ 1 ∗ *0*10*1* ∗0∗10∗1∗为例 |
---|---|---|
原始长度 L ( H ) L(H) L(H) | 模板中总的基因位数 | L ( H ) = 8 L(H)=8 L(H)=8 |
定义距 δ ( H ) \delta(H) δ(H) | 模板中从左到右第一确定字符到最后一个确定字符的距离 | δ ( H ) = 6 − 1 = 5 \delta(H)=6-1=5 δ(H)=6−1=5 |
阶数 o ( H ) o(H) o(H) | 模板中包含的确定字符的个数 | o ( H ) = 4 o(H)=4 o(H)=4 |
容量 D ( H ) D(H) D(H) | 模板包含字符串个数 | D ( H ) = 2 4 = 16 D(H)=2^4=16 D(H)=24=16 |
精英选择:经典遗传算法中,每一个个体都有被淘汰的概率,而保留每一代的最高适应度个体就是精英保留策略
锦标赛算法:从种群中随机选择k个个体(k
多种群遗传算法:保持多个种群同时进化,种群之间通过移民方式交换基因
单形杂交*[3]*:从种群中选出n+1个个体作为父体,其基因作为向量在n维欧式空间形成单形,将单形沿各方向扩张得到子代。
球形杂交*[4]*:给定 X = ( x 1 , x 2 , . . . , x n ) X=(x_1,x_2,...,x_n) X=(x1,x2,...,xn)和 Y = ( y 1 , y 2 , . . . , y n ) Y=(y_1,y_2,...,y_n) Y=(y1,y2,...,yn)两个父体的基因,以
z j = a x j 2 + ( 1 − a ) y j 2 , j ∈ [ 1 , n ] z_j = \sqrt{ax^2_j+(1-a)y^2_j},\ \ \ \ \ j\in[1,n] zj=axj2+(1−a)yj2, j∈[1,n]
的方式产生一个后代 Z = ( z 1 , z 2 , . . . , z n ) Z=(z_1,z_2,...,z_n) Z=(z1,z2,...,zn)
import random
from operator import itemgetter
import matplotlib.pyplot as plt
import numpy as np
class Gene:
def __init__(self, **data):
self.__dict__.update(data) #在__dict__属性中更新data
self.size = len(data['data'])
class GA:
def __init__(self, parameter):
#包括自变量可取的最大值,最小值,种群大小,交叉率,变异率,繁殖代数
#parameter = [CXPB, MUTPB, NGEN, popsize, low, up]
self.parameter = parameter
self.NGEN = self.parameter[2]
self.CXPB = self.parameter[0]
self.MUTPB = self.parameter[1]
low = self.parameter[4]
up = self.parameter[5]
self.bound = []
self.bound.append(low)
self.bound.append(up)
pop = []
for i in range(self.parameter[3]): #生成popsize的种群个数
geneinfo = []
for pos in range(len(low)): #生成len(low)维的随机数据作为个体初始值
geneinfo.append(random.randint(self.bound[0][pos],self.bound[1][pos]))
fitness = self.evaluate(geneinfo) #计算生成的随机个体的适应度函数值
pop.append({'Gene':Gene(data=geneinfo), 'fitness':fitness})
self.pop = pop
self.bestindividual = self.selectBest(self.pop) #保存最好的个体数据{'Gene':Gene(), 'fitness':fitness}
def evaluate(self, geneinfo):
#作为适应度函数评估该个体的函数值
x1 = geneinfo[0]
x2 = geneinfo[1]
x3 = geneinfo[2]
x4 = geneinfo[3]
y = np.exp(x1 + x2 ** 2) + 100*np.sin(x3 ** 2) + x4**2
return y
def selectBest(self, pop):
#选出当前代种群中的最好个体作为历史记录
s_inds = sorted(pop, key=itemgetter("fitness"), reverse=True) #从大到小排列
return s_inds[0] #返回依然是一个字典
def selection(self, individuals, k):
#用轮盘赌的方式,按照概率从上一代选择个体直至形成新的一代
#k表示需要选出的后代个数
s_inds = sorted(individuals, key=itemgetter("fitness"), reverse=True)
sum_fits = sum(ind['fitness'] for ind in individuals) #计算所有个体适应度函数的和
chosen = []
for i in range(k):
u = random.random() * sum_fits #随机产生一个[0,sun_fits]范围的数,即轮盘赌这局的指针
sum_ = 0
for ind in s_inds:
sum_ += ind['fitness'] #逐次累加从大到小排列的个体的适应度函数的值,直至超过指针,即选择它
if sum_ >= u:
chosen.append(ind)
break
chosen = sorted(chosen, key=itemgetter('fitness'), reverse=False) #从小到大排列选择的个体,方便进行交叉操作
return chosen
def crossperate(self, offspring):
#实现交叉操作,交换倆数据随机两维之间的数据
dim = len(offspring[0]['Gene'].data) #获得数据维数,即基因位数
geneinfo1 = offspring[0]['Gene'].data #交叉的第一个数据
geneinfo2 = offspring[1]['Gene'].data #交叉的第二个数据
if dim == 1:
pos1 = 1
pos2 = 1
else:
pos1 = random.randrange(1,dim)
pos2 = random.randrange(1,dim)
newoff1 = Gene(data=[]) #后代1
newoff2 = Gene(data=[]) #后代2
temp1 = []
temp2 = []
for i in range(dim):
if min(pos1, pos2) <= i < max(pos1,pos2): #交换的部分维度
temp1.append(geneinfo2[i])
temp2.append(geneinfo1[i])
else:
temp1.append(geneinfo1[i])
temp2.append(geneinfo2[i])
newoff1.data = temp1
newoff2.data = temp2
return newoff1, newoff2
def mutation(self, crossoff, bound):
#实现单点编译,不实现逆转变异
dim = len(crossoff.data)
if dim == 1:
pos = 0
else:
pos = random.randrange(0, dim) #选择单点变异的点
crossoff.data[pos] = random.randint(bound[0][pos], bound[1][pos])
return crossoff
def GA_main(self):
popsize = self.parameter[3]
print('Start of evolution')
ever_best = []
for g in range(self.NGEN):
print("############ Generation {} ##############".format(g))
#首先进行选择
selectpop = self.selection(self.pop, popsize)
nextoff = []
while len(nextoff) != popsize:
offspring = [selectpop.pop() for _ in range(2)] #后代间两两选择
if random.random() < self.CXPB: #进行交叉的后代
crossoff1, crossoff2 = self.crossperate(offspring)
if random.random() < self.MUTPB:
muteoff1 = self.mutation(crossoff1, self.bound)
muteoff2 = self.mutation(crossoff2, self.bound)
fit_muteoff1 = self.evaluate(muteoff1.data)
fit_muteoff2 = self.evaluate(muteoff2.data)
nextoff.append({'Gene':muteoff1, 'fitness':fit_muteoff1})
nextoff.append({'Gene': muteoff2, 'fitness': fit_muteoff2})
else:
fit_muteoff1 = self.evaluate(crossoff1.data)
fit_muteoff2 = self.evaluate(crossoff2.data)
nextoff.append({'Gene': crossoff1, 'fitness': fit_muteoff1})
nextoff.append({'Gene': crossoff2, 'fitness': fit_muteoff2})
else:
nextoff.extend(offspring) #直接追加两个后代
self.pop = nextoff #种群直接变为下一代
fits = [ind['fitness'] for ind in self.pop]
best_ind = self.selectBest(self.pop)
if best_ind['fitness'] > self.bestindividual['fitness']:
self.bestindividual = best_ind
ever_best.append(self.bestindividual['fitness'])
print("Best individual found is {}, {}".format(self.bestindividual['Gene'].data,
self.bestindividual['fitness']))
print(" Max fitness of current pop: {}".format(max(fits)))
plt.plot(ever_best)
plt.show()
print('------------ End ----------------')
if __name__ == '__main__':
CXPB, MUTPB, NGEN, popsize = 0.8, 0.5, 100, 100
up = [90, 10, 5, 6]
low = [1, 2, 3, 1]
parameter = [CXPB, MUTPB, NGEN, popsize, low, up]
run = GA(parameter)
run.GA_main()
目标函数的表达式并不限于显示表达,只要能够通过一个函数(def)
返回适应度函数值就可以,其次在设计适应度函数时并不拘于最大值的形式,只要在后续更新历史最佳,以及选择个体时代码注意配合即可。
且此处实现的遗传算法是经典版的,没有做任何优化,甚至连精英策略也没有加,推荐大家学习的时候可以先根据范例代码学习一次,然后再找一个实际问题自己写一次,最好是类型不相同的问题(总不能改个适应度函数就说自己解决了一个新问题吧),或者可以将自己觉得有提点(这里用提点这个词好像很奇怪,哈哈哈哈)的优化方法加入其中,这些都是能加深对GA算法理解的。
[1] 群智能优化算法及应用/汤可宗,杨静宇著. ——北京:科学出版社,2015.7 ISBN 978-7-03-044740-1
[2] Rahnamayan S, Tizhoosh H R. A novel population initialization method for accelerating evolutionary algorithms[J]. Computers and Mathematics with Applications, 2007, 53(10): 1605-1614.
[3] 周育人,李元香等. Pareto强度值演化算法求解约束优化问题[J]. 软件学报,2003,14(7):1243-1249
[4] Schoemauer M, Michalewic z. Sphere operators and their applicability for constrained parameter optimization problems[C]. Proceedings of the 7th International Conference on Evolutionary Programming, 1998: 241-250.
[5] 遗传算法(五)——模式定理_SamYu-CSDN博客
https://blog.csdn.net/weixin_30239361/article/details/101695793
[6] 遗传算法系列之四:遗传算法的变种 - 云+社区 - 腾讯云
https://cloud.tencent.com/developer/article/1015614
[7] Python手把手构建遗传算法(GA)实现最优化搜索 - FINTHON
https://finthon.com/python-genetic-algorithm/
原书自带的参考文献直接给我搬过来了,用于以后详细了解时有个出处
这里就不遵循标准应用格式了