python求函数最⼤值_遗传算法与Python图解
import matplotlib.pyplot as plt
import numpy as np
import random
import pandas as pd
遗传算法求函数最值
遗传算法的特点有⽆须可导,可优化离散异构数据。
参考:
遗传算法python实现
科学前沿:Genetic Algorithm - 遗传算法究竟怎么回事
问题定义
如下图所⽰,
在区间
[-1, 2]上有很多极⼤值和极⼩值,如果要求其最⼤值或最⼩值,很多单点优化的⽅法(梯度下降等)就不适合,这种情况下就可以⽤遗传算法(Genetic Algorithm)。
def fun(x):
return x * np.sin(10*np.pi*x) + 2
Xs = np.linspace(-1, 2, 100)
plt.plot(Xs, fun(Xs))
初始化原始种
遗传算法的⼀个特点是同时优化⼀批解(种),例如下⾯在[-1, 2]范围内随机⽣成
个点:
np.random.seed(0)
# 初始化原始种
def ori_popular(num, _min=-1, _max=2):
return np.random.uniform(_min, _max, num) # 范围[-1, 2)
#__TEST__
population = ori_popular(10)
for pop, fit in zip(population, fun(population)):
print("x=%5.2f, fit=%.2f"%(pop, fit))
plt.plot(Xs, fun(Xs))
plt.plot(population, fun(population), '*')
plt.show()
>>>
x= 0.65, fit=2.64
x= 1.15, fit=0.87
x= 0.81, fit=2.21
x= 0.63, fit=2.56
x= 0.27, fit=2.21
x= 0.94, fit=1.13
x= 0.31, fit=1.88
x= 1.68, fit=3.17
x= 1.89, fit=2.53
x= 0.15, fit=1.85
上图显⽰我们随机选的
个点离最⼤值(3.8左右)差距还挺远,下⾯⽤GA算法看能否求到最优解。
编码
编码,也就是由表现型到基因型,性征到染⾊体。
⼆进制编码的缺点:对于⼀些连续函数的优化问题,由于其随机性使得其局部搜索能⼒较差,如对于⼀些⾼精度的问题,当解迫近于最优解后,由于其变异后表现型变化很⼤,不连续,所以会远离最优解,达不到稳定。⽽格雷码能有效地防⽌这类现象。
TODO:
[ ] ⽤2**18扩展似乎会损失精度,准备⽤10000代替
下⾯分别将1, 10, 0转成⼆进制编码,注意浮点数0.1⽆法转换:
print("1:", bin(1))
print("10:", bin(10))
print("0:", bin(0))
try:
print("0.1:", bin(0.1))
except Exception as E:
print("Exception: {}".format(type(E).__name__), E)
>>>
1: 0b1
10: 0b1010
0: 0b0
Exception: TypeError 'float' object cannot be interpreted as an integer
为了能编码浮点数,需要扩⼤倍数转成整数:
X = [1, 0.1, 0.002]
print("2**18 =", 2**18, 'n')
for x in X:
tx = int(x * 2**18)
print("%.4f => %6d => %s"%(x, tx, bin(tx)))
2**18 = 262144
>>>
1.0000 => 262144 => 0b1000000000000000000
0.1000 =>  26214 => 0b110011001100110
0.0020 =>    524 => 0b1000001100
for x in X:
tx = int(x * 2**18)
ex = bin(tx)
dx = int(ex, 2) / 2**18
print("%25s => %6d => %.4f"%(ex, tx, dx))
>>>
0b1000000000000000000 => 262144 => 1.0000
0b110011001100110 =>  26214 => 0.1000
0b1000001100 =>    524 => 0.0020
需要注意的是,上⾯⽤%.4f进⾏了四舍五⼊,实际上⽤2**18=262144来放⼤编码会有精度的损失,例如:
x = 0.1
tx = int(x * 2**18)
ex = bin(tx)
int('0b110011001100110', 2) / (2**18)
>>>
0.09999847412109375
def encode(popular, _min=-1, _max=2, scale=2**18, bin_len=18):  # popular应该是float类型的列表
"""
bin_len: 染⾊体序列长度
"""
norm_data = (popular-_min) / (_max-_min) * scale
bin_data = np.array([np.binary_repr(x, width=bin_len) for x in norm_data.astype(int)]) # 转成⼆进制编码
return bin_data
chroms = encode(population)
for pop, chrom, fit in zip(population, chroms, fun(population)):
print("x=%.2f, chrom=%s, fit=%.2f"%(pop, chrom, fit))
>>>
x=0.65, chrom=100011000111111100, fit=2.64
x=1.15, chrom=101101110001011010, fit=0.87
x=0.81, chrom=100110100100111010, fit=2.21
x=0.63, chrom=100010110111110101, fit=2.56
x=0.27, chrom=011011000111010010, fit=2.21
x=0.94, chrom=101001010101100101, fit=1.13
x=0.31, chrom=011100000000010110, fit=1.88
x=1.68, chrom=111001000100101100, fit=3.17
x=1.89, chrom=111101101011001010, fit=2.53
x=0.15, chrom=011000100010100100, fit=1.85
解码和适应度函数
通过基因(染⾊体)得到个体的适应度值,也就是评判当前种每个个体(解)表现好坏,要对编码解码后才能代⼊函数。注意,因为函数
的结果就是⼤⼩,所以直接⽤来当适应度函数。
def decode(popular_gene, _min=-1, _max=2, scale=2**18):
return np.array([(np.int(x, base=2)/scale*3)+_min for x in popular_gene])
fitness = fun(decode(chroms))
for pop, chrom, dechrom, fit in zip(population, chroms, decode(chroms), fitness):
print("x=%5.2f, chrom=%s, dechrom=%.2f, fit=%.2f"%(pop, chrom, dechrom, fit))
>>>
x= 0.65, chrom=100011000111111100, dechrom=0.65, fit=2.64
x= 1.15, chrom=101101110001011010, dechrom=1.15, fit=0.87
x= 0.81, chrom=100110100100111010, dechrom=0.81, fit=2.21
x= 0.63, chrom=100010110111110101, dechrom=0.63, fit=2.56
x= 0.27, chrom=011011000111010010, dechrom=0.27, fit=2.21
x= 0.94, chrom=101001010101100101, dechrom=0.94, fit=1.13
x= 0.31, chrom=011100000000010110, dechrom=0.31, fit=1.88
x= 1.68, chrom=111001000100101100, dechrom=1.68, fit=3.17
x= 1.89, chrom=111101101011001010, dechrom=1.89, fit=2.53
x= 0.15, chrom=011000100010100100, dechrom=0.15, fit=1.85
这⾥将最⼩的适应度调整到0.000001,主要为了防⽌负数。
fitness = fitness - fitness.min() + 0.000001 # 保证所有的都为正
选择与交叉
选择就是淘汰不好的染⾊体(解),选择⽅式有很多,这⾥⽤轮牌赌。
交叉就是让染⾊体相互交换基因,⽅式有很多,这⾥在染⾊体中间进⾏交叉,交叉概率默认为0.66。
def SelectandCrossover(chroms, fitness, prob=0.6):
probs = fitness/np.sum(fitness)  # 各个个体被选择的概率
probs_cum = np.cumsum(probs)  # 概率累加分布
each_rand = np.random.uniform(size=len(fitness))
#und(fitness, 2), np.round(probs, 2), np.round(probs_cum, 2), sep='n')
linspace函数python#print([np.where(probs_cum > rand)[0][0] for rand in each_rand])
# 根据随机概率选择出新的基因编码
newX = np.array([chroms[np.where(probs_cum > rand)[0][0]] for rand in each_rand])
# 繁殖,随机配对(概率为0.6)
pairs = np.random.permutation(int(len(newX)*prob//2*2)).reshape(-1, 2)
center = len(newX[0])//2
for i, j in pairs:
# 在中间位置交叉
x, y = newX[i], newX[j]
newX[i] = x[:center] + y[center:]
newX[j] = y[:center] + x[center:]
#print(x, y, newX[i], '', sep='n')
return newX
chroms = SelectandCrossover(chroms, fitness)
dechroms = decode(chroms)
fitness = fun(dechroms)
for gene, dec, fit in zip(chroms, dechroms, fitness):
print("chrom=%s, dec=%5.2f, fit=%.2f"%(gene, dec, fit))
fig, (axs1, axs2) = plt.subplots(1, 2, figsize=(14, 5))
axs1.plot(Xs, fun(Xs))
axs1.plot(population, fun(population), 'o')
axs2.plot(Xs, fun(Xs))
axs2.plot(dechroms, fitness, '*')
plt.show()
>>>
chrom=111101101000010110, dec= 1.89, fit=2.64
chrom=011100000011001010, dec= 0.31, fit=1.86
chrom=011100000010100100, dec= 0.31, fit=1.86
chrom=011000100000010110, dec= 0.15, fit=1.85
chrom=100011000111111100, dec= 0.65, fit=2.64
chrom=100011000111111100, dec= 0.65, fit=2.64
chrom=100011000111111100, dec= 0.65, fit=2.64
chrom=111101101011001010, dec= 1.89, fit=2.53
chrom=111001000100101100, dec= 1.68, fit=3.17
chrom=111101101011001010, dec= 1.89, fit=2.53

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。