遗传算法Benchmark测评
前言
本文用来记录GA求解Benchmark代码思路。
一、问题介绍
求解Benchmark用例为:
F
(
x
)
=
∑
1
n
x
i
2
F(x)=\sum_{1}^{n}x_{i}^{2}\space
F(x)=∑1nxi2
x
∈
[
−
100
,
100
]
x\in[-100,100]
x∈[−100,100]
图形可视化为如下,容易看出最优点为(0,0,0,0,…),最优值为0。
为了可视化方便,仅计算二维情况。
二、Python实现GA求解
2.1 定义求解函数
def func1(x1,x2):
return x1**2+x2**2
2.2 GA类初始化部分
def __init__(self, pop_size, epoch, cross_prob, mutate_prob,func,print_batch=10):
self.pop_size = pop_size # 种群数量
self.epoch = epoch # 迭代次数
self.cross_prob = cross_prob # 交叉概率
self.mutate_prob = mutate_prob # 变异概率
self.width = 48 # 一个基因的长度,长度越长精度越高
self.best = None # 索存最优结果
self.genes = np.array(
["".join([random.choice(['0', '1']) for _ in range(self.width)]) for _ in range(self.pop_size)]
) # 随机生成初始种群
2.3 交叉操作
def inter_cross(self):
# 交叉操作
ready_index = list(range(self.pop_size)) # ready_index索存所有基因的索引号,挑选任意两个进行交叉操作。
while len(ready_index) >= 2: # 必须有2个以上的基因存在才可以交叉。
d1 = random.choice(ready_index) # 从0-100挑一个父基因
ready_index.remove(d1) # 挑选以后移除列表
d2 = random.choice(ready_index) # 从0-100挑一个父基因
ready_index.remove(d2) # 挑选以后移除列表
if np.random.uniform(0, 1) <= self.cross_prob: # 随机数判断是否需要交叉
loc = random.choice(range(1, self.width - 1)) # 随机挑选交叉的位置
d1_a, d1_b = self.genes[d1][0:loc], self.genes[d1][loc:] # 将d1基因分为2份
d2_a, d2_b = self.genes[d2][0:loc], self.genes[d2][loc:] # 将d2基因分为2份
self.genes[d1] = d1_a + d2_b # 进行交叉操作
self.genes[d2] = d2_a + d1_b # 进行交叉操作
2.4 变异操作
def mutate(self):
# 变异操作
ready_index = list(range(self.pop_size)) # ready_index索存所有基因的索引号,挑选任意两个进行变异操作。
for i in ready_index:
if np.random.uniform(0, 1) <= self.mutate_prob: # 随机数判断是否需要变异
loc = random.choice(range(0, self.width)) # 随机挑选需要变异的位置
t0 = list(self.genes[i]) # t0为列表,存储第i个基因的字符串
t0[loc] = str(1 - int(self.genes[i][loc])) # 取反操作 1-0=1,1-1=0
self.genes[i] = "".join(t0)
2.5 适应度计算
def get_adjust(self):
# 计算适应度
x1,x2 = self.get_decode() # 首先解码,之后返回对应值。
return func1(x1,x2)
def get_decode(self):
# x1,x2分别对应列表中各24位,通过对其进行二进制转十进制操作后除以(2**24-1)*200,得到对应[0,200]的数,在加上-100后,变为[-100,100].
x1 = (-100) + np.array([int(x[:24], 2) * 200 / (2 ** 24 - 1) for x in self.genes])
x2 = (-100) + np.array([int(x[24:], 2) * 200 / (2 ** 24 - 1) for x in self.genes])
return x1,x2
2.6 轮盘赌
由于轮盘赌的原理是概率越大越容易被选中,但是问题是求MIN问题,所以选择使用倒数的方法。
def cycle_select(self):
# 轮盘赌
adjusts = 1/self.get_adjust() # 得到每个基因的适应度值.
if self.best is None or 1/np.max(adjusts) < self.best[1]: # 筛选最优的基因并存到best中.
self.best = self.genes[np.argmax(adjusts)], 1/np.max(adjusts)
p = adjusts / np.sum(adjusts) # 计算所有基因的概率
cu_p = [] # 计算累计分布概率
for i in range(self.pop_size):
cu_p.append(np.sum(p[0:i]))
cu_p = np.array(cu_p) # 变为np.array对象
r0 = np.random.uniform(0, 1, self.pop_size) # 随机生成0-1的均匀分布与种群的累计分布概率比较
sel = [max(list(np.where(r > cu_p)[0]) + [0]) for r in r0]
# 确保最优个体的存活.
if 1/np.max(adjusts[sel]) < self.best[1]:
self.genes[sel[np.argmax(adjusts[sel])]] = self.best[0]
self.genes = self. Genes[sel]
2.7 主函数
def evolve(self):
# 主程序.
for i in range(self.epoch):
self.cycle_select() # 进行轮盘赌.
self.inter_cross() # 交叉操作.
self.mutate() # 变异操作.
三、代码整体
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
'''
@Project :最优化方法
@File :GA-1.py
@IDE :PyCharm
@Author :Pan YX
@Date :2023/4/29 15:24
@Descib :
'''
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import re
import math
import random
def func1(x1,x2):
return x1**2+x2**2
def show(x_s,x_e,func,end1=None,end2=None):
fig = plt.figure(figsize=(10, 7))
ax = Axes3D(fig, auto_add_to_figure=False)
fig.add_axes(ax)
ax.set_xlabel('$x1$', fontsize=16)
ax.set_ylabel('$x2$', fontsize=16)
ax.set_zlabel('$f(x)$', fontsize=16)
x1 = np.linspace(x_s, x_e, 100)
x2 = np.linspace(x_s, x_e, 100)
# x-y 平面的网格
x1, x2 = np.meshgrid(x1, x2)
z = func(x1,x2)
ax.plot_surface(x1, x2, z, rstride=1, cstride=1, cmap=plt.get_cmap('cool'))
if end1 != None and end2 != None:
ax.scatter(end1,end2,func(end1,end2),s=500,c='r',marker='.')
plt.show()
class GeneSolve:
def __init__(self, pop_size, epoch, cross_prob, mutate_prob,func,print_batch=10):
self.pop_size = pop_size # 种群数量
self.epoch = epoch # 迭代次数
self.cross_prob = cross_prob # 交叉概率
self.mutate_prob = mutate_prob # 变异概率
self.width = 48 # 一个基因的长度,长度越长精度越高
self.best = None # 索存最优结果
self.genes = np.array(
["".join([random.choice(['0', '1']) for _ in range(self.width)]) for _ in range(self.pop_size)]
) # 随机生成初始种群
def inter_cross(self):
# 交叉操作
ready_index = list(range(self.pop_size)) # ready_index索存所有基因的索引号,挑选任意两个进行交叉操作。
while len(ready_index) >= 2: # 必须有2个以上的基因存在才可以交叉。
d1 = random.choice(ready_index) # 从0-100挑一个父基因
ready_index.remove(d1) # 挑选以后移除列表
d2 = random.choice(ready_index) # 从0-100挑一个父基因
ready_index.remove(d2) # 挑选以后移除列表
if np.random.uniform(0, 1) <= self.cross_prob: # 随机数判断是否需要交叉
loc = random.choice(range(1, self.width - 1)) # 随机挑选交叉的位置
d1_a, d1_b = self.genes[d1][0:loc], self.genes[d1][loc:] # 将d1基因分为2份
d2_a, d2_b = self.genes[d2][0:loc], self.genes[d2][loc:] # 将d2基因分为2份
self.genes[d1] = d1_a + d2_b # 进行交叉操作
self.genes[d2] = d2_a + d1_b # 进行交叉操作
def mutate(self):
# 变异操作
ready_index = list(range(self.pop_size))
for i in ready_index:
if np.random.uniform(0, 1) <= self.mutate_prob:
loc = random.choice(range(0, self.width))
t0 = list(self.genes[i])
t0[loc] = str(1 - int(self.genes[i][loc]))
self.genes[i] = "".join(t0)
def get_adjust(self):
# 计算适应度
x1,x2 = self.get_decode() # 首先解码,之后返回对应值。
return func1(x1,x2)
def get_decode(self):
# x1,x2分别对应列表中各24位,通过对其进行二进制转十进制操作后除以(2**24-1)*200,得到对应[0,200]的数,在加上-100后,变为[-100,100].
x1 = (-100) + np.array([int(x[:24], 2) * 200 / (2 ** 24 - 1) for x in self.genes])
x2 = (-100) + np.array([int(x[24:], 2) * 200 / (2 ** 24 - 1) for x in self.genes])
return x1,x2
def cycle_select(self):
# 轮盘赌
adjusts = 1/self.get_adjust() # 得到每个基因的适应度值.
if self.best is None or 1/np.max(adjusts) < self.best[1]: # 筛选最优的基因并存到best中.
self.best = self.genes[np.argmax(adjusts)], 1/np.max(adjusts)
p = adjusts / np.sum(adjusts) # 计算所有基因的概率
cu_p = [] # 计算累计分布概率
for i in range(self.pop_size):
cu_p.append(np.sum(p[0:i]))
cu_p = np.array(cu_p) # 变为np.array对象
r0 = np.random.uniform(0, 1, self.pop_size) # 随机生成0-1的均匀分布与种群的累计分布概率比较
sel = [max(list(np.where(r > cu_p)[0]) + [0]) for r in r0]
# 确保最优个体的存活.
if 1/np.max(adjusts[sel]) < self.best[1]:
self.genes[sel[np.argmax(adjusts[sel])]] = self.best[0]
self.genes = self.genes[sel]
def evolve(self):
# 主程序.
for i in range(self.epoch):
self.cycle_select() # 进行轮盘赌.
self.inter_cross() # 交叉操作.
self.mutate() # 变异操作.
# 求解Benchmark1 -GA
show(-100,100,func=func1)
gs = GeneSolve(100,500,0.85,0.1,func=func1)
gs.evolve()
x1 = -100+ int(gs.best[0][:24], 2) * 200 / (2 ** 24 - 1)
x2 = -100+ int(gs.best[0][24:], 2) * 200 / (2 ** 24 - 1)
print(gs.best[0])
print('x1={},x2={},最优值为{}.'.format(x1,x2,gs.best[1]))
# show(-100,100,func=func1,end1=x1,end2=x2)
# 输出结果:
#011111111111111111111111100000000000000000000000
#x1=-5.96046483281043e-06,x2=5.96046483281043e-06,最优值为7.105428204633975e-11.