基本术语
种群:是一个候选解集合,可以有交叉,变异这样的遗传操作作用于他们。
候选解:给定问题的一个可能的解。
基因:组成染色体的不可分割的构建块。经典的二元基因包含0和1。
基因位点:表示一个基因在染色体中的位置,例如,S=1011,0的基因位是3(从左往右)。
染色体:染色体是由一串基因组合而成的。染色体定义了一个特定的候选解。用二进制编码一个典型的染色体可能包含“010001”这样的内容。
变异:一个过程,其中候选解中的基因被随机改变以创建新的性状。 常用的交叉算子有实值变异和二进制变异。
交叉:两个父代个体的部分结构加以替换重组而形成新个体的操作。常用的交叉算子有实值重组和二进制交叉。最常见的交叉算子为单点交叉。如
个
体
A
:
1100
↑
1
−
>
11000
新
个
体
个体A:1100↑1->11000 新个体
个体A:1100↑1−>11000新个体
个
体
B
:
0011
↑
0
−
>
00111
新
个
体
个体B:0011↑0->00111 新个体
个体B:0011↑0−>00111新个体
选择:这是选择的候选解,繁殖下一代解的技术。常用的选择算子有适应度比例方法、随机遍历抽样法、局部选择法。最常用的是轮盘赌选择法,即各个体的选择概率和其适应度值成比例。设群体大小为n,其中个体i的适应度为
f
i
f_i
fi,则
i
i
i被选择的概率为:
p
i
=
f
i
∑
f
i
p_{i} =\frac{ f_{i} }{ \sum{f_{i}} }
pi=∑fifi
搜索空间:处理优化问题时,有多个候选解需要搜索,我们就称解的集合是搜索空间。在搜索空间中有距离的概念,距离近的解更有可能表示出相似的特征。例如”101“和”111“距离为1,”000“和”111“距离为3。
特征值:基因的特征值与二进制的权一致,例如在S=1011中,基因位置3中的1对应的基因特征值为2。
适应度:一个评分,用于评定候选解适合给定问题的程度。交叉和变异是可以看做在搜索空间中跨出一步,得到的解有可能比亲代的适应度更差。如果解表现的足够差,最终会在选择过程中从基因库删除。适应度函数指的是对问题中的每一个染色体都能够度量的函数,这个函数是计算个体在群体中被使用的概率。
参数
变异率:特定基因将要变异的概率,变异率允许种群中有更多的遗传多样性,避免局部最优。但是过高会失去原有种群中的良好解,太低则会用过长的时间在搜索空间中移动,从而妨碍其找到一个满意解的时间。例如“100”和“101”如果没有变异,其后代只能来自于交叉,只有两个后代’100’和’101’。 变异率通常取值为
[
0.001
,
0.1
]
[0.001,0.1]
[0.001,0.1]
种群规模:即遗传算法中任意一代种群的个体数。较大的种群规模有利于搜素空间中的取样,但是会消耗过多计算资源,所以需要平衡。
交叉率:较高的交叉率有利于产生新的,潜在的优越的解,较低的交叉率有利于保证原种群中的良好解不受破坏。
常用遗传算法终止条件
1.到达世代的最大数目
2.超过分配给它的时间
3.发现一个满足所需条件的解
4.该算法到了一个稳定阶段
算法实现过程
1.初始化种群:随机提供整个搜索空间的均匀覆盖。
2.评估:为种群中的每一个个体分配一个适应度值,对种群进行评估,在这个阶段我们要注意当前最优解及种群的平均适应度。
3.判断:根据终止条件集判断是否该终止搜索。如果终止条件不满足,则转入下一个阶段。
4.选择:种群经过一个选择阶段,基于适应度评分,从种群中选择个体,适应度越高,个体就更有机会被选择。
5.对选择的个体进行交叉和变异,为下一代创建新的个体。
6.返回到评估,对当前种群重新评估,我们称以上过程为一个世代。预设代数一般为100-500代。
伪代码
generation = 0;
population[generation] = initializePopulation(populationSize);
evalutePopulation(population[generation]);
while isTerminatationConditionMet() == false do
parents = selectParents(population[generation]);
population[generation + 1] = crossover(parents);
population[generation + 1] = mutate(population[generation + 1]);
evaluatePopulation(population[generation]);
generation + +;
EndLoop
实例+Python实现
问题:求下列函数最大值: f ( x ) = 9 s i n ( 5 x ) + 8 c o s ( 4 x ) , 其 中 x ∈ [ 0 , 15 ] f(x) = 9sin(5x)+8cos(4x),其中x\in[0,15] f(x)=9sin(5x)+8cos(4x),其中x∈[0,15]
import math
import random
import numpy as np
import matplotlib.pyplot as pltclass Population(object):
# 初始化种群,num为每个种群的个体数量,chromlength为二进制编码长度
# left为定义域的左边界,right为定义域的右边界
def __init__(self, num, chromlength, left, right):
#empty返回一个指定大小的空矩阵,并以0或者1填满
pop = np.empty((num, chromlength),
dtype=np.uint)
for i in range(num):
for j in range(len(pop[0, :])):
pop[i, j] = random.randint(0, 1)
self.pop = pop
self.left = left
self.right = right # 定义种群二进制解码方式
def decodex(self):
# pop1用来将pop中的二进制数转换成十进制数,方便解码
pop1 = np.empty_like(self.pop)
for i in range(len(pop1[0, :])):
for j in range(len(pop1[:, 0])):
pop1[j, i] = self.pop[j, i] * math.pow(2, 9 - i)
# pop2用来统计pop1每行之和,pop[]表示行数列数,这里pop1表示的是基因位对应的特征值
pop2 = []
for i in range(len(pop1[:, 0])):
pop2.append(np.sum(pop1[i, :]))
pop2 = np.asarray(pop2, dtype=np.float)
# x将二值域中的数转换为变量域中的数,并计算目标函数值,pop2算的是行的指数和,即每条染色体的特征值和
#enumerate返回的是索引和对应列表值
for i, x in enumerate(pop2):
x = x * (self.right - self.left) / (math.pow(2, len(self.pop[0, :])) - 1)
pop2[i] = x
return pop2 # 解码后的相应函数值
def decodey(self):
pop = np.empty_like(self.decodex(),
dtype=np.float)
for i, x in enumerate(self.decodex()):
# 这里算的是各个染色体对应的函数值(带入)
pop[i] = 9 * math.sin(5 * x) + 8 * math.cos(4 * x)
return pop # 计算群体中每个个体的适应度
def fitness(self):
fit = []
# 适应度映射函数设为每个值减去群体中的最小值
for individual in self.decodey():
fit.append(individual -
np.min(self.decodey()))
fit = np.asarray(fit, dtype=np.float)
return fit # 种群个体的选择复制,采用轮盘赌选择法选择
def selection(self):
total = np.sum(self.fitness()) # 求适应度之和
# 防止种群内每个个体适应度一样使得total为0
if total == 0:
total = 1
pfit = self.fitness() / total # 单个个体被选择的概率
pfit = np.cumsum(pfit) # 个体的累计概率?这里不覆盖吗
ps = []
# 生成0-1的随机数,用来与个体被选择的概率进行比较
for i in range(len(pfit)):
ps.append(random.random())
ps.sort()
ps = np.asarray(ps)
# 生成新的种群
newpop = []
fitin = 0
newin = 0
while newin < len(pfit):
if ps[newin] < pfit[fitin]:
# copy.copy是浅拷贝,子对象不会拷贝,为原对象,即被选择概率比随机概率大的染色体我们就将其拷贝为新种群
newpop.append(copy.copy(self.pop
[fitin, :]))
newin += 1
else:
# 略过某行种群
fitin += 1
self.pop = np.asarray(newpop, dtype=np.uint) # 种群个体的交叉
def crossover(self, pc):
# 生成新的种群
newpop = np.empty_like(self.pop)
for i in range(0, len(self.pop[:, 0]), 2):
# 如果随机概率小于发生交叉的概率,则随机选择一段进行交叉,左交叉点和右交叉点
if random.random() < pc:
le = random.randint(0, len(self.pop[0, :]))
ri = random.randint(0, len(self.pop[0, :]))
if ri <= le:
le, ri = ri, le
newpop[i, :le] = copy.copy(self.pop
[i, :le])
newpop[i, le:ri] = copy.copy(self.pop
[i + 1, le:ri])
newpop[i, ri:] = copy.copy(self.pop
[i, ri:])
# 以上构成了第一段交叉新个体,下面为第二段新个体
newpop[i + 1, :le] = copy.copy(self.pop
[i + 1, :le])
newpop[i + 1, le:ri] = copy.copy(self.pop
[i, le:ri])
newpop[i + 1, ri:] = copy.copy(self.pop
[i + 1, ri:])
else:
newpop[i, :] = copy.copy(self.pop
[i, :])
newpop[i + 1, :] = copy.copy(self.pop
[i + 1, :])
self.pop = newpop # 种群个体的变异
def mutation(self, pm):
for i in range(len(self.pop[:, 0])):
# 如果随机概率小于发生变异的概率,则随机选择一点进行变异
if random.random() < pm:
# point指代一条染色体中的某个位置
point = random.randint(0, len(self.pop
[0, :]) - 1)
self.pop[i, point] = (self.pop[i, point] + 1) % 2
else:
pass
if __name__ == '__main__':
p=Population(20,10,left=0,right=15)#随机产生初始化群体
x0=[]#设置横坐标点
y0=[]#设置纵坐标点
for i in range(20):#设置迭代次数为20次
fitness=p.fitness()#计算群体中每个个体的适应度
#将最大适应度的点添加进x列表和y列表
x0.append(p.decodex()[np.argmax(fitness)])
y0.append(p.decodey()[np.argmax(fitness)])
p.selection()#进行选择复制操作
p.crossover(pc=0.7)#进行交叉操作,交叉概率为0.7
p.mutation(pm=0.1)#进行变异操作,变异概率为0.1
print(max(y0))
# 下面为画图操作
x=np.arange(0.,15.,0.0001)
y=np.empty_like(x)
for i,v in enumerate(x):
v0=9*math.sin(5*v)+8*math.cos(4*v)
y[i]=v0
plt.plot(x,y,label='aimfunction')
plt.plot(x0,y0,'*')
plt.legend(loc=3)
plt.axis([0,15,-18,18])
plt.show()```