概述
优化问题是指在满足一定条件下,在众多方案或参数之中寻找最优方案或参数值,以使得某个或多个功能指标达到最优解,或使系统的某些性能指标达到最大值或最小值。
进化类算法
自然界的生物体在遗传选择和变异等一系列作用下优胜劣汰,不断的由低级向高级进化发展,人们将这种适者生存的进化规律的实质加以模式化而构成一种优化算法,即进化算法。
遗传算法1
遗传算法(Genetic Algorithm,GA)是模拟生物在自然环境中的遗传进化的过程而形成的自适应全局优化搜索算法。它模仿了生物遗传学的理论,通过自然选择、遗传和变异等作用机制,实现各个个体适应性的提高。
关键参数说明
种群规模
种群规模将影响遗传优化的最终结果以及遗传算法的执行效率。当种群规模太小时,遗传优化性能一般会不好。采用较大的种群规模可以减小遗传算法陷入局部最优解的机会,但较大的种群规模意味着计算复杂度较高。一般取10~200。
交叉概率
交叉概率控制着交叉操作被使用的额度。较大的交叉概率可以增强遗传算法开辟新的搜索区域的能力,但搞性能的模式遭到破坏的可能性增大;若交叉概率太低,遗传算法搜索可能陷入迟钝状态。一般取0.25~1.00。
变异概率
变异在遗传算法中属于辅助性的搜索操作,它的主要目的是保持群体的多样性。一般低频度的变异可以防止群体中重要基因的可能丢失,高频度的变异将是遗传算法趋于纯粹的随机搜索。通常取0.001~0.1。
遗传运算的终止进化代数
终止进化代数是表示遗传算法运行结束条件的一个参数,它表示遗传算法运行到指定的进化代数就停止运行,并将当前群体中的最佳个体作为所求问题的最优解输出。一般视具体问题而定,可以取100~1000。
直接从实例入手试试。
- 用标准遗传算法求函数的最大值,其中的取值范围为。这是一个有多个局部极值的函数,其函数值图形如图所示。
- 解:仿真过程如下:
(1)初始化种群数目为,染色体二进制编码长度为,最大进化代数设置为,交叉概率,变异概率为。
(2)产生初始种群,将二进制编码转换成十进制,计算个体适应度值,并进行归一化;采用基于轮盘赌的选择操作、基于概率的交叉和变异操作,产生新的种群,并把历代的最优个体保留在新种群中,进行下一步遗传操作。
(3)判断是否满足终止条件:若满足,则结束搜索过程,输出优化值;若不满足,则继续进行迭代优化。 - 计算函数的最小值,其中个体x的维数。
解:同上,只需要将评估函数更换即可。
由于该题求最小值,我们已知最小值,则将函数结果的倒数作为适应度返回即可;
函数结果时会报除零错误,判断可得当有且只有一个为1其余全为0时,倒数为1且为最大合法适应度,所以使函数结果为0时返回2即可。
from matplotlib import pyplot as plt
import numpy as np
import random
from operator import itemgetter
plt.rcParams['font.sans-serif'] = ['SimHei'] # 显示中文
# 1. 用标准遗传算法求函数f(x)=x+10sin(5x)+7cos(4x)的最大值,其中x的取值范围为[0,10]。
# 2. 求Σᵢ₌₁ⁿ xᵢ²(-20<=xᵢ<=20)的最小值,其中个体x的维数为10.
def draw():
# 第一题的函数图像
x = np.arange(0, 10, 0.001)
y = x + 10 * np.sin(5 * x) + 7 * np.cos(4 * x)
plt.xlabel('x')
plt.ylabel('f(x)')
plt.plot(x, y)
plt.show()
class Gene:
def __init__(self, **data):
self.__dict__.update(data)
self.size = len(data['data'])
class GA:
def __init__(self, parameter):
# parameter = [Pc, Pm, G, NP, low, up]
self.parameter = parameter
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]):
geneinfo = []
for pos in range(len(low)):
geneinfo.append(random.randint(self.bound[0][pos], self.bound[1][pos])) # 初始化种群
fitness = self.evaluate(geneinfo) # 评估染色体(chromosome)的适应度
pop.append({'Gene': Gene(data=geneinfo), 'fitness': fitness}) # 记录染色体及其适应度
self.pop = pop
self.bestindividual = self.selectBest(self.pop) # 记录种群中最优的染色体
def evaluate(self, geneinfo):
# 第1题
s = ''.join([str(x) for x in geneinfo])
x = int(s, 2)
xs = '1' * 20
xs = int(xs, 2)
x = x / xs * 10
y = x + 10 * np.sin(5 * x) + 7 * np.cos(4 * x)
return y
def selectBest(self, pop):
s_inds = sorted(pop, key=itemgetter("fitness"), reverse=True) # 从大到小,返回整个种群
return s_inds[0]
def selection(self, individuals, k):
s_inds = sorted(individuals, key=itemgetter("fitness"),
reverse=True) # 依据‘fitness’进行排序
sum_fits = sum(ind['fitness'] for ind in individuals) # 整个种群适应度求和
chosen = []
for i in range(k):
u = random.random() * sum_fits # 随机生成一个范围为[0,sum_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 crossoperate(self, offspring):
dim = len(offspring[0]['Gene'].data)
geninfo1 = offspring[0]['Gene'].data # 从选定的pop中选择的第一个后代的基因数据
geninfo2 = offspring[1]['Gene'].data # 从选定的pop中选择的第二个后代的基因数据
if dim == 1:
pos1 = 1
pos2 = 1
else:
pos1 = random.randrange(1, dim) # 在0到dim-1范围内选择一个位置
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):
temp2.append(geninfo2[i])
temp1.append(geninfo1[i])
else:
temp2.append(geninfo1[i])
temp1.append(geninfo2[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("算法开始")
# Begin the evolution
for g in range(self.parameter[2]):
print("############### 子代 {} ###############".format(g))
# 根据转换后的适应度应用选择
selectpop = self.selection(self.pop, popsize)
nextoff = []
while len(nextoff) != popsize:
# 对后代应用交叉和突变
# 选择两个个体
offspring = [selectpop.pop() for _ in range(2)]
if random.random() < self.parameter[0]: # 以交叉概率交叉两个个体
crossoff1, crossoff2 = self.crossoperate(offspring)
# if random.random() < self.parameter[1]: # 以变异概率突变个体
# 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_crossoff1 = self.evaluate(crossoff1.data) # 评估个体适应度
# fit_crossoff2 = self.evaluate(crossoff2.data)
# nextoff.append({'Gene': crossoff1, 'fitness': fit_crossoff1})
# nextoff.append({'Gene': crossoff2, 'fitness': fit_crossoff2})
# 我的想法 实验证明:当种群G较小时性能明显更好
if random.random() < self.parameter[1]:
muteoff1 = self.mutation(crossoff1, self.bound)
if random.random() < self.parameter[1]:
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(muteoff1.data) # 评估个体适应度
fit_crossoff2 = self.evaluate(crossoff2.data)
nextoff.append({'Gene': muteoff1, 'fitness': fit_muteoff1})
nextoff.append({'Gene': crossoff2, 'fitness': fit_crossoff2})
else:
fit_crossoff1 = self.evaluate(crossoff1.data) # 评估个体适应度
fit_crossoff2 = self.evaluate(crossoff2.data)
nextoff.append({'Gene': crossoff1, 'fitness': fit_crossoff1})
nextoff.append({'Gene': crossoff2, 'fitness': fit_crossoff2})
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
print("找到最佳个体为:基因{}, 适应度{:.4f}".format(self.bestindividual['Gene'].data,
self.bestindividual['fitness']))
# # 第一题
# print("最佳个体对应x为:{:.4f}".format(
# int(''.join([str(x) for x in self.bestindividual['Gene'].data]), 2)
# / int('1' * 20, 2) * 10))
print(" 当前种群的最大适应度: {:.4f}".format(max(fits)))
print("------ 进化(成功)结束 ------")
ax.append(g) # 添加 g 到 x 轴的数据中
ay.append(self.bestindividual['fitness']) # 添加相应的适应度值到 y 轴的数据中
plt.clf() # 清除之前画的图
plt.xlabel('代数')
plt.ylabel('适应度')
plt.plot(ax, ay) # 画出当前 ax 列表和 ay 列表中的值的图形
plt.pause(0.01) # 暂停0.01秒
def new_evaluate(self, geneinfo):
# 第二题
y = 0
for x in geneinfo:
y += pow(x, 2)
if y == 0:
return 2 # 当只有一个基因为1时,倒数为1,所以全为0时让适应度为2就好
return 1 / y # 已知最小值为0,所以用倒数表示求最小值
if __name__ == "__main__":
# # 第一题
# Pc, Pm, G, NP, L = 0.8, 0.1, 100, 50, 20 # 种群规模(NP)必须是偶数
# up = [1] * L # 变量的上限范围
# low = [0] * L # 变量的下限范围
# draw()
# parameter = [Pc, Pm, G, NP, low, up]
# run = GA(parameter)
# ax = [] # 定义一个 x 轴的空列表用来接收动态的数据
# ay = [] # 定义一个 y 轴的空列表用来接收动态的数据
# plt.ion() # 开启一个画图的窗口
# run.GA_main()
# plt.ioff() # 关闭画图的窗口
# 第二题
Pc, Pm, G, NP, L = 0.8, 0.1, 1000, 100, 10
up = [20] * L # 变量的上限范围
low = [-20] * L # 变量的下限范围
parameter = [Pc, Pm, G, NP, low, up]
GA.evaluate = new_evaluate
run = GA(parameter)
ax = [] # 定义一个 x 轴的空列表用来接收动态的数据
ay = [] # 定义一个 y 轴的空列表用来接收动态的数据
plt.ion() # 开启一个画图的窗口
run.GA_main()
plt.ioff() # 关闭画图的窗口
代码借鉴自博客: Python手把手构建遗传算法(GA)实现最优化搜索,通用性很好。
第一题适应度曲线 | 第一题适应度曲线 |
- 包子阳. 智能优化算法及其MATLAB实例. 2版 ed. 北京: 电子工业出版社, 2018. Print. ↩︎