遗传算法-勾心斗角50载,绝世天才也得抓去做研究

遗传算法

本博客参考大佬:大佬原文(点击跳转)

兄弟,别走,最最最细讲解全在注释中,仔细看下去,让我们看看,绝代天骄的养成之路

参考阅读路线:看完常量定义后,直接从main函数开始一步一步跟着看下去

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

# 一组DNA的碱基对数
DNA_SIZE = 24
# 种群大小
POP_SIZE = 200
# 设置交叉率
CROSSOVER_RATE = 0.8
# 设置变异率,变异可以跳出局部最优
MUTATION_RATE = 0.005
# 迭代次数,也就是有多少代人
N_GENERATIONS = 50

# 规定x和y的取值范围
X_BOUND = [-3, 3]
Y_BOUND = [-3, 3]


# 目标函数,计算基因为x,y的孩子的环境适应度
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)


# 3d图像打印效果显示
def plot_3d(ax):
    # 将x和y的取值范围等分为100份
    # linspace基本的三个参数,start,end,num,因为X_BOUND是一个一维数组表示的是x的取值范围,这里使用*就可以解析成start=-3,end=3,达到动态的效果
    X = np.linspace(*X_BOUND, 100)
    Y = np.linspace(*Y_BOUND, 100)
    # 将x和y中的点进行组合,X中存放所有点的x坐标,Y中存放所有点的y坐标
    X, Y = np.meshgrid(X, Y)
    # Z就是一个100*100的数组了,
    Z = F(X, Y)
    # 绘制3维坐标图,rstride表示行,cstride表示列,cmap设置颜色映射
    ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm)
    # 设置z轴范围
    ax.set_zlim(-10, 10)
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')
    # 画图之前进行预测,表示几秒后展示
    plt.pause(1)
    plt.show()


def get_fitness(pop):
    # 解码之后的子代基因,
    x, y = translateDNA(pop)
    # 通过目标函数去判断这个子代是天才还是蠢材,也就是性状,官方一点来说就是适应度
    pred = F(x, y)
    # 减去最小的适应度是为了防止适应度出现负数,
    # 通过这一步fitness的范围为[0, np.max(pred)-np.min(pred)],
    # 最后在加上一个很小的数防止出现为0的适应度
    # 返回值是一个一维数组,表示每一个个体在种群中的适应度
    return (pred - np.min(pred)) + 1e-3

# 解码DNA,pop表示种群矩阵,一行表示一个二进制编码表示的DNA,矩阵的行数为种群数目
def translateDNA(pop):
    # 奇数列表示X,第一维度全要,然后第二维度从1开始表示奇数,之后每2个数取一个,确保奇数
    x_pop = pop[:, 1::2]
    # 偶数列表示y
    y_pop = pop[:, ::2]

    # 这个地方就是解码的关键操作
    # x_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1)可以将二进制转换成十进制
    x = x_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (X_BOUND[1] - X_BOUND[0]) + X_BOUND[0]
    y = y_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (Y_BOUND[1] - Y_BOUND[0]) + Y_BOUND[0]
    # 接收到两条碱基链,也就是一条DNA,双螺旋结构
    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)]
            # 随机产生交叉的点
            cross_points = np.random.randint(low=0, high=DNA_SIZE * 2)
            # 孩子将交叉点之后母亲的碱基序列放入自己的碱基序列中
            child[cross_points:] = mother[cross_points:]
        # 每个后代有一定的机率发生变异
        mutation(child)
        # 得到一个新的孩子,放入产房,等待全部孩子都出生之后,送出产房
        new_pop.append(child)

    return new_pop


# 基因突变,由于突变概率极小,因此我们的基因突变率设置的非常的小
def mutation(child, MUTATION_RATE=0.003):
    # 小于设置的突变率则发生基因突变
    if np.random.rand() < 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
    # 随机选择
    # 参数说明 在np.arange(POP_SIZE)这个一维数组中抽取,抽取数量为POP_SIZE,replace表示可以抽取同样的数字
    # p表示概率的选择方式,就是种群中每个个体被抽到的概率
    probability = fitness / fitness.sum()
    # 此处表示根据适应度获取优秀孩子的下标,将优秀孩子的下标放在培育仓idx中
    idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True, p = probability)
    # 适者生存,劣质的孩子将会被优质的孩子取代
    return pop[idx]


# 长达 N_GENERATIONS 代人的马拉松式角逐接近尾声了,决定胜负的时候了,到底哪个孩子才是千年难遇的天才
def print_info(pop):
    # 决赛!!获取到千年以来,最完美的 POP_SIZE 个基因
    fitness = get_fitness(pop)
    # 巅峰王者!运气与实力并存的选手,argmax可以获取到参数数组中的最大值
    max_fitness_index = np.argmax(fitness)
    print("max_fitness:", fitness[max_fitness_index])
    # 对巅峰王者进行基因实验,看看这个千年奇才的基因到底有啥好的,长啥样
    print("最优的基因型:", pop[max_fitness_index])
    
    x, y = translateDNA(pop)
    print("(x, y):", (x[max_fitness_index], y[max_fitness_index]))


if __name__ == "__main__":
    fig = plt.figure()
    ax = Axes3D(fig)
    plt.ion()  # 将画图模式改为交互模式,程序遇到plt.show不会暂停,而是继续执行
    plot_3d(ax)

    # 调用随机数生成最大数为2,大小为200行48列的种群(最大数是2是因为碱基序列是0和1的组合,dna的单链有24个碱基,dna是双联双螺旋结构,所以是DNA_SIZE*2)
    pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE * 2))  # matrix (POP_SIZE, DNA_SIZE)
    for _ in range(N_GENERATIONS):  # 迭代N代
        # DNA解码,模拟翻译过程
        x, y = translateDNA(pop)
        # 内置函数 locals() 采用字典形式存储当前作用域中的全部变量
        if 'sca' in locals():
            sca.remove()
        # 绘制一个散点
        sca = ax.scatter(x, y, F(x, y), c='black', marker='o');
        plt.show();
        # 每隔0.1秒执行一次便于更好的观察变化
        plt.pause(0.1)
        # 当前种群开始通过交叉和变异两种模式去繁殖后代,上一代的繁殖成果,作为下一代的父代
        pop = np.array(crossover_and_mutation(pop, CROSSOVER_RATE))
        # 获取种群中每个孩子的适应度
        fitness = get_fitness(pop)
        pop = select(pop, fitness)  # 选择生成新的种群

    print_info(pop)
    # 程序执行完后,3维图不销毁
    plt.ioff()
    plot_3d(ax)


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值