进化算法------代码示例

前言

遗传算法就是在一个解空间上,随机的给定一组解,这组解称为父亲种群,通过这组解的交叉,变异,构建出新的解,称为下一代种群,然后在目前已有的所有解中抽取表现好的解组成新的父亲种群,然后继续上面的过程,直到达到了迭代条件或者获取到了最优解。

进化算法流程框架

下面我们来解释下这个流程图里面的一些概念

  • 适应度

所谓的适应度,本质上可以理解为一个代价函数,或者一个规则,通过对初始种群中的个体计算适应度,能够得到对初始种群中的个体是否优劣的一个度量

  • 选择

选择操作是根据种群中的个体的适应度函数值所度量的优、劣程度决定它在下一代是被淘汰还是被遗传

  • 交叉
    交叉操作是将选择的两个个体p1和 p2
    作为父母节点,将两者的部分码值进行交换。假设有下面的两个节点的二进制编码表示:

    随机产生一个1到7之间的随机数,假设为3,则将p1和 p2的低三位进行互换,如下图所示,就完成了交叉操作:
  • 变异
    变异操作就是改变一个DNA序列上某一个点,

    我们以一定的概率进行变异的操作,例如上方的DNA序列的第6个结点,由1变成了0

代码示例

1、寻找函数最大值

首先我们生成POP_SIZE个长度为DNA_SIZE的DNA序列

csharp 复制代码
pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE))

定义函数图像

csharp 复制代码
def F(x):
    """
    定义一个函数,就是图像中的线条
    :param x:
    :return:
    """
    return np.sin(10*x)*x + np.cos(2*x)*x     # to find the maximum of this function

我们可以将DNA转换为函数中x轴中的某个点,将0 1 DNA序列翻译成范围在(0, 5)的数字

csharp 复制代码
def translateDNA(pop):
    """
    将0 1 DNA序列翻译成范围在(0, 5)的数字
    :param pop:
    :return:
    """
    return pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2**DNA_SIZE-1) * X_BOUND[1]

x轴上对应的y轴上的值,我们将其称为适应度

csharp 复制代码
def get_fitness(pred):
    """
    获取个体的适用度
    :param pred:
    :return:
    """
    return pred + 1e-3 - np.min(pred)   #防止适应度为负数

我们从种群中选择个体
注意: 并不是每次都选择适应度最高的个体,只是适应度高的个体选择的概率比较大,其它适应度较低的个体,选择的概率小,将来适应度低的个体变异后,适应度可能会变大

csharp 复制代码
def select(pop, fitness):    # nature selection wrt pop's fitness
    """
    从种群中选择
    :param pop:
    :param fitness:
    :return: 所选个体的下标,可重复选择replace=True
    """
    idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,
                           p=fitness/fitness.sum())
    return pop[idx]

 pop = select(pop, fitness)

之后我们对选择后的种群进行交叉和变异

csharp 复制代码
def crossover(parent, pop):     # mating process (genes crossover)
    """
    交叉配对
    :param parent:
    :param pop:
    :return:
    """
    if np.random.rand() < CROSS_RATE:
        i_ = np.random.randint(0, POP_SIZE, size=1)                             # select another individual from pop 从种群中选择一个个体,
        cross_points = np.random.randint(0, 2, size=DNA_SIZE).astype(np.bool_)   # choose crossover points  生成要交叉的位置
        parent[cross_points] = pop[i_, cross_points]                            # mating and produce one child   进行交叉
    return parent

def mutate(child):
    """
    变异
    :param child:
    :return:
    """
    for point in range(DNA_SIZE):
        if np.random.rand() < MUTATION_RATE:
            child[point] = 1 if child[point] == 0 else 0
    return child

 # 复制一个副本
 pop_copy = pop.copy()
 # 进行遗传 和变异
 for parent in pop:
     child = crossover(parent, pop_copy)
     child = mutate(child)
     parent[:] = child  # parent is replaced by its child 父亲个体被孩子代替

最后,我们对其迭代多次

csharp 复制代码
    for _ in range(N_GENERATIONS):
        # 得到图像上的y值
        F_values = F(translateDNA(pop))
        # 更新散点图
        if 'sca' in globals(): sca.remove()
        sca = plt.scatter(translateDNA(pop), F_values, s=200, lw=0, c='red', alpha=0.5)
        plt.pause(0.1)
        # 获取每一个个体的适应度
        fitness = get_fitness(F_values)

        #获取最好适用度的个体下标
        i = np.argmax(fitness)
        print("最优DNA",pop[i,:])
        pop = select(pop, fitness)
        # 复制一个副本
        pop_copy = pop.copy()
        # 进行遗传 和变异
        for parent in pop:
            child = crossover(parent, pop_copy)
            child = mutate(child)
            parent[:] = child  # parent is replaced by its child 父亲个体被孩子代替

完整代码:

csharp 复制代码
#!/usr/bin/env python 
# -*- coding:utf-8 -*-
import time

import numpy as np
import matplotlib.pyplot as plt

DNA_SIZE = 10            # DNA length  DNA长度
POP_SIZE = 100           # population size  种群大小
CROSS_RATE = 0.8         # mating probability (DNA crossover)  交叉配对的概率
MUTATION_RATE = 0.003    # mutation probability  编译的概率
N_GENERATIONS = 200     #   代数
X_BOUND = [0, 5]         # x upper and lower bounds   x轴范围

def F(x):
    """
    定义一个函数,就是图像中的线条
    :param x:
    :return:
    """
    return np.sin(10*x)*x + np.cos(2*x)*x     # to find the maximum of this function

def get_fitness(pred):
    """
    获取个体的适用度
    :param pred:
    :return:
    """
    return pred + 1e-3 - np.min(pred)   #防止适应度为负数
def translateDNA(pop):
    """
    将0 1 DNA序列翻译成范围在(0, 5)的数字
    :param pop:
    :return:
    """
    return pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2**DNA_SIZE-1) * X_BOUND[1]


def select(pop, fitness):    # nature selection wrt pop's fitness
    """
    从种群中选择
    :param pop:
    :param fitness:
    :return: 所选个体的下标,可重复选择replace=True
    """
    idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,
                           p=fitness/fitness.sum())
    return pop[idx]



def crossover(parent, pop):     # mating process (genes crossover)
    """
    交叉配对
    :param parent:
    :param pop:
    :return:
    """
    if np.random.rand() < CROSS_RATE:
        i_ = np.random.randint(0, POP_SIZE, size=1)                             # select another individual from pop 从种群中选择一个个体,
        cross_points = np.random.randint(0, 2, size=DNA_SIZE).astype(np.bool_)   # choose crossover points  生成要交叉的位置
        parent[cross_points] = pop[i_, cross_points]                            # mating and produce one child   进行交叉
    return parent

def mutate(child):
    """
    变异
    :param child:
    :return:
    """
    for point in range(DNA_SIZE):
        if np.random.rand() < MUTATION_RATE:
            child[point] = 1 if child[point] == 0 else 0
    return child



if __name__ == '__main__':
    # initialize the pop DNA   s生成0-1的POP_SIZE行 DNA_SIZE列的种群
    pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE))
    plt.ion()  # something about plotting
    # x轴
    x = np.linspace(*X_BOUND, 200)
    plt.plot(x, F(x))
    # 迭代N_GENERATIONS次
    for _ in range(N_GENERATIONS):
        # 得到图像上的y值
        F_values = F(translateDNA(pop))
        # 更新散点图
        if 'sca' in globals(): sca.remove()
        sca = plt.scatter(translateDNA(pop), F_values, s=200, lw=0, c='red', alpha=0.5)
        plt.pause(0.1)
        # 获取每一个个体的适应度
        fitness = get_fitness(F_values)

        #获取最好适用度的个体下标
        i = np.argmax(fitness)
        print("最优DNA",pop[i,:])
        pop = select(pop, fitness)
        # 复制一个副本
        pop_copy = pop.copy()
        # 进行遗传 和变异
        for parent in pop:
            child = crossover(parent, pop_copy)
            child = mutate(child)
            parent[:] = child  # parent is replaced by its child 父亲个体被孩子代替

    plt.ioff()
    plt.show()
相关推荐
xiaoshiguang32 小时前
LeetCode:222.完全二叉树节点的数量
算法·leetcode
爱吃西瓜的小菜鸡2 小时前
【C语言】判断回文
c语言·学习·算法
别NULL2 小时前
机试题——疯长的草
数据结构·c++·算法
TT哇2 小时前
*【每日一题 提高题】[蓝桥杯 2022 国 A] 选素数
java·算法·蓝桥杯
yuanbenshidiaos4 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习4 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA4 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
chengooooooo4 小时前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展
jackiendsc4 小时前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
游是水里的游5 小时前
【算法day20】回溯:子集与全排列问题
算法