无约束的遗传算法(最简单的)
最开始真正理解遗传算法,是通过这个博主的讲解,安利给小白们看一看,遗传算法的Python实现(通俗易懂),我觉得博主写的让人特别容易理解,关键是代码也不报错,然后我就照着他的代码抄了一遍,认真地理解了一下每一个模块,:编码、解码、适应度函数写法、选择、交叉和变异的实现过程,下面也谈一谈我在整个过程中的认识,以及对代码的一种通俗解释: 1、编码:这里主要运用的就是一种二进制的编码,将要求解的问题的解,从十进制的自然数以某一种方式用二进制表达出来,每一个0或1作为是一个基因位,一个数的众多基因构成一条染色体,也就是一个个体,进而众多染色体构成一个种群,所说的种群规模其实就是这些染色体的个数,最后用这个种群去参与到遗传算法的各个过程中,并且二进制的编码对于交叉和变异的操作会简单一点。 2、解码:因为适应度函数是用十进制的自然数写的,所以在每一次迭代中,要把二进制的染色体,再变成十进制的数,才能参与到适应度的计算中,就照着解码公式写出来就好了。
def decode(pop):
return pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) *(X_max-X_min)/ float(2**DNA_SIZE-1) +X_min
3、适应度函数:作为遗传变异的根据,这个函数通常跟我们目标函数有关系,因为后面在选择过程中,是保留适应度值更大的个体,所以适应度值我们都认为是越大越好,那么目标函数是求最大值的时候,那也就是说适应度值越大,越接近目标,所以令适应度函数=目标函数。 目标函数求最小值时,如果也想达到这样的效果,则令适应度函数=1/目标函数。 但是在这里又遇到了一个问题,因为适应度值必须是正的,因为要求占比,但目标函数值有可能是负的,所以就做了下面的这个转换,让求出来的适应度值是正的,并且与目标函数值的变化是一致的,如果以后遇到的话,也可以尝试着自己去想表达式来解决,只要明白这个目的。
"""求解的目标表达式为:
y = 10 * math.sin(5 * x) + 7 * math.cos(4 * x)
x=[0,5]
"""
def aim(x):
return 10*np.sin(5*x)+7*np.cos(4*x)
def fitnessget(pred):
return pred + 1e-3 - np.min(pred)
4、选择:最常用的就是轮盘赌方式,了解完他的定义之后发现他的代码实现也不难,比如有五个个体的种群,索引序号是[0,1,2,3,4],那就是计算适应度值,并且求一下在总的适应度里面的占比,得到[0.1,0.2,0.3,0.1,0.3],然后根据这每一个个体的比例来随机抽数,得到[2,4,2,1,4],就比如两个0.3的概率最大,那么抽取到个体2和4的概率就更大,最终根据这个序号去选择种群中对应的个体,进行保留到下一代,这就实现了适应环境的留下,不适应的淘汰。
def select(pop, fitness):
idx = np.random.choice(np.arange(pop_size), size=pop_size, replace=True,p=fitness/fitness.sum())
return pop[idx]
4、交叉:二进制的交叉就是针对每一个个体,随机找到一个个体,然后再随机找到某几个基因位,这两个个体的这些基因位进行交换,这里我看的时间比较久,实在不懂就打印出来看看是啥咯
def change(parent, pop):
x = np.random.rand()
print('x:{}'.format(x))
if x < PC:
i_ = np.random.randint(0, pop_size, size=1)
print('i_:{}'.format(i_))
cross_points = np.random.randint(0, 2, size=DNA_SIZE)
print('cross_points:{}'.format(cross_points))
cross_points = cross_points.astype(np.bool)
print('cross_points:{}'.format(cross_points))
print(np.where(cross_points==True))
print('cross_points:{}'.format(cross_points))
print('parent[cross_points]:{}'.format(parent[cross_points]))
parent[cross_points] = pop[i_, cross_points]
print('parent:{}'.format(parent))
return parent
5、变异:二进制的变异就很简单了,就是选择基因位,然后0变成1,1变成0,代码也比较容易实现。
def variation(child,pm):
for point in range(DNA_SIZE):
if np.random.rand() < pm:
child[point] = 1 if child[point] == 0 else 0
return child
6、遗传算法主体
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
pop_size = 5
PC=0.6
PM=0.01
X_max=5
X_min=0
DNA_SIZE=10
N_GENERATIONS=100
pop = np.random.randint(2, size=(pop_size, DNA_SIZE))
X = np.arange(0,N_GENERATIONS,1)
Y = [None]*N_GENERATIONS
for i in range(N_GENERATIONS):
X_value= decode(pop)
F_values = aim(X_value)
fitness = fitnessget(F_values)
if(i==0):
max=np.max(F_values)
max_DNA = pop[np.argmax(F_values), :]
if(max<np.max(F_values)):
max=np.max(F_values)
max_DNA=pop[np.argmax(F_values), :]
if (i % 10 == 0):
print("Most fitted value and X: \n",
np.max(F_values), decode(pop[np.argmax(F_values), :]))
pop = select(pop,fitness)
pop_copy = pop.copy()
for parent in pop:
child = change(parent,pop_copy)
child = variation(child,PM)
parent[:] = child
Y[i] = max
print("目标函数最大值为:",max)
print("其DNA值为:",max_DNA)
print("其X值为:",decode(max_DNA))
plt.plot(X,Y)
结果如下:
好了,大家把上面的代码和解释全部认真读一遍,小白也能懂遗传算法了。
|