1 基本原理
对每个个体都进行上述的进化,然后择优录取。
2 步骤
2.1 编码
二进制编码:用一个二进制串 表示 这个十进制数值 。
- 给定数值解的区间范围: [ 1 , 10 ] [1,10] [1,10]。
- 给定精度: e − 5 e^{-5} e−5 ,两个数值解的间隔。
- 进行编码:为每个数值解 分配 一个独一无二的二进制串。
例如
区间范围为 [ 1 , 10 ] [1, 10] [1,10],长度为 2 的串提供的精度为多少?对应精度为 10 − 1 2 2 − 1 = 3 \frac{10-1}{2^2 - 1} = 3 22−110−1=3
于是数值区间长度为 L ,精度为 E E E 的条件下,二进制串的长度 n ,三者关系为 L 2 n − 1 ≤ E \frac{L}{2^n - 1} ≤E 2n−1L≤E
要想达到 e − 5 e^{-5} e−5的精度,对于 区间长度为 10 ,串的长度 n 应该满足 10 2 n − 1 ≤ 0.00001 \frac{10}{2^n - 1} ≤0.00001 2n−110≤0.00001 → n ≥ 20 n≥20 n≥20
n = 20 n = 20 n=20 时, 串提供的精度约为 0.00000954
例如
以 [ 1 , 10 ] [1, 10] [1,10]为例,其十进制转换结果为 0 ∗ 2 0 + 1 ∗ 2 1 = 2 0*2^0 + 1 * 2^1 = 2 0∗20+1∗21=2
对应表示的数值为: 7 = 1 + 2 ∗ 3 7 = 1 + 2 * 3 7=1+2∗3
以 [ 1 , 10 ] [1, 10] [1,10]为例,其十进制转换结果为 1 ∗ 2 0 + 1 ∗ 2 1 = 3 1*2^0 + 1 * 2^1 = 3 1∗20+1∗21=3
对应表示的数值为: 10 = 1 + 3 ∗ 3 10 = 1 + 3 * 3 10=1+3∗3
一般的,区间范围为 [ a , b ] [a,b] [a,b],区间长度为 L ,即 L = b − a L = b - a L=b−a串长为 n ,当前串对应十进制为 T ,则该串对应实值解为: X = a + T ∗ b − a 2 n − 1 X = a + T* \frac{b - a}{2^n - 1} X=a+T∗2n−1b−a
X
2.2 复制
个体进行复制,以概率𝑝𝑐进行基因的交叉,注意 复制交叉方式多种多样 。
复制
- 将个体适应度大小映射为概率进行复制:适应度高的个体有更大概率复制,且复制的份数越多 轮盘赌法 。
- 对适应度高的前 N 4 \frac{N}{4} 4N 的个体进行复制,然后用这些个体把后 N 4 \frac{N}{4} 4N个体替换掉 精英产生精英 。
- 一定是将当前个体的复制体将下一个个体替换掉吗?随机可以吗? YES!
- 一定只能把“坏解”替换掉吗?把随机某个适应度高的解替换掉呢? YES!
2.3 交叉
交叉
- 按顺序,两两个体之间按概率交叉。如 1 和 2,2 和 3 等。或者 1 和 2,2 和 4 等,或者 1 和 4 这样。
- 必须两两交叉? 3 个可以吗? YES! 。 5 个? YES!
- 对适应度高的前 N 2 \frac{N}{2} 2N个体、甚至 N 4 \frac{N}{4} 4N 的个体之间相互交叉? YES!
- 一定是按顺序交叉吗?对每个个体随机从前 N 2 \frac{N}{2} 2N 中选一个个体交叉? YES!
- 一定是只有一段交叉?多段呢? YES!
2.4 变异
变异
- 每个个体都进行变异。
- 只对适应度低的后 N 4 \frac{N}{4} 4N的,或者后 N 2 \frac{N}{2} 2N个个体变异? YES!
- 必须都变异吗?按适应度大小映射为概率变异? YES!
- 一定是只有一个位点变异?多个位点呢? YES!
轮盘赌基本思想:适应度越高的解,按道理越应该高概率的进行复制,且复制的份数应该越多。
对于个体 x i x_i xi,计算对应适应度 𝑓 ( 𝑥 𝑖 ) 𝑓(𝑥_𝑖) f(xi) ==> 𝑝 𝑖 = 𝑓 ( 𝑥 𝑖 ) ∑ 𝑓 ( 𝑥 𝑖 ) 𝑝_𝑖 = \frac{𝑓(𝑥_𝑖)}{\sum{𝑓(𝑥_𝑖)}} pi=∑f(xi)f(xi)
3 代码
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation as animation # 动画
import math
from numpy import random
# 解所在的可能区间
start = 0 # 起始
end = 8 # 结尾
# 迭代次数
iteration = 200
# 种群大小
size = 50
# 交叉概率
pc = 0.85
# 变异概率
pm = 0.05
# 表达式
def fitness(x):
y = x * np.sin(4 * x) + 3 * x * np.cos(x) + 15
return y
# 二进制编码
def getStrLen(precision):
# 根据精度求串长
l = math.ceil(math.log2((end - start) / precision + 1))
return l
# 二进制解码: 二进制转十进制
def binaryDecode(matrix):
len = getStrLen(1e-5)
realValue = np.zeros(size)
for i in range(size): # 按照行来遍历
for j in range(len): # 对第i行进行遍历
realValue[i] += matrix[i][j] * math.pow(2, j)
for i in range(size):
realValue[i] = start + realValue[i] * (end - start) / (math.pow(2, len) - 1)
return realValue
# 复制
def copyX(parentX, fitness): # martix 二进制基因片段;fitness种群的适应度
l = getStrLen(1e-5)
newX = np.random.randint(0, 2, (size, l)) #初始化一个下一代矩阵空间
# print("初始化下一代矩阵")
# print(newX)
p = fitness / np.sum(fitness) #概率
cs = np.cumsum(p) #累积概率
# print("累积概率")
# print(cs)
cps= np.sort(np.random.rand(size)) # 轮盘赌,每个个体复制的概率
# print("每个个体复制的概率")
# print(cps)
for i in range(size):
if cps[i] < cs[i]:
newX[i,:] = parentX[i,:]
# print("复制后的种群")
# print(newX)
return newX
# 交叉方式 第i行的[crossStart, crossEnd]片段 与 第 i + 1行的[crossStart, crossEnd]片段交换
def crossX(matrix):
i = 0
l = getStrLen(1e-5)
while i < size:
if np.random.rand(1) < pc:
positions = np.random.choice(l, 2, False)
crossStart = positions.min()
crossEnd = positions.max()
np.random.choice(l, 2, False) # 在[0, len)之间选择两个不重复的数字
j = (i + 1) % size
pre = matrix[i: j, crossStart: crossEnd + 1]
if j > i:
matrix[i: j, crossStart: crossEnd + 1] = matrix[j: j + 1, crossStart: crossEnd + 1]
matrix[j: j + 1, crossStart: crossEnd + 1] = pre
i = i + 2
else:
i = i + 1
if i >= size:
break
# print("交叉后的种群")
# print(matrix)
return matrix
# 变异
def mutationX(matrix):
l = getStrLen(1e-5)
for i in range(size):
if np.random.rand(1) < pm:
position = np.random.choice(l, 1, False)
matrix[i][position] = np.mod((matrix[i][position] + 1), 2)
# print("变异后的种群")
# print(matrix)
return matrix
if __name__ == '__main__':
l = getStrLen(1e-5)
# 生成初代种群,size--种群大小;len--串长
matrix = np.random.randint(0, 2, (size, l))
print("初代矩阵")
print(matrix)
# 绘制函数图像
t = np.arange(start, end, 0.001)
for i in range(iteration):
dec = binaryDecode(matrix) # 上一代解的十进制
fit = fitness(dec) # 上一代适应值
# print("上代适应值")
# print(fit)
# print("******* 复制操作 ********")
newX = copyX(matrix, fit)
# print("******* 交叉操作 ********")
newX = crossX(newX)
# print("******* 变异操作 ********")
newX = mutationX(newX)
# 对下一代择优保留
newDec = binaryDecode(newX) #新一代十进制
newFit = fitness(newDec) # 新一代适应度
for j in range(size):
if newFit[j] > fit[j]:
matrix[j,:] = newX[j,:] # 将好的子代替换进父代
# 动态每一代最优值图像
dec = binaryDecode(matrix) # 择优后最终新一代解的十进制
fit = fitness(dec) # 择优后最终新一代适应值
plt.plot(t, fitness(t), color='black')
plt.scatter(dec, fit, s=5, c='red')
plt.draw()
plt.pause(0.2)
plt.clf()
dec = binaryDecode(matrix) # 最终一代解的十进制
fit = fitness(dec) # 最一代适应值
maxIndex = fit.argmax()
plt.plot(t,fitness(t),color = 'black')
plt.scatter(dec, fit, s=5, c='red')
plt.scatter(dec[maxIndex], fit.max(), s=5, c = 'blue')
plt.show()
# 取矩阵的第一行 matrix[0,:]
# 取矩阵的第一列 matrix[:,0]
# 取部分矩阵,行[i, j),列[m,n):matrix[i: j, m: n]
# 产生一个随机小数:np.random.rand(1)
# -------------------动态图-----------------------
# fig:窗口画布,用 fig, ax = plt.subplots() 生成
# func:要反复调用的函数(至少要传入一个参数),用该函数生成、更改图像
# frames:向要调用的函数传递的参数(默认依次传入0,1,2,3…)
# init_func:用于给定图像初始信息的函数