遗传算法介绍

遗传算法是一种全局仿生优化算法,通过模拟环境和生物种群之间的相互作用以改进传统搜索算法。

生物学概念和算法概念之间的对应关系

生物学概念算法概念
种群解的编码集合
环境阻力适应度函数
种群适应环境的能力目标函数
通过环境的筛选保留优势个体,完成种群的进化通过一定的选择规则保留优质解,最终找到最优解

种群—编码集合

通过对 解空间中的解进行编码,一个编码代表一个解,编码构成构成 编码空间 代表解空间
算法开始时,需要有一个 初始种群 ,初始种群就是随机从编码空间中抽取 编码 所构成的编码空间子集

种群适应环境的能力—目标函数

目标函数是 根据所求解的问题得出的,比如在TSP问题中是所经过城市 路线的长度,目标函数值直接反映了解的质量
对应到生物学概念中就是种群中个体适应环境的能力

环境阻力—适应度函数

适应度函数是 对解质量的描述,即通过函数的方式 将解的质量 映射到 0-1之间,方便后续对解的选择

TSP问题简介

TSP问题全称 Travelling Salesman Problem (旅行商问题)
给出城市坐标,求解旅行商 如何每个城市仅经过一次就可以遍历所有城市,而且要求路径长度最短

遗传算法中TSP问题的处理

城市坐标

城市坐标城市坐标城市坐标
0(18,54)10(71,44)20(7,64)
1(87,76)11(64,60)21(22,60)
2(74,78)12(68,58)22(25,62)
3(71,71)13(83,69)23(62,32)
4(25,38)14(58,69)24(87,7)
5(58,35)15(54,62)25(91,38)
6(4,50)16(51,67)26(83,46)
7(13,40)17(37,84)27(41,26)
8(18,40)18(41,94)28(45,21)
9(24,42)19(2,99)29(44,35)

编码

城市编号从0至29,而且需要记录30个城市的顺序,所以采用实数编码
具体形式如下:
		编码空间中的一个编码,也就是解空间中的一个解,也是种群中的一个个体
		用向量表示
				path = [20, 1, 3, 7, 14, 25, 10, 2, 27, 24, 8, 0, 16, 26, 22, 5, 18, 28, 4, 19, 13, 12, 11, 21, 6, 23, 29, 9, 17, 15]
		该向量含义:
		从城市20开始,经历城市1,3,7, 14, 25, 10, 2, 27, 24, 8, 0, 16, 26, 22, 5, 18, 28, 4, 19, 13, 12, 11, 21, 6, 23, 29, 9, 17 ,到城市15再回到20结束

遗传算法中参数和函数设计

目标函数

将路径长度函数作为目标函数,两城市间 距离按照欧式距离(向量差的二范数)计算
定义城市 i 和城市 j 之间的距离,用Xi代表第i个城市的坐标,Xj代表第j个城市的坐标

d i s t ( i , j ) = ∥ X i − X j ∥ 2 dist(i,j)= \left\|Xi-Xj\right\|_2 dist(i,j)=XiXj2

定义推销员走过的路径总长度,path[i]代表路径中经过的第i个城市的编号,数据可以参考上一段中编码的内容
path作为一个可行解

t a r g e t f u n c ( p a t h ) = ∑ i = 0 28 d i s t ( p a t h [ i ] , p a t h [ i + 1 ] ) + d i s t ( p a t h [ 29 ] , p a t h [ 0 ] ) targetfunc(path) = \sum_{i=0}^{28} dist(path[i],path[i+1]) + dist(path[29],path[0]) targetfunc(path)=i=028dist(path[i],path[i+1])+dist(path[29],path[0])

适应度函数

简单的适应度函数 要根据 需要的问题设计
比如在TSP问题中,要求找到的解的目标函数值越小,对解的评价越好,这样就可以直接使用 目标函数值的倒数作为适应度函数值
下列公式中path作为一个可行解

f i t n e s s ( p a t h ) = 1 t a r g e t f u n c ( p a t h ) fitness(path) = \frac{1}{targetfunc(path)} fitness(path)=targetfunc(path)1

也可以自己设计适应度函数,比如

f i t n e s s ( p a t h ) = 1 1 + e A ⋅ t a r g e t f u n c ( p a t h ) − m e a n m a x − m i n + B fitness(path) = \frac{1}{1+e^{A·\frac{targetfunc(path)-mean}{max-min}+B}} fitness(path)=1+eAmaxmintargetfunc(path)mean+B1

借鉴了sigmoid函数的形式,并对数据做了最大最小标准化,A、B是人为给定的常系数
mean、max、min是种群所有个体的目标函数值的均值、最大值、最小值
图像如下A=5,B=0
目标函数值小于均值的个体有很大的概率被训责进入下一代种群中
目标函数值较大的个体则被选择进入下一代种群中的概率很小

在这里插入图片描述

算法流程图

适应度计算中包含了目标函数值的计算

在这里插入图片描述

交叉操作

交叉操作 模拟的是生物将染色体遗传到下一代中时,染色体的交叉现象
在遗传算法中,交叉操作 是为实现算法的区域随机搜索功能而存在的
执行交叉操作时,随机将种群中的个体两两配对,按照交叉概率(Pc)选择是否对一对个体进行染色体片段的交换

简单介绍两种交叉操作的方式:
	假设某问题的一对待交叉个体的编码是(| |标记的是交叉位点):
						X1 = 1 2 |4 6 3| 9 8
						X2 = 4 2 |9 1 8| 3 6
		顺序交叉:
			将X1 的交换片段取出
						X1‘ = _ _ |4 6 3| _ _
			使用X2中不与X1’中相等的元素,按原顺序填充到X1‘中
			X2中不与X1’中相等的元素
							  _ 2 |9 1 8| _ _
							  
			得到			X1‘ = 2 9 |4 6 3| 1 8
			同理可得到	X2’ = 2 4 |9 1 8| 6 3
			
		匹配交叉:
			将X1与X2的交换片段直接交换,若有重复元素,则用交换片段的对位 替换非交换片段中的重复元素
						X1‘ = 1 2 |9 1 8| 9 8
						X2’ = 4 2 |4 6 3| 3 6
			很明显X1中9 1 8 均重复,所以使用4 6 3 替换掉非交换片段中的9 1 8
					   X1‘’ = 6 2 |9 1 8| 4 3
			同理可得
					   X2‘’ = 9 2 |4 6 3| 8 1

变异操作

变异操作 模拟生物遗传时的 基因突变现象
遗传算法中,变异操作保证了个体(解,也是编码)的多样性,保证算法能在足够多的方向上探索可能的解
执行变异操作时,对每一个个体(解,也是编码),根据变异概率(Pm)决定是否将编码的某一个元素用另一个元素代替掉

借用介绍交叉操作时用的例子:
					X1 = 1 2 4 6 3 9 8
若满足变异条件,则随机选择一个元素a,再随机选择另一个元素b,用b替换a,为保证X1中无重复元素也用a替换b
假设a=2,b=3,则变异后的X1:
				   X1’ = 1 3 4 6 2 9 8

选择操作

选择操作 模拟环境对生物种群中个体的选择,适者生存
选择比例:根据一定的选择规则和策略对每个个体进行筛选,选择出的进入下一代种群的个体数量占原种群的比例

种群的相关参数

种群规模  Np = 200
交叉概率  Pc = 0.4
变异概率  Pm = 0.01
选择比例  C  = 0.35
最大迭代次数  G = 1000   ——> 也就是终止规则

编程实现

编程思路

采用函数式编程
程序中 一个解(编码)以列表的形式储存,数据类型是 list,元素数据类型是int
处理路径长度时,将城市间的距离以二维列表的形式储存起来,计算解的目标函数值时仅相加就可以,数据类型是list,list的元素数据类型是list
选择操作使用轮盘赌的方式实现,对每一个解,选择时,使用random模块随机产生一个0-1之间的数p,若p小于这个解的适应度值,则选择这个解进入下一代解集中

代码

使用的是jupyter代码
In[1]:
	locations = [
	    [18,54],[87,76],[74,78],[71,71],[25,38],
	    [58,35],[4,50],[13,40],[18,40],[24,42],
	    [71,44],[64,60],[68,58],[83,69],[58,69],
	    [54,62],[51,67],[37,84],[41,94],[2,99],
	    [7,64],[22,60],[25,62],[62,32],[87,7],
	    [91,38],[83,46],[41,26],[45,21],[44,35]
	]
In[2]:
	import numpy as np
	
	def dist_cal(x,y):
	    return ((x[0]-y[0])**2+(x[1]-y[1])**2)**0.5
	#距离矩阵
	dist_mat = np.zeros((30,30))
	for i in range(0,30):
	    for j in range(i+1,30):
	        tmp = dist_cal(locations[i],locations[j])
	        dist_mat[i][j] = tmp
	        dist_mat[j][i] = tmp
In[3]:
	import math
	#目标函数
	def target_fuc(order):
	    global dist_mat
	    sum_dist = 0
	    for i in range(1,len(order)):
	       sum_dist += dist_mat[order[i]][order[i-1]]
	    return sum_dist
	
	#适应度函数
	def fitness(dist):
	    m,me = max(dist)-min(dist),sum(dist)/len(dist)
	    def sigmoid(x):
	        return 1/(1+math.exp(5*x))
	    temp = map(lambda x:(x-me)/m,dist)
	    fit_values = [sigmoid(i) for i in temp]
	    return fit_values
In[4]:
	import random
	#实现选择操作
	def choice(target_values,Np,pd):
	    #轮盘赌的实现
	    re = copy.deepcopy(target_values)
	    i = 0
	    while len(re)/Np > pd:
	        k = random.random()
	        for i in re:
	            if i<k:
	                re.remove(i)
	    return [target_values.index(item) for item in re]
In[5]:
	#实现交叉操作函数
	import copy
	def op(l1,l2):
	    len_v = len(l1)
	    oops = random.randint(0,len_v-3)
	    for t in range(oops,oops+len_v//10):
	        elem1 = l1[t]
	        elem2 = l2[t]
	        k = l1.index(elem2)
	        l1[t] = l2[t]
	        l2[t] = l2[k]
	        l2[k] = l1[k]
	        l1[k] = elem1
	def change(pc,target_exam):
	    target_vecs = copy.deepcopy(target_exam)
	    nums = 0
	    len_t = len(target_vecs)
	    #交叉操作以及映射
	    #对选中的P群体进行交叉操作
	    for i in range(len_t-1):
	        for j in range(i+1,len_t):
	            k = random.random()
	            if k > pc:
	                continue
	            op(target_vecs[i],target_vecs[i+1])
	    return target_vecs
In[6]:
	#实现变异操作
	def mutate(pm,target_data):
	    target = copy.deepcopy(target_data)
	    len_v = len(target)
	    for i in range(len_v):
	        k = random.random()
	        if k > pm:
	            continue
	        id1 = random.randint(0,len_v-1)
	        id2 = random.randint(0,len_v-1)
	        temp = target[id1]
	        target[id1] = target[id2]
	        target[id2] = temp
	    return target
In[7]:
	#随机初始化种群
	def rand_creat_group(Np,len_vec):
	    res = []
	    for i in range(Np):
	        temp = []
	        while len(temp)<len_vec:
	            ite = random.randint(0,len_vec-1)
	            if ite not in temp:
	                temp.append(ite)
	        res.append(temp)
	    return res
In[8]:
	#参数
	Np = 200
	pc = 0.4
	pm = 0.01
	pd = 0.35
	#初始种群
	group = rand_creat_group(Np,len(locations))
In[9]:
	G = 1000
	processing_array = dict()
	min_path = list()
	gmin = 1e5
	len_vec = len(group[0])
	group1 = group
	while G>0:
	    G -= 1
	    #计算访问顺序总距离
	    target_values = []
	    for item in group1:
	        tmp = target_fuc(item)
	        target_values.append(tmp)
	        #记录每次迭代的最优路径
	        if gmin > tmp:
	            gmin = tmp
	            min_path.append(copy.deepcopy(item))
	    fitness_values = fitness(target_values)
	    #存放每一次迭代的结果
	    processing_array[1000-G] = min(target_values)
	    #根据适应值选取遗传个体
	    choice_ids = choice(fitness_values,Np,pd)
	    choice_group = list()
	    for i in choice_ids:
	        choice_group.append(group1[i])
	    temp_group = copy.deepcopy(choice_group)
	    #实现交叉遗传,扩张种群和维持种群规模不变
	    temp_group += change(pc,choice_group)
	    while len(temp_group)<Np:
	        temp_group += copy.deepcopy(temp_group)
	    temp_group = temp_group[:200]
	    #对种群实现变异
	    for i in range(len(temp_group)):
	        temp_group[i] = mutate(pm,temp_group[i])
	    group1 = copy.deepcopy(temp_group)
In[10]:
	#结果可视化
	import matplotlib.pyplot as plt
	generties = list(processing_array.keys())
	dist_square = list(processing_array.values())
In[11]:
	plt.rcParams['figure.figsize'] = (20,10)
	plt.title('Np={} pc={} pm={} pd={} G={} min_dist={}'.format(Np,pc,pm,pd,1000,min(dist_square)))
	plt.plot(generties,dist_square)
	plt.xlabel('Generations')
	plt.ylabel('Distance of path')
	#plt.savefig('./result/{}_{}_{}_{}_{}_{}_5_blog.png'.format(Np,pc,pm,pd,100,2))
Out[11]:

在这里插入图片描述

路径可视化

路径可视化gif

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐