应用
1、用在NLG(自然语言生成)技术中,比如自动生成宋词。推荐一篇论文(游维前辈写的):基于遗传算法的宋词自动生成研究
2、机器人的路径规划
3、自动排班,自动排课,车间调度等事件规划
4、组合优化问题,比如旅行商问题
5、
框架
1、生成种群染色体矩阵
调用ea.crtbp(这里的ea指代的是geatpy,以下同)方法,参数是种群个数和染色体长度。
Chrom = ea.crtbp(NIND, Lind) # 生成种群染色体矩阵
2、种群染色体矩阵解码为基因表现型矩阵
调用ea.bs2real方法,参数是种群染色体矩阵和译码矩阵
FieldD = ea.crtfld(Encoding, varTypes, ranges, borders, precisions, codes, scales) # 调用函数生成译码矩阵
variable = ea.bs2real(Chrom, FieldD) # 对初始种群进行解码,二进制/格雷编码矩阵到实数值矩阵的转换
3、定义适应度函数(求解函数)
4、把基因表现型矩阵代入适应度函数,并找出最优种群(最大值或者最小值)
ObjV = aim(variable) # 计算初始种群个体的目标函数值
best_ind = np.argmax(ObjV) # 计算当代最优个体的序号
5、根据适应度函数值排序进行适应度分配
FitnV = ea.ranking(-ObjV) # 根据目标函数大小分配适应度值(由于遵循目标最小化约定,因此最大化问题要对目标函数值乘上-1)
6、然后进行选择、交叉、变异三个操作
SelCh = Chrom[ea.selecting('rws', FitnV, NIND - 1), :] # 选择,采用'rws'轮盘赌选择
SelCh = ea.recombin('xovsp', SelCh, 0.7) # 重组(采用两点交叉方式,交叉概率为0.7)
SelCh = ea.mutbin(Encoding, SelCh) # 二进制种群变异
7、把经过第6步操作后的新种群和最优种群合并
Chrom = np.vstack([Chrom[best_ind, :], SelCh])
8、重复2-7,直到满足要求(遗传的代数达到一定次数,或者种群个数只剩下若干个)
for gen in range(MAXGEN):
python实现
"""demo.py"""
import numpy as np
import geatpy as ea # 导入geatpy库
import matplotlib.pyplot as plt
import time
"""============================目标函数============================"""
def aim(x): # 传入种群染色体矩阵解码后的基因表现型矩阵
return x * np.sin(10 * np.pi * x) + 2.0
x = np.linspace(-1, 2, 200)
plt.plot(x, aim(x)) # 绘制目标函数图像
"""============================变量设置============================"""
x1 = [-1, 2] # 自变量范围
b1 = [1, 1] # 自变量边界
varTypes = np.array([0]) # 自变量的类型,0表示连续,1表示离散
Encoding = 'BG' # 'BG'表示采用二进制/格雷编码
codes = [1] # 变量的编码方式,2个变量均使用格雷编码
precisions = [4] # 变量的编码精度
scales = [0] # 采用算术刻度
ranges = np.vstack([x1]).T # 生成自变量的范围矩阵
borders = np.vstack([b1]).T # 生成自变量的边界矩阵
"""=========================遗传算法参数设置========================="""
NIND = 40; # 种群个体数目
MAXGEN = 25; # 最大遗传代数
FieldD = ea.crtfld(Encoding, varTypes, ranges, borders, precisions, codes, scales) # 调用函数生成译码矩阵
Lind = int(np.sum(FieldD[0, :])) # 计算编码后的染色体长度
obj_trace = np.zeros((MAXGEN, 2)) # 定义目标函数值记录器
var_trace = np.zeros((MAXGEN, Lind)) # 定义染色体记录器,记录每一代最优个体的染色体
"""=========================开始遗传算法进化========================"""
start_time = time.time() # 开始计时
Chrom = ea.crtbp(NIND, Lind) # 生成种群染色体矩阵
variable = ea.bs2real(Chrom, FieldD) # 对初始种群进行解码,二进制/格雷编码矩阵到实数值矩阵的转换
ObjV = aim(variable) # 计算初始种群个体的目标函数值
best_ind = np.argmax(ObjV) # 计算当代最优个体的序号
# 开始进化
for gen in range(MAXGEN):
FitnV = ea.ranking(-ObjV) # 根据目标函数大小分配适应度值(由于遵循目标最小化约定,因此最大化问题要对目标函数值乘上-1)
SelCh = Chrom[ea.selecting('rws', FitnV, NIND - 1), :] # 选择,采用'rws'轮盘赌选择
SelCh = ea.recombin('xovsp', SelCh, 0.7) # 重组(采用两点交叉方式,交叉概率为0.7)
SelCh = ea.mutbin(Encoding, SelCh) # 二进制种群变异
# 把父代精英个体与子代合并
Chrom = np.vstack([Chrom[best_ind, :], SelCh])
variable = ea.bs2real(Chrom, FieldD) # 对育种种群进行解码(二进制转十进制)
ObjV = aim(variable) # 求育种个体的目标函数值
# 记录
best_ind = np.argmax(ObjV) # 计算当代最优个体的序号
obj_trace[gen, 0] = np.sum(ObjV) / NIND # 记录当代种群的目标函数均值
obj_trace[gen, 1] = ObjV[best_ind] # 记录当代种群最优个体目标函数值
var_trace[gen, :] = Chrom[best_ind, :] # 记录当代种群最优个体的变量值
# 进化完成
end_time = time.time() # 结束计时
"""============================输出结果及绘图================================"""
best_gen = np.argmax(obj_trace[:, [1]])
print('目标函数最大值:', obj_trace[best_gen, 1]) # 输出目标函数最大值
variable = ea.bs2real(var_trace[[best_gen], :], FieldD) # 解码得到表现型
print('对应的决策变量值为:')
print(variable[0][0]) # 因为此处variable是一个矩阵,因此用[0][0]来取出里面的元素
print('用时:', end_time - start_time)
plt.plot(variable, aim(variable), 'bo')
ea.trcplot(obj_trace, [['种群个体平均目标函数值', '种群最优个体目标函数值']])
优缺点
1、优点:擅长解决目标未知或难以描述、但却知道怎么评估的一类问题。具有很好的收敛性,在计算精度要求时,计算时间少,鲁棒性高等。
2、缺点:容易产生早熟收敛的问题,稳定性差,对非线性约束问题难以处理。
案例实现(TSP问题)
问题描述
一个外国的旅行商要走遍中国34个省会,现在知道全国省会的经纬度坐标,要用遗传算法算出怎么走的路程最短。
问题参数
1、种群:打乱了的城市序列
2、种群数量:打乱了的城市序列的数量
3、染色体长度:城市的数量(34)
4、适配函数:一个打乱了的城市序列的按序列号计算的总距离
5、交叉概率:
6、突变概率:
7、最优子代:
8、遗传代数:
需掌握的知识点
1、random.random():随机生成 0 到 1 之间的浮点数[0.0, 1.0)。用于判断是否进行交叉或者变异操作。
2、random.shuffle(items):把列表 items 中的元素随机打乱。用于打乱城市序列。
3、random.randint(a , b):随机生成 a 与 b 之间的整数[a, b]。用于进行交叉或者变异操作时确定哪个部分发生改变。
4、list.extend(sublist):在list列表里一次性往末尾添加一个列表sublist。用于进行交叉操作时生成新种群。
5、list.append(a):在list列表的末尾添加一个字符a。用于进行交叉或者变异操作时确定哪个部分发生改变。
6、string.replace(old,new):将字符串string里面的old字符串替换为new字符串,用于读取文本行数据时去掉回车。
7、
代码实现
import random
import math
import matplotlib.pyplot as plt
shortestdistance = float('inf')
bestorder = []
class GA(object):
def __init__(self,aCrossRate, aMutationRate, aLifeCount, aGeneLength, aMatchFun = lambda life : 1):
self.crossrate = aCrossRate #交叉概率
self.mutationrate = aMutationRate #变异概率
self.lifecount = aLifeCount #种群数量
self.lives = [] #存放单位,种群及其分数为一个单位
self.genelength = aGeneLength #染色体长度
self.matchfun = aMatchFun #适应度函数
self.bestone = None #最优种群
self.generationcount = 1
self.initpopulation()
def initpopulation(self):#种群初始化
for lc in range(self.lifecount):#产生多少种群
gene = list(range(self.genelength))#城市序列,0-33
random.shuffle(gene) #将城市序列打乱
life = Population(gene) #将种群及其分数看成一个整体
self.lives.append(life)#将这个整体放进单位里面
def judge(self):
global shortestdistance
global bestorder
self.bestone = self.lives[0]
for life in self.lives:
life.score = self.matchfun(life)
if(life.score < self.bestone.score):
self.bestone = life
if(self.bestone.score < shortestdistance):
shortestdistance = self.bestone.score
bestorder = self.bestone.gene
def cross(self,parent1,parent2):
index1 = random.randint(0,self.genelength-1)
index2 = random.randint(index1,self.genelength-1)
tempgene = parent2.gene[index1:index2]
new_gene = []
for gl in range(self.genelength):
if gl == index1:
new_gene.extend(tempgene)
if parent1.gene[gl] not in tempgene:
new_gene.append(parent1.gene[gl])
return new_gene
def mutation(self,gene):
index1 = random.randint(0,self.genelength-1)
index2 = random.randint(0,self.genelength-1)
gene[index1],gene[index2] = gene[index2],gene[index1]
return gene
def getone(self):
index = random.randint(0,self.genelength-1)
life = self.lives[index]
return life
def new_one(self):
parent1 = self.getone()
aCrossrate = random.random()
if self.crossrate > aCrossrate:
parent2 = self.getone()
gene = self.cross(parent1,parent2)
else:
gene = parent1.gene
aMutationrate = random.random()
if self.mutationrate > aMutationrate:
gene = self.mutation(gene)
return Population(gene)
def next_generation(self):
self.judge()
new_lives = []
new_lives.append(self.bestone)
while len(new_lives) < self.lifecount:
new_lives.append(self.new_one())
self.lives = new_lives
self.generationcount += 1
class Population(object):
gene_score = -1
def __init__(self,aGene=None):
self.gene = aGene
self.score = self.gene_score
class TSP_GA(object):
def __init__(self):
self.initcitys()
self.ga = GA(aCrossRate=0.7,
aMutationRate=0.02,
aLifeCount=200,
aGeneLength=34,
aMatchFun=self.distancefunc())
def initcitys(self):
self.citys = []
f = open(r'distanceMatrix.txt')
while True:
jwcity = str(f.readline())
if jwcity:
pass
else:
break
jwcity = jwcity.replace("\n","")
jwcity = jwcity.split("\t")
self.citys.append((float(jwcity[1]),float(jwcity[2]),jwcity[0]))
def distance(self, order):
distance = 0.0
# i从-1到32,-1是倒数第一个
for i in range(-1, len(self.citys) - 1):
index1, index2 = order[i], order[i + 1]
city1, city2 = self.citys[index1], self.citys[index2]
distance += math.sqrt((city1[0] - city2[0]) ** 2 + (city1[1] - city2[1]) ** 2)
return distance
# 适应度函数,因为我们要从种群中挑选距离最短的,作为最优解,所以(1/距离)最长的就是我们要求的
def distancefunc(self):
return lambda life: self.distance(life.gene)
def run(self):
while self.ga.generationcount < 1000:
self.ga.next_generation()
distance = self.distance(self.ga.bestone.gene)
print(("%d : %f") % (self.ga.generationcount, distance))
print("经过%d次迭代,最后最优解距离为:%f" % (self.ga.generationcount, distance))
print(self.ga.bestone.gene)
print("遍历城市顺序为:")
for i in self.ga.bestone.gene:
print(self.citys[i][2])
self.drawline(self.ga.bestone.gene)
print("经过%d次迭代,全局最优解距离为:%f" % (self.ga.generationcount, shortestdistance))
print(bestorder)
print("遍历城市顺序为:")
for i in bestorder:
print(self.citys[i][2])
self.drawline(bestorder)
def drawline(self,order):
plot_x_set = []
plot_y_set = []
for i in order:
# plt.plot(coordinates[i])
plot_x_set.append(self.citys[i][0])
plot_y_set.append(self.citys[i][1])
plt.plot(plot_x_set, plot_y_set, 'r')
plt.scatter(plot_x_set, plot_y_set, )
# for i in order:
# plt.annotate(i, xy=(self.citys[i][0], self.citys[i][1]))
# 首尾2个点的连线
x = [plot_x_set[0], plot_x_set[-1]]
y = [plot_y_set[0], plot_y_set[-1]]
plt.plot(x, y, 'k') # 用黑色标识
plt.show()
if __name__ == '__main__':
tsp = TSP_GA()
tsp.run()
所遇问题
效果不好,有待改进,为什么不好呢。