遗传算法代码详细注释,适合初学者!

        遗传算法的基本原理和实现思路大家可以搜这篇《遗传算法详解 附python代码实现》
,本文则是对代码进行详细标注,方便大家理解每行代码,以便后续修改,祝大家一切顺利呀!


import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D

DNA_SIZE = 24
POP_SIZE = 200       #种群规模是指任意一代中的个体总数,这个是人为设定的,种群规模越大越可能找到全局解,但运行时间也相对较长
CROSSOVER_RATE = 0.9 #交叉操作中的概率是用于判定两个个体是否进行交叉操作,一般都会大于0.9
MUTATION_RATE = 0.005 #变异操作的概率是允许少数个体存在变异情况,以避免限入局部最优解,其值一般都在0.1以下
N_GENERATIONS = 50
X_BOUND = [-3, 3]
Y_BOUND = [-3, 3]


def F(x, y):
    return 3*(1-x)**2*np.exp(-(x**2)-(y+1)**2)- 10*(x/5 - x**3 - y**5)*np.exp(-x**2-y**2)- 1/3**np.exp(-(x+1)**2 - y**2)

def plot_3d(ax):

    X = np.linspace(*X_BOUND, 100) #生成有序列表,在[-3,3]之间由100个数据分割,*生成一行数据
    Y = np.linspace(*Y_BOUND, 100)
    X,Y = np.meshgrid(X, Y) #将两个一维数组都变为二维数组
    Z = F(X, Y)  #[[-0.99993005 -0.99990448 -0.99987068 ... -1.00001943 -1.00001107
    ax.plot_surface(X,Y,Z,rstride=1,cstride=1,cmap=cm.coolwarm) #绘制行步长,列步长为1,曲面块颜色映射为冷暖的3D 图形
    ax.set_zlim(-10,10)
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')

    plt.pause(3) #可视化显示结果,# 间隔的秒数:3s
    plt.show()


def get_fitness(pop):
    x,y = translateDNA(pop)
    pred = F(x, y)
    return (pred - np.min(pred)) + 1e-3 #减去最小的适应度是为了防止适应度出现负数,choice函数的p值不能为负,通过这一步fitness的范围为[0, np.max(pred)-np.min(pred)],最后在加上一个很小的数防止出现为0的适应度


def translateDNA(pop): #pop表示种群矩阵,一行表示一个二进制编码表示的DNA,矩阵的行数为种群数目,编码过程在代码层面不需要,因为种群初始化就是以二进制的形式,需要的是我们怎么理解这个二进制,也就是解码
    x_pop = pop[:,1::2]#奇数列表示X,从索引列1开始,加入了步长2
    y_pop = pop[:,::2] #偶数列表示y

    #pop:(POP_SIZE,DNA_SIZE)*(DNA_SIZE,1) --> (POP_SIZE,1),解码,压缩到[0,1]之间,并映射到 X_BOUND = [-3, 3], Y_BOUND = [-3, 3]
    x = x_pop.dot(2**np.arange(DNA_SIZE)[::-1])/float(2**DNA_SIZE-1)*(X_BOUND[1]-X_BOUND[0])+X_BOUND[0] #a[::-1] 表示将一维数组a反转, x_pop.dot(2**np.arange(DNA_SIZE)[::-1])等价于print(np.dot(x_pop,2**np.arange(DNA_SIZE*2)[::-1]))
    y = y_pop.dot(2**np.arange(DNA_SIZE)[::-1])/float(2**DNA_SIZE-1)*(Y_BOUND[1]-Y_BOUND[0])+Y_BOUND[0]
    return x,y

def crossover_and_mutation(pop, CROSSOVER_RATE = 0.8):     #经过适应函数筛选后的解再通过交叉算子来维持解的多样性
    new_pop = []
    for father in pop:		#遍历种群中的每一个个体,将该个体作为父亲,遗传算法采用循环交叉一般是因为个体是有某个序列组成,染色体不能有相同的情况,
        child = father		#孩子先得到父亲的全部基因(这里我把一串二进制串的那些0,1称为基因)
        if np.random.rand() < CROSSOVER_RATE:			#产生子代时不是必然发生交叉,而是以一定的概率发生交叉
            mother = pop[np.random.randint(POP_SIZE)]	#zai1种群中选择另一个个体,并将该个体作为母亲
            cross_points = np.random.randint(low=0, high=DNA_SIZE*2)	#随机产生交叉的点
            child[cross_points:] = mother[cross_points:]		#孩子得到位于交叉点后的母亲的基因,child[cross_points:]从cross_points+1开始往后取
        mutation(child)	#每个后代有一定的机率发生变异
        new_pop.append(child)

    return new_pop

def mutation(child, MUTATION_RATE=0.003):     #变异是为了产生新模式,摆脱局部最优解
    if np.random.rand() < MUTATION_RATE: 				#以MUTATION_RATE的概率进行变异
        mutate_point = np.random.randint(0, DNA_SIZE*2)	#随机产生一个实数,代表要变异基因的位置
        child[mutate_point] = child[mutate_point]^1 	#将变异点的二进制为反转, ^在算术运算中,表示异或

def select(pop, fitness):    # nature selection wrt pop's fitness,choice()是不能直接访问的,需要导入 random 模块,然后通过 random 静态对象调用该方法
    idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,p=(fitness)/(fitness.sum())) #choice返回一个列表,元组或字符串的随机项,p与a对应,arange() 函数的作用是创建一个数组,生成数组[  0   1   2   3   4...199],p为轮盘对赌法
    return pop[idx]

def print_info(pop):
    fitness = get_fitness(pop)
    max_fitness_index = np.argmax(fitness) #求自变量最大的函数
    print("max_fitness:", fitness[max_fitness_index])
    x,y = translateDNA(pop)
    print("最优的基因型:", pop[max_fitness_index])
    print("(x, y):", (x[max_fitness_index], y[max_fitness_index]))
    print("最大值:", F(x[max_fitness_index],y[max_fitness_index]))


if __name__ == "__main__":     #指定主方法函数。在脚本执行时开启main函数,但是在其他文件import调用时不会执行。
    fig = plt.figure() #新建一个名叫 Figure的画图窗口,plt.plot()是在画图窗口里具体绘制曲线

    ax = Axes3D(fig,auto_add_to_figure=False) #Axes3D是matplotlib中的绘制函数
    fig.add_axes(ax) #add_axes为新增子区域,该区域可以座落在figure内任意位置,且该区域可任意设置大小
    plt.ion()#将画图模式改为交互模式,程序遇到plt.show不会暂停,而是继续执行
    plot_3d(ax)

    pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE*2)) #randint(low, high=None, size=None, dtype=int)二进制串随机生成,返回一个随机整型数,2表示2进制
    for _ in range(N_GENERATIONS):#迭代N代,N无明确规定,只要收敛就行,看x,y变化大不大,变化不大说明收敛了,根据散点图,(x,y)最后都是集中在一个地方
        x,y = translateDNA(pop)
        if 'sca' in locals():   #locals()函数不需要参数,会以字典类型返回当前位置的全部局部变量
            sca.remove()        #remove() 函数用于移除列表中某个值的第一个匹配项,定义过的话删除图上的点
        sca = ax.scatter(x, y, F(x,y), c='black', marker='o');plt.show();plt.pause(0.1)
        pop = np.array(crossover_and_mutation(pop, CROSSOVER_RATE))
        #F_values = F(translateDNA(pop)[0], translateDNA(pop)[1])#x, y --> Z matrix
        fitness = get_fitness(pop)
        pop = select(pop, fitness) #选择生成新的种群

    print_info(pop)
    plt.ioff() #没有使用ioff()关闭的话,则图像会一闪而过,并不会常留。要想防止这种情况,需要在plt.show()之前加上ioff()命令
    plot_3d(ax)

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值