数学建模(二):遗传算法(GA)

这篇具有很好参考价值的文章主要介绍了数学建模(二):遗传算法(GA)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

最优化之遗传算法

一、 概述

1、 算法简介

计算智能(Computational Intelligence,CI)方法主要包括:

  • 神经网络(Neural Network,NN);
  • 模糊逻辑(Fuzzy Logic,FL);
  • 遗传算法(Genetic Algorithm,GA);
  • 蚁群优化算法(Ant Colony Optimization,ACO);
  • 粒子群优化算法(Particle Swarm Op);
  • 免疫算法(Immune Algorithm,IA);
  • 分布估计算法(Estimation of Distribution Algorithm,EDA);
  • Memetic算法(Memetic Algorithm,MA);
  • 模拟退火(Simulated Annealing,SA);
  • 禁忌搜索(Tabu Search,TS)。

后面会一一学习。

遗传算法(Genetic Algorithm)是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法。遗传算法是从代表问题可能潜在的解集的一个种群(population)开始的,而一个种群则由经过基因(gene)编码的一定数目的个体(individual)组成。每个个体实际上是染色体(chromosome)带有特征的实体。染色体作为遗传物质的主要载体,即多个基因的集合,其内部表现(即基因型)是某种基因组合,它决定了个体的形状的外部表现,如黑头发的特征是由染色体中控制这一特征的某种基因组合决定的。因此,在一开始需要实现从表现型到基因型的映射即编码工作。由于仿照基因编码的工作很复杂,我们往往进行简化,如二进制编码,初代种群产生之后,按照适者生存优胜劣汰的原理,逐代(generation)演化产生出越来越好的近似解,在每一代,根据问题域中个体的适应度(fitness)大小选择(selection)个体,并借助于自然遗传学的遗传算子(genetic operators)进行组合交叉(crossover)和变异(mutation),产生出代表新的解集的种群。这个过程将导致种群像自然进化一样的后生代种群比前代更加适应于环境,末代种群中的最优个体经过解码(decoding),可以作为问题近似最优解。

主要思想:

  • 遗传算法是根据达尔文的“适者生存,优胜劣汰”的思想来找到最优解的额,其特点是所找到的解是全局最优解,相对于蚁群算法可能出现的局部最优解还是有优势的。

主要名词:

  • 个体(染色体):一个染色体代表一个具体问题的一个解,一个染色体包含若干基因。
  • 基因:一个基因代表具体问题解的一个决策变量。
  • 种群:多个个体(染色体)构成一个种群。即一个问题的多组解构成了解的种群。我们的目的就是让种群中”优胜劣汰“,最终只剩下一个最优解。接下来介绍最基本遗传算法,只用了选择,交叉,变异三种遗传算子。

2、 简单实例

ga建模,建模,python,# 机器学习,算法,python,机器学习

ga建模,建模,python,# 机器学习,算法,python,机器学习

ga建模,建模,python,# 机器学习,算法,python,机器学习

3、 主要步骤

相信大家看完那个简单实例之后,对我们遗传算法的实现原理有了一个初步的认识。现在,让我们来总结一些其实现的步骤。

ga建模,建模,python,# 机器学习,算法,python,机器学习

  1. 种群初始化。我们需要首先通过随机生成的方式来创造一个种群,一般该种群的数量为100~500,这里我们采用二进制将一个染色体(解)编码为基因型。随后用进制转化,将二进制的基因型转化成十进制的表现型。

  2. 适应度计算(种群评估)。这里我们直接将目标函数值作为个体的适应度。

  3. 选择(复制)操作。根据种群中个体的适应度大小,通过轮盘赌等方式将适应度高的个体从当前种群中选择出来。其中轮盘赌即是与适应度成正比的概率来确定各个个体遗传到下一代群体中的数量。

    具体步骤如下:

    • 首先,计算出所有个体的适应度总和 Σfi;
    • 其次,计算出每个个体的相对适应度大小fi/Σfi,类似于softmax;
    • 再产生一个 0 到 1 之间的随机数,依据随机数出现在上述哪个概率区域内来确定各个个体被选中的次数。
  4. 交叉(交配)运算。该步骤是遗传算法中产生新的个体的主要操作过程,它用一定的交配概率阈值(pc,一般是0.4到0.99)来控制是否采取单点交叉,多点交叉等方式生成新的交叉个体。

    具体步骤如下:

    • 先对群体随机配对;
    • 再随机设定交叉点的位置;
    • 再互换配对染色体间的部分基因。
  5. 变异运算。该步骤是产生新的个体的另一种操作。一般先随机产生变异点,再根据变异概率阈值(pm,一般是0.0001到0.1)将变异点的原有基因取反。

  6. 终止判断。如果满足条件(迭代次数,一般是200~500)则终止算法,否则返回 step2 。

二、 步骤详解

先来看一个简单的函数优化的实例,来体验下遗传算法是如何实际应用的。

已知 ga建模,建模,python,# 机器学习,算法,python,机器学习 ,求函数y的最大值。要求解精确到小数点后4 位。

ga建模,建模,python,# 机器学习,算法,python,机器学习

1、 染色体编码

在遗传算法中,问题的每个有效解被称为一个“染色体 (chromosome)”,也称为“串”,对应于种群中的每个生物个体(individual)。

比如在这个问题中,每一个符合条件的有效解 ( x 1 , x 2 , x 3 , x 4 ) (x1, x2, x3, x4) (x1,x2,x3,x4),即表示一个染色体。

许多应用问题的结构很复杂,我们希望找到一种既简单又不影响算法性能的编码方式。

  • 将问题结构变换为位串形式编码表示的过程叫做编码;
  • 将位串形式编码表示变换为原问题结构的过程叫做解码或译码。

遗传算法中,一般的编码方法分为2种。

第1种是二进制编码方法。二进制编码方法产生的染色体是一个二进制符号序列,染色体的每一个基因只能取值 0 或 1 。

假定问题定义的有效解取值空间为 [ U m i n , U m a x ] [U_{min},U_{max}] [Umin,Umax] ,使用 L 位二进制符号串表示解的一维变量 ,则我们可以得到如下表所示的编码方式。

ga建模,建模,python,# 机器学习,算法,python,机器学习

第2种是浮点数编码方法。浮点数编码方法中每个染色体用某一范围内的一个浮点数来表示,染色体的编码长度等于问题定义的解的变量个数,染色体的每一个基因等于解的每一维变量。其中使用D作为有效解的变量维数。

待求解问题的一个有效解为 $X_i=(x_i1,x_i2,⋯,x_i{D-1},x_i{D}) $。则该解对应的染色体编码为 ( x i 1 , x i 2 , ⋯ , x i D − 1 , x i D ) (x_i^1,x_i^2,⋯,x_i^{D-1},x_i^D) (xi1,xi2,,xiD1,xiD)

因为这种编码方法使用的是变量的真实值,所以浮点数编码方法也叫做真值编码方法。对于一些多维、高精度要求的连续函数优化问题,用浮点数编码来表示个体时将会有一些益处。

在我们的实例中,我们采用第二种浮点数编码方法构造染色体,即每个染色体以 ( x 1 , x 2 , x 3 , x 4 ) (x_1,x_2,x_3,x_4) (x1,x2,x3,x4) 的形式表示。

2、 种群初始化

采用生成随机数的方法,对染色体的每一维变量进行初始化赋值。初始化染色体时必须满足优化问题对有效解的定义。产生出规模为 N 的种群集合。

在我们的实例中,我们假设种群规模为5,随机初始化种群的染色体,得到:

ga建模,建模,python,# 机器学习,算法,python,机器学习

3、 适应度评价

为了体现染色体的适应能力,引入了对每一个染色体都能进行度量的函数,叫做适应度函数 (fitness function)。通过计算适应度函数值,来决定染色体的优劣程度,它体现了自然进化中的优胜劣汰原则。

在遗传算法中,规定适应值越大的染色体越优。适应度函数要求能有效地反映每一个染色体的适应能力。若一个染色体与问题的最优解染色体之间的差距较小,则对应的适应度函数值就会较大。

评估函数常常根据问题的优化目标来确定,例如求解函数优化问题时,问题定义的目标函数可以作为评估函数的原型。

对于一些求解最大值的数值优化问题,我们可以直接套用问题定义的函数表达式。但是对于其他优化问题,问题定义的目标函数表达式必须经过一定的变换。

在我们的实例中,由于本身就是求解最大值的,故可以直接使用y作为适应值评价函数。即:

ga建模,建模,python,# 机器学习,算法,python,机器学习

计算每个染色体的适应值如下:

ga建模,建模,python,# 机器学习,算法,python,机器学习

因此,初始条件下的最优解为:ga建模,建模,python,# 机器学习,算法,python,机器学习

4、 选择算子

选择操作也叫做复制(reproduction)操作,根据个体的适应度函数值所度量的优劣程度决定它在下一代是被淘汰还是被遗传。

一般地,适应度较大(优良)的个体有较大的存在机会,而适应度较小(低劣)的个体继续存在的机会也较小。简单遗传算法采用赌轮选择机制
根据每个染色体的适应值得到种群中所有染色体的适应值总和。计算每个染色体适应值与种群适应值总和的比 P i P_i Pi

假设一个具有N个扇区的轮盘,每个扇区对应种群中的一个染色体,扇区的大小与对应染色体的 P i P_i Pi 值成正比。

ga建模,建模,python,# 机器学习,算法,python,机器学习

轮盘赌模型的计算机实现可以参照下面实例中的操作方式。

在实例中,采用轮盘赌算法,计算种群适应度总和为0.0373603+0.0255988+0.0448529+0.0238214+0.0331319=0.164765。分别计算每个染色体适应值同种群适应值总和的比, [ C 1 , C 2 , C 3 , C 4 , C 5 ] [C_1, C_2, C_3, C_4, C_5] [C1,C2,C3,C4,C5] 依次为0.226749,0.155365,0.272223,0.144578,0.201085。累计值依次为0.226749,0.382114,0.654337,0.798915,1。

下面从[0,1]之间随机地产生5个随机数,落在哪个区间内,选择对应的染色体。

(1): 0.278756 对应选择的是 C 2 C_2 C2

(2): 0.604389 对应选择的是 C 3 C_3 C3

(3): 0.230964 对应选择的是 C 2 C_2 C2

(4): 0.376263 对应选择的是 C 2 C_2 C2

(5): 0.858791 对应选择的是 C 5 C_5 C5

经过选择算子以后,得到的种群即为:

ga建模,建模,python,# 机器学习,算法,python,机器学习

5、 交叉算子

交叉操作的简单方式是: 按交叉概率 P c P_c Pc 选择出两个父代个体 P 1 P_1 P1 P 2 P_2 P2 ,将两者的部分基因(码值)进行交换。(当然也可以采取其他方式,目的在于打乱顺序且不会错乱溢出。)

交叉概率的 P c P_c Pc 一般取值为:0.4 到 0.99 之间。随机数 Random(0, 1) 小于 P c P_c Pc ,则表示该染色体可进行交叉操作。随机产生一个有效的交配位置,父代染色体交换位于该交配位置后的所有基因。

ga建模,建模,python,# 机器学习,算法,python,机器学习

在我们的实例中,假设交配概率为 0.88。下面生成 5 个 0 到 1 之间的随机数,决定这 5 个染色体是否参加交配。

(1): 0.341044<0.88,故 C 1 ′ C_1^{'} C1 参加交配;

(2): 0.613782<0.88,故 C 2 ′ C_2^{'} C2 参加交配;

(3): 0.972173>0.88,故 C 3 ′ C_3^{'} C3 不参加交配;

(4): 0.310424<0.88 ,故 C 4 ′ C_4^{'} C4 参加交配;

(5): 0.672331<0.88,故 C 5 ′ C_5^{'} C5 参加交配。

C 1 ′ C_1^{'} C1 C 2 ′ C_2^{'} C2 C 4 ′ C_4^{'} C4 C 5 ′ C_5^{'} C5 进行交配,每对染色体交配时随机生成1到4之间的自然数作为交配位

ga建模,建模,python,# 机器学习,算法,python,机器学习

这样交叉操作之后得到的新种群为:

ga建模,建模,python,# 机器学习,算法,python,机器学习

6、 变异算子

变异操作的简单方式就是改变码串中某个位置上的数码,变异概率的 P m P_m Pm 一般取值为:0.001 到 0.2 之间。随机数 Random(0, 1) 小于 P m P_m Pm ,则表示该染色体可进行变异操作随机产生一个有效的变异位置,染色体上位于该位置的基因发生改变。

二进制编码表示的每个位置的数码只有0和1两种可能,二进制编码的简单变异操作是将0 与1互换:0变异为1,1变异为0。浮点数编码形式的染色体若某基因发生变异,则可采用随机数方法产生一个满足问题定义的数值 取代该基因现有的值。

在实例当中,假设变异概率为0.1。对于5个染色体的20个基因随机生成0 到1 之间的随机数,若该随机数小于0.1,则改变该基因的值为另一符合条件的值作为变异,否则不改变基因的值。

以下是发生变异的两个染色体和基因改变的过程:

ga建模,建模,python,# 机器学习,算法,python,机器学习

7、 适者生存

重新计算新种群中各个染色体的适应值。倘若新种群的最大适应值大于现在的最佳适应值, 则以该最大适应值对应的染色体更新为最优染色体。

在我们的例子中,计算新种群中每个染色体的适应值如下:

ga建模,建模,python,# 机器学习,算法,python,机器学习

8、 终止条件

决定算法何时停止运行,输出找到的最优解。采用何种终止条件,跟具体问题的应用有关。

可以使算法在达到最大进化代数时停止,最大进化代数一般可设置为 100 代到 1000 代,根据具体问题可对该建议值作相应的修改。也可以通过考察找到的当前最优解的情况来控制算法的停止。

例如,当目前进化过程算法找到的最优解达到一定的误差要求,则算法可以停止。 误差范围的设置同样跟具体的优化问题相关。或者是算法在持续很长的一段进化时间内所找到的最优解没有得到改善时,算法可以停止。

三、 python实现

1、 种群初始化

这里我们需要初始化一个种群:

def init(N_species):
    """
    生成 -1 ~ 1 之间的随机数
    
    Parameters
    ----------
    N : int
        需要初始化的种群数量.

    Returns
    -------
    species : list
        返回生成的初始化种群

    """    
    species = []
    for i in range(N_species):
        species.append([
            random() * 2 - 1, random() * 2 - 1, random() * 2 - 1, random() * 2 - 1
            ])
    
    return species

我们使用的是浮点数编码,则这里可以跳过编码的环节。

2、 计算适应度

首先,我们需要确定我们的目标是什么,由此来确定适应度的表达方式

def f(x_1, x_2, x_3, x_4):
    return 1 / (pow(x_1, 2) + pow(x_2, 2) + pow(x_3, 2) + pow(x_4, 2) + 1) 


def fitness(f, species):
    """
    计算种群适应度

    Parameters
    ----------
    f : function
        传入需要求最大值的函数.
    species : list
        传入种群所有的种群里面的值.

    Returns
    -------
    fitness : list
        返回适应度占比组成的列表.
    best : double
        返回当前的最优解
    """
    _fitness = []
    for i in species:
        val = f(*tuple(i))  # 计算出函数值
        _fitness.append(val)  
    
    
    return _fitness

def bests(population, _fit):
    """
    获取种群中,最优的个体,返回二进制数据以及适应度

    Parameters
    ----------
    population : list
        传入种群.
    _fit : list
        传入种群的适应度.

    Returns
    -------
    best_individual : double
        返回最优个体的具体数值.
    best_fit : double
        返回最优的适应度.

    """
    best_individual_lis, best_fit = [], _fit[0]
    # 查找种群中最优的个体
    for i in range(len(population)):
        if best_fit < _fit[i]:
            best_fit = _fit[i]  # 获得适应度最高的个体
            best_individual_lis = population[i]  # 传入染色体
    return [best_fit, best_individual_lis]  

由上面的的选择操作可知,适应度是选择操作的主要参考依据,适应度函数(Fitness Function)的选取直接影响到遗传算法的收敛速度以及能否找到最优解。因而适应度函数的选择问题在遗传算法中是一项很值得研究的课题。一般情况下,关于适应度与目标函数的选择有以下这两种关系:

ga建模,建模,python,# 机器学习,算法,python,机器学习

选择与适应函数

3、 选择算子

def cumsum(fitness):
    """
    传入计算的适应度,给适应度划分区间;
    计算适应度斐波纳挈列表,这里是为了求出累积的适应度

    Parameters
    ----------
    fitness : list
        传入适应度.

    Returns
    -------
    None.
        使用指针,直接修改原列表.

    """
    for i in range(len(fitness) - 1, -1, -1):
        total = 0
        j = 0
        while(j <= i):
            total += fitness[j]
            j += 1
        # 这里是为了将适应度划分成区间
        fitness[i] = total


def selection(population, _fit):
    """
    使用轮盘赌的方法来筛选优势种群

    Parameters
    ----------
    population : list
        传入种群.
    _fit : list
        传入适应度.

    Returns
    -------
    None.
        使用指针,直接修改原列表.

    """
    relative_fitness = []
    # 将所有个体的适应度概率化,类似于softmax
    total_fit = sum(_fit)
    for i in _fit:
        relative_fitness.append(i / total_fit)
    
    # 将适应度转换为斐波那契列表
    cumsum(relative_fitness)
    
        
    # 使用随机数的方法,来确定哪些种群可以存活
    # 产生种群个数的随机值
    exist_population = []
    for i in range(len(population)):
        exist_population.append(random())
    exist_population.sort()  # 升序排列,会从低到高不断匹配种群
    
    new_population = []
    fit_i, new_i = 0, 0  # 分别存放适应度指针,和随机数指针
    while new_i < len(population):
        if (exist_population[new_i] < relative_fitness[fit_i]):
            new_population.append(population[fit_i])
            new_i += 1
        else:
            fit_i += 1
            
    population = new_population        

4、 交叉算子

def crossover(population, chromosome_length, Cross_rate):
    """
    进行交叉操作,这里是利用列表,直接对原数据进行修改,不设置返回值
    这里选择的是一个双点交叉,可以进行多点交叉


    Parameters
    ----------
    population : list
        传入种群.
    chromosome_length : int
        染色体长度.
    Cross_rate : double
        交叉概率,[0.4, 0.99].

    Returns
    -------
    None.

    """
    
    for i in range(len(population) - 1):
        if random() > Cross_rate:  # 如果大于交叉概率,则不进行交叉
            continue
        # 这里进行双点交叉
        num1, num2 = randint(0, round(chromosome_length / 2) ), randint(round(chromosome_length / 2), chromosome_length - 1)
        population[i][num1:num2], population[i + 1][num1:num2] = population[i + 1][num1:num2], population[i][num1:num2]
        # 把第 i + 1 个染色体中的后 num1 到第 i + 1 个染色体中的 num2 与 i 的互换        

5、 变异算子

def mutation(Muta_rate, species):
    """
    进行变异操作,这里是利用列表,直接对原数据进行修改,不设置返回值
    这里选择的是单点变异

    Parameters
    ----------
    Muta_rate : double
        变异概率,[0.001, 0.2].
    species : list
        种群.

    Returns
    -------
    None.

    """
    for i in range(len(species)):
        if random() < Muta_rate:
            muta_num = randint(0, 3)
            species[i][muta_num] = random() * 2 - 1

6、 主程序

def main(N_species, Cross_rate, Muta_rate, max_iteration, results): 
    """
    主函数

    Parameters
    ----------
    N_species : int
        种群的数量.
    Cross_rate : double
        交叉概率.
    Muta_rate : double
        变异概率.
    max_iteration : int
        最大繁衍的代数.
    results : list
        用来存放最优适应度.

    Returns
    -------
    status_code.

    """

    species = init(N_species)
    
    for i in range(max_iteration):
        _fitness = fitness(f, species)
        
        results.append(bests(species, _fitness))

                
        selection(species, _fitness)  # 进行选择操作
        crossover(species, 4, Cross_rate)  # 进行交叉操作
        mutation(Muta_rate, species)  # 进行变异操作

        
    results.sort()
    X, Y = [], []
    for i in range(max_iteration):
        X.append(i)
        Y.append(results[i][0])
    
    plt.plot(X, Y)
    
    return 0

7、 总代码

# -*- coding: utf-8 -*-
"""
Created on Wed Mar 29 19:09:23 2023

@author: steve
"""
from random import (
    random, randint
    )
import matplotlib.pyplot as plt


def init(N_species):
    """
    生成 -1 ~ 1 之间的随机数
    
    Parameters
    ----------
    N : int
        需要初始化的种群数量.

    Returns
    -------
    species : list
        返回生成的初始化种群

    """    
    species = []
    for i in range(N_species):
        species.append([
            random() * 2 - 1, random() * 2 - 1, random() * 2 - 1, random() * 2 - 1
            ])
    
    return species



def f(x_1, x_2, x_3, x_4):
    return 1 / (pow(x_1, 2) + pow(x_2, 2) + pow(x_3, 2) + pow(x_4, 2) + 1) 


def fitness(f, species):
    """
    计算种群适应度

    Parameters
    ----------
    f : function
        传入需要求最大值的函数.
    species : list
        传入种群所有的种群里面的值.

    Returns
    -------
    fitness : list
        返回适应度占比组成的列表.
    best : double
        返回当前的最优解
    """
    _fitness = []
    for i in species:
        val = f(*tuple(i))  # 计算出函数值
        _fitness.append(val)  
    
    
    return _fitness


def cumsum(fitness):
    """
    传入计算的适应度,给适应度划分区间;
    计算适应度斐波纳挈列表,这里是为了求出累积的适应度

    Parameters
    ----------
    fitness : list
        传入适应度.

    Returns
    -------
    None.
        使用指针,直接修改原列表.

    """
    for i in range(len(fitness) - 1, -1, -1):
        total = 0
        j = 0
        while(j <= i):
            total += fitness[j]
            j += 1
        # 这里是为了将适应度划分成区间
        fitness[i] = total


def selection(population, _fit):
    """
    使用轮盘赌的方法来筛选优势种群

    Parameters
    ----------
    population : list
        传入种群.
    _fit : list
        传入适应度.

    Returns
    -------
    None.
        使用指针,直接修改原列表.

    """
    relative_fitness = []
    # 将所有个体的适应度概率化,类似于softmax
    total_fit = sum(_fit)
    for i in _fit:
        relative_fitness.append(i / total_fit)
    
    # 将适应度转换为斐波那契列表
    cumsum(relative_fitness)
    
        
    # 使用随机数的方法,来确定哪些种群可以存活
    # 产生种群个数的随机值
    exist_population = []
    for i in range(len(population)):
        exist_population.append(random())
    exist_population.sort()  # 升序排列,会从低到高不断匹配种群
    
    new_population = []
    fit_i, new_i = 0, 0  # 分别存放适应度指针,和随机数指针
    while new_i < len(population):
        if (exist_population[new_i] < relative_fitness[fit_i]):
            new_population.append(population[fit_i])
            new_i += 1
        else:
            fit_i += 1
            
    population = new_population        

def crossover(population, chromosome_length, Cross_rate):
    """
    进行交叉操作,这里是利用列表,直接对原数据进行修改,不设置返回值
    这里选择的是一个双点交叉,可以进行多点交叉


    Parameters
    ----------
    population : list
        传入种群.
    chromosome_length : int
        染色体长度.
    Cross_rate : double
        交叉概率,[0.4, 0.99].

    Returns
    -------
    None.

    """
    
    for i in range(len(population) - 1):
        if random() > Cross_rate:  # 如果大于交叉概率,则不进行交叉
            continue
        # 这里进行双点交叉
        num1, num2 = randint(0, round(chromosome_length / 2) ), randint(round(chromosome_length / 2), chromosome_length - 1)
        population[i][num1:num2], population[i + 1][num1:num2] = population[i + 1][num1:num2], population[i][num1:num2]
        # 把第 i + 1 个染色体中的后 num1 到第 i + 1 个染色体中的 num2 与 i 的互换        
    
        
        
def mutation(Muta_rate, species):
    """
    进行变异操作,这里是利用列表,直接对原数据进行修改,不设置返回值
    这里选择的是单点变异

    Parameters
    ----------
    Muta_rate : double
        变异概率,[0.001, 0.2].
    species : list
        种群.

    Returns
    -------
    None.

    """
    for i in range(len(species)):
        if random() < Muta_rate:
            muta_num = randint(0, 3)
            species[i][muta_num] = random() * 2 - 1

def bests(population, _fit):
    """
    获取种群中,最优的个体,返回二进制数据以及适应度

    Parameters
    ----------
    population : list
        传入种群.
    _fit : list
        传入种群的适应度.

    Returns
    -------
    best_individual : double
        返回最优个体的具体数值.
    best_fit : double
        返回最优的适应度.

    """
    best_individual_lis, best_fit = [], _fit[0]
    # 查找种群中最优的个体
    for i in range(len(population)):
        if best_fit < _fit[i]:
            best_fit = _fit[i]  # 获得适应度最高的个体
            best_individual_lis = population[i]  # 传入染色体
    return [best_fit, best_individual_lis]  
    

        
def main(N_species, Cross_rate, Muta_rate, max_iteration, results): 
    """
    主函数

    Parameters
    ----------
    N_species : int
        种群的数量.
    Cross_rate : double
        交叉概率.
    Muta_rate : double
        变异概率.
    max_iteration : int
        最大繁衍的代数.
    results : list
        用来存放最优适应度.

    Returns
    -------
    status_code.

    """

    species = init(N_species)
    
    for i in range(max_iteration):
        _fitness = fitness(f, species)
        
        results.append(bests(species, _fitness))

                
        selection(species, _fitness)  # 进行选择操作
        crossover(species, 4, Cross_rate)  # 进行交叉操作
        mutation(Muta_rate, species)  # 进行变异操作

        
    results.sort()
    X, Y = [], []
    for i in range(max_iteration):
        X.append(i)
        Y.append(results[i][0])
    
    plt.plot(X, Y)
    
    return 0
    
        
            
if __name__ == "__main__":
    N_species = 60  # 种群的数量,最好选择偶数,便于进行交叉
    Cross_rate = 0.5  # 交叉概率,[0.4, 0.99]
    Muta_rate = 0.01  # 变异概率,[0.001, 0.2]
    max_iteration = 1000  # 最大繁殖的代数,[100, 1000]
    results = []  # 用来存放每一次的最优适应度

    main(N_species, Cross_rate, Muta_rate, max_iteration, results)

四、 案例

1、 需求分析

y = 2 ∗ s i n ( x ) + c o s ( x ) y = 2*sin(x)+cos(x) y=2sin(x)+cos(x)的最大值:

分析步骤:

  1. 我们首先需要分析染色体的编码方式,这里我们使用二进制数据来进行编码

    通过使用比例来得到具体的值

  2. 初始化种群

  3. 确定适应度函数

    适应度函数的选择

  4. 选择、交叉、变异操作

    多点交叉、多点变异文章来源地址https://www.toymoban.com/news/detail-786756.html

2、 实现代码

# -*- coding: utf-8 -*-
"""
Created on Thu Mar 30 10:47:02 2023
需求: 目标求解2*sin(x)+cos(x)最大值

@author: steve
"""

from random import (
    randint, random
    )

from math import (
    sin, cos
    )
from matplotlib import pyplot as plt


f = lambda x : 2*sin(x)+cos(x)  # 我们的目标函数

def species_origin(population_size, chromosome_length):
    """
    进行种群的初始化操作

    Parameters
    ----------
    population_size : int
        种群的规模.
    chromosome_length : int
        个体中染色体的数量.

    Returns
    -------
    list
        返回生成的种群,其是一个二维数组.

    """
    populations = []  # 用来存放初始化完成的种群
    for i in range(population_size):
        temp_chromoseome = []  # 用来存放生成的染色体
        for j in range(chromosome_length):
            temp_chromoseome.append(randint(0, 1))  # 随机产生一个染色体,存入准备好的容器中
        populations.append(temp_chromoseome)  # 将生成的个体存入准备好的容器中
    
    return populations  # 将种群返回,种群是个二维数组,个体和染色体两维
    

def translation(populations, chromosome_length):
    """
    进行二进制到十进制数据的转换

    Parameters
    ----------
    populations : list
        传入一个种群.
    chromosome_length : int
        传入每个个体染色体的数量.

    Returns
    -------
    list
        返回转换后的十进制数据.

    """
    temp_data = []
    for population in populations:
        total = 0
        for (index, value) in enumerate(population):
            num = chromosome_length - index - 1  # 二进制数据转换是从右到左的
            total += value * pow(2, num)  # 二进制转换为十进制的方法是按权相加

        temp_data.append(total)  # 将十进制的值存入容器中
    
    return temp_data  # 将十进制数据返回

def fitness(populations, chromosome_length, max_value):
    """
    目标函数相当于环境 对染色体进行筛选,这里是2*sin(x)+cos(x)
    这里是计算函数的值
        这里求的是最大值:
            小于零的函数值,其几乎没什么作用,但是又会对整体的适应度之和照成影响,把其取为 0

    Parameters
    ----------
    populations : list
        种群.
    chromosome_length : int
        染色体数量.
    max_value : int
        x 的最大值.

    Returns
    -------
    list
        返回函数值构成的列表.

    """

    data = translation(populations, chromosome_length)  # 进行数据的反编码
    temp_fit = []
    for i in data:
        # 首先,我们需要对数据进行
        x = i * max_value / (pow(2,chromosome_length)-1)  # 计算出基因的值
        temp_fit.append(f(x) if f(x) > 0 else 0)  # 计算出函数的 y 值,并添加到容器中,小于0的y值,等于 0
    
    return temp_fit

def cumsum(fitness):
    """
    传入计算的适应度,给适应度划分区间;
    计算适应度斐波纳挈列表,这里是为了求出累积的适应度

    Parameters
    ----------
    fitness : list
        传入适应度.

    Returns
    -------
    None.
        使用指针,直接修改原列表.

    """
    for i in range(len(fitness) - 1, -1, -1):
        total = 0
        j = 0
        while(j <= i):
            total += fitness[j]
            j += 1
        # 这里是为了将适应度划分成区间
        fitness[i] = total


def selection(population, _fit):
    """
    使用轮盘赌的方法来筛选优势种群

    Parameters
    ----------
    population : list
        传入种群.
    _fit : list
        传入适应度.

    Returns
    -------
    None.
        使用指针,直接修改原列表.

    """
    
    relative_fitness = []
    # 将所有个体的适应度概率化,类似于softmax
    total_fit = sum(_fit)
    for i in _fit:
        relative_fitness.append(i / total_fit)
    
    # 将适应度转换为斐波那契列表
    cumsum(relative_fitness)
    
        
    # 使用随机数的方法,来确定哪些种群可以存活
    # 产生种群个数的随机值
    exist_population = []
    for i in range(len(population)):
        exist_population.append(random())
    exist_population.sort()  # 升序排列,会从低到高不断匹配种群
    
    new_population = []
    fit_i, new_i = 0, 0  # 分别存放适应度指针,和随机数指针
    while new_i < len(population):
        if (exist_population[new_i] < relative_fitness[fit_i]):
            new_population.append(population[fit_i])
            new_i += 1
        else:
            fit_i += 1
            
    population = new_population
            

def crossover(population, chromosome_length, Cross_rate):
    """
    进行交叉操作,这里是利用列表,直接对原数据进行修改,不设置返回值
    这里选择的是一个双点交叉,可以进行多点交叉


    Parameters
    ----------
    population : list
        传入种群.
    chromosome_length : int
        染色体长度.
    Cross_rate : double
        交叉概率,[0.4, 0.99].

    Returns
    -------
    None.

    """
    
    for i in range(len(population) - 1):
        if random() > Cross_rate:  # 如果大于交叉概率,则不进行交叉
            continue
        # 这里进行双点交叉
        num1, num2 = randint(0, round(chromosome_length / 2) ), randint(round(chromosome_length / 2), chromosome_length - 1)
        population[i][num1:num2], population[i + 1][num1:num2] = population[i + 1][num1:num2], population[i][num1:num2]
        # 把第 i + 1 个染色体中的后 num1 到第 i + 1 个染色体中的 num2 与 i 的互换
        

def mutation(population, chromosome_length, Muta_rate):
    """
    进行变异操作

    Parameters
    ----------
    population : list
        传入种群.
    chromosome_length : TYPE
        传入基因的长度.
    Muta_rate : TYPE
        传入变异率,[0.001, 0.2].

    Returns
    -------
    None.

    """
    for i in range(len(population) - 1):
        if random() > Muta_rate:  # 如果大于变异率,则不进行变异
            continue
        num = randint(0, chromosome_length - 1)  # 寻找变异位点
        population[i][num] = 0 if population[i][num] == 1 else 1  # 进行位点的变化
        

def bests(population, chromosome_length, max_value, _fit):
    """
    获取种群中,最优的个体,返回二进制数据以及适应度

    Parameters
    ----------
    population : list
        传入种群.
    chromosome_length : int
        个体中染色体的数量.
    max_value : int
        个体的最大值.
    _fit : list
        传入种群的适应度.

    Returns
    -------
    best_individual : double
        返回最优个体的具体数值.
    best_fit : double
        返回最优的适应度.

    """
    best_individual_lis, best_fit = [], _fit[0]
    # 查找种群中最优的个体
    for i in range(len(population)):
        if best_fit < _fit[i]:
            best_fit = _fit[i]  # 获得适应度最高的个体
            best_individual_lis = population[i]  # 传入染色体
    best_individual = translation([best_individual_lis], chromosome_length)[0] * max_value / (pow(2,chromosome_length)-1)  # 求出个体的具体数值
    return [best_fit, best_individual]  
            
    
def main(chromosome_length, population_size, max_value, Cross_rate, Muta_rate, max_iter, results):
    populations = species_origin(population_size, chromosome_length)  # 初始化种群


    for i in range(max_iter):
        _fit = fitness(populations, chromosome_length, max_value)  # 计算适应度
        results.append(bests(populations, chromosome_length, max_value, _fit))  # 计算最优的个体
        selection(populations, _fit)  # 进行选择
        crossover(populations, chromosome_length, Cross_rate)  # 进行交叉运算
        mutation(populations, chromosome_length, Muta_rate)  # 进行变异运算
    
    results.sort()
    X=[]
    Y=[]
    for i in range(max_iter):#500轮的结果
        X.append(i)
        Y.append(results[i][0])
    plt.plot(X,Y)


if __name__ == "__main__":
    chromosome_length = 20  # 染色体数量
    population_size = 300  # 种群的规模
    max_value = 10  # 限制基因中允许出现的最大值
    Cross_rate = 0.8  # 进行双点交叉的概率
    Muta_rate = 0.1  # 传入变异的概率
    max_iter = 100  # 传入最大繁衍次数
    results = []  # 传入每次繁衍的最优个体
    main(chromosome_length, population_size, max_value, Cross_rate, Muta_rate, max_iter, results)  # 运行主要的程序

到了这里,关于数学建模(二):遗传算法(GA)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 遗传算法[GA]

    遗传算法 (Genetic Algorithm,GA) 是模拟生物在自然环境中的遗传和进化的过程而形成的自适应 全局优化搜索算法。 遗传算法借鉴了达尔文的进化论和孟德尔的遗传学说。其本质是一种并行、高效、全局搜索的方法, 它能在搜索过程中自动获取和积累有关搜索空间的知识,并自

    2024年02月09日
    浏览(34)
  • Python实现GA遗传算法优化BP神经网络回归模型(BP神经网络回归算法)项目实战

    说明:这是一个机器学习实战项目(附带数据+代码+文档+视频讲解),如需数据+代码+文档+视频讲解可以直接到文章最后获取。 遗传算法(Genetic Algorithm,GA)最早是由美国的 John holland于20世纪70年代提出,该算法是根据大自然中生物体进化规律而设计提出的。是模拟达尔文生

    2024年02月14日
    浏览(227)
  • 遗传算法(Genetic Algorithm,GA)

    这是一篇关于遗传算法的总结博客,包括算法思想,算法步骤,python实现的两个简单例子,算法进阶(持续更新ing)。 遗传算法的应用很多,诸如寻路问题,8数码问题,囚犯困境,动作控制,找圆心问题(在一个不规则的多边形中,寻找一个包含在该多边形内的最大圆圈的

    2023年04月17日
    浏览(44)
  • 遗传算法 (Genetic Algorithm, GA)

    遗传算法(Genetic Algorithm,简称GA)起源于对生物系统所进行的计算机模拟研究,是一种随机全局搜索优化方法,它模拟了自然选择和遗传中发生的复制、交叉(crossover)和变异(mutation)等现象,从任一初始种群(Population)出发,通过随机选择、交叉和变异操作,产生一群更适合

    2024年02月05日
    浏览(37)
  • 遗传算法模型--数学建模

    遗传算法是一种模仿自然选择和遗传机制的优化算法,主要用于求解最优化问题。它模拟了生物进化过程中的遗传、交叉和变异过程,通过不断地进化优秀的个体,逐渐搜索到全局最优解。 在开始之前,我们先来了解下遗传算法中的几个概念。 在遗传算法中,我们首先需要

    2024年02月16日
    浏览(40)
  • 数学建模之遗传算法

    遗传算法是美国教授Holland于1975年提出的一种基于模仿生物遗传学的优化算法。这种算法很难得到问题的精确答案,但是能够在允许的时间复杂度内得到一个较优的答案。常用来解决一些目前不存在多项式算法的问题,如旅行商问题(TSP问题),背包问题。 我们假设在自然界

    2024年02月08日
    浏览(45)
  • 数学建模-退火算法和遗传算法

    退火算法和遗传算法 一.退火算法 退火算法Matlab程序如下: [W]=load(\\\'D:100个目标经度纬度.txt\\\'); 二、遗传算法 [E]=xlsread(\\\'D:100个目标经度纬度\\\');  % 加载敌方 100 个目标的数据, 数据按照表格中的位置保存在纯文本文件 sj.txt 中 x=[E(:,1)]; y=[E(:,2)]; e =[x y]; d1=[70,40]; e =[d1;  e ;d1];

    2024年02月20日
    浏览(56)
  • 【Matlab】智能优化算法_遗传算法GA

    遗传算法(Genetic Algorithm,简称GA)是一种基于生物进化理论的优化算法,由John Holland于20世纪70年代初提出。它通过模拟自然选择和遗传机制,利用群体中个体之间的遗传信息交流和变异来搜索问题的解空间。 遗传算法的设计灵感来源于达尔文的进化论。达尔文提出,自然界

    2024年02月16日
    浏览(57)
  • 数学建模学习(10):遗传算法

    遗传算法简介 • 遗传算法(Genetic Algorithms)是基于生物进化理论的原理发展起来的一种广为 应用的、高效的随机搜索与优化的方法。其主要特点是群体搜索策略和群体中个体之 间的信息交换,搜索不依赖于梯度信息。它是20世纪70年代初期由美国密执根 (Michigan)大学的霍

    2024年02月13日
    浏览(43)
  • 遗传算法与Matlab GA工具箱

    GA是一种进化算法,基本原理效仿生物界“物竞天择,适者生存”的演化法则。 一些基本概念 种群population:问题潜在的解集 个体individual:每一个可能的解,通过基因编码一定数目的个体形成一个种群 适应度fitness:由此判断个体的优良,进而进行选择 选择selection、交叉cr

    2024年02月09日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包