遗传算法python进阶理解+论文复现(纯干货,附前人总结引路)

news2024/11/15 3:22:56

遗传算法python进阶理解+论文复现(纯干货,附前人总结引路)

  • 一、简介和相关概念
    • 遗传算法简介
    • 相关概念介绍
  • 二、与其他智能优化算法的比较
    • 蚁群算法
    • 粒子群优化算法
    • 人工神经网络算法
    • 模拟退火算法
    • 鱼群算法
  • 三、必学知识(站在前人的肩膀上)
  • 四、python论文复现
  • 五、遗传算法的改进(预告)

今天是2023年的第一天,首先祝各位兄弟姐妹们新年快乐,上学的学习进步,上班的工作顺心!我老早就想做一期遗传算法的讲解,内容主要是我在22年9月份时做的,当时因为太忙了没来得及搞,现在把它大概整理如下,供各位兄弟姐妹们参考!

时间隔得比较久,如有不足请在评论区或者私信我指出。

本文的大纲如上所示,首先是简要介绍下遗传算法的概念和与其他优化算法的比较,重点是后面两个,必学知识和一篇相关的论文复现。因为全网的资料比较多,我在学习的时候也参考了大量的博客,我会在第三部分给出博客的链接,这样会帮大家节省很多查找时间。第四部分是重中之重,我会给出复现论文的一些方法和思路,并且它具有一定的通用性。

一、简介和相关概念

遗传算法简介

遗传算法是一种智能优化算法,同时它也是一种找全局最优解的随机搜索算法,它可以用来求最值和求参。从名字来看,遗传算法借用了生物学里达尔文的进化理论:”适者生存,不适者淘汰“,将该理论以算法的形式表现出来就是遗传算法的过程。

遗传算法由美国的Holland教授提出,它是一种利用自然选择和生物进化思想在搜索空间搜索最优解的随机搜索算法。遗传算法通过模拟自然选择中的繁殖、交叉、变异的操作来寻求优良个体,用适应度函数评价个体优劣,依据优胜劣汰的原则,搜索出适应度较高的个体,并在搜索中不断增加优良个体的数量,循环往复,直至搜索出适应性最高的个体。遗传算法采用一种启发式搜索的方式进行群体搜索,易于并行化处理,尤其近年来随着计算机应用的发展,遗传算法的卓越性能引起人们的关注,被广泛应用于各个领域,如:函数优化、组合优化、图像处理等。1

遗传算法是一种借鉴生物界自然选择和进化机制发展起来的高度并行、随机、自适应搜索算法。它使用群体搜索技术,将种群代表一组问题解,通过对当前种群施加选择、交叉和变异等一系列遗传操作,从而产生新一代种群,并逐步使种群进化到包含近似最优解的状态。2

第二种对遗传算法的解释来自于“一种基于遗传算法和高斯-牛顿法的合成算法在放射性药物生物动力学数据分析中的应用”这篇文章,这也是本文所要复现的文章,放在文章结尾的参考文献2处。

相关概念介绍

遗传算法存在一些与生物遗传学相对应的术语,这些术语在其他文章或博客中经常出现,为了便于理解,先将其给出如下:

①基因:在生物学中,它是一个基本的遗传单位,对应于解中每一个分量的特征,也称为特性、位。
②染色体:由基因构成的二进制串,是解的编码,也称为数组或位串。
③基因座:染色体中基因的位置。染色体中的基因所取的值称为等位基因。
④个体:是一个实体,并且带有染色体的特征,表示问题的可行解。而多个个体集合在一起称为种群,种群中所包含的个体数目称为种群的规模,表示解的个数。
⑤基因型:表示基因的组成,它与表现型的相关性是很强的。与基因型对应的是表现型,它是指染色体中基因型的外部表现,即生物个体所表现出来的性质状态,对应于遗传算法中的解空间。
⑥适应性:对应遗传算法中评价个体优劣的适应度函数,表示个体适应外部环境的能力。 ⑦选择:与遗传算法中的选择算子进行对应。
⑧交叉:基因重组产生一组新解的过程,对应遗传算法中的交叉算子。
⑨变异:基因突变的过程,即编码的某一个分量发生变化的过程,对应遗传算法中的变异算子。 ⑩进化过程:遗传算法的求解过程。1

我的补充:
11、适应度函数和目标函数:通常情况下,适应度函数是由目标函数进行相应变换得到的。算法中也是利用适应度函数的大小来进行个体的选择。
对于适应度函数的设计有以下三点要求:

  1. 非负性。遗传算法是根据个体适应度确定每个个体被保留下来的概率,而概率不能是负值。因此,我们要确保设计出的适应度函数大于或等于0;
  2. 选择性。遗传算法实现个体“优胜劣汰”的重要操作就是选择,适应度高的个体被保留,参与下一代进化,适应度低的个体则被淘汰。因此,适应度函数仅需要体现个体间相对水平位置,而不需要绝对水平位置。(通俗的讲,对所有个体的适应度同时加上或者减去一个数,不会改变个体的相对水平位置,自然也不会影响后续的选择操作。)
  3. 通用性。适应度函数的设计要满足通用性,使大家使用适应度时不必再修改适应度函数中的参数。
    对于适应度函数的设计有以下思路:
    第一种:

    第二种:
    来自于参考文献1的
    以上两种适应度函数的设计方法基本相同,加上(减去)最小值或者最小值都是为了使得函数非负;这种做法是可行的,因为它不会改变个体间的相对水平位置,仍然是适应度高的个体被保留了下来(举个例子,假设3个个体对应的适应度分别为-2、1、 3。现在我要选择适应度最大的个体,把他们分别减去最小值可得:0、3、5,此时我仍然选择的是第三个个体)。满足我以上说的那三条性质。

但是在我们编写程序的时候,为了保证程序的可运行性,我们通常会在后面加上一个很小的正数,通常是是le-3。

最大值适应度函数的代码如下:

def get_fitness(pop): 
    x,y = translateDNA(pop)
	pred = F(x, y)
	return (pred - np.min(pred)) + 1e-3 

最小值适应度函数的代码如下:

def get_fitness(pop): 
	x,y = translateDNA(pop)
	pred = F(x, y)
	return -(pred - np.max(pred)) + 1e-3

以上两段代码内容参考自 这篇博客。代码的实现思路与刚才我上面的分析是一致的,而且这位大兄弟写得很详细很好,强推这篇。

12、编码和解码:回想一下,我们能够进行数学运算的都是十进制的位数,比如说数乘和四则运算等等,这是一种规则。那么现在遗传算法也有自己的一套规则,就是交叉和变异,而这两项不能够直接对十进制的数进行操作,所以我们要进行编码和解码,编码就是为了进行交叉和变异的操作,解码就是把根据这一套规则计算出的结果,再转化为十进制,即是我们最后要求的结果。最常用的编码规则是二进制编码。本文也是以二进制的编码规则为例来介绍的遗传算法。

二、与其他智能优化算法的比较

下面介绍几种常见的智能优化算法:

蚁群算法

粒子群优化算法

人工神经网络算法

模拟退火算法

鱼群算法

1、蚁群算法 1992年,意大利学者Colorni A、Dorigo M、Maniezzo V提出了蚁群算法。在寻找食物源的过程中,蚁群总能找到一条最短路径(从蚁巢到食物源),蚁群算法主要是利用蚁群的这种寻优能力来解决一些离散系统优化中的困难问题。蚁群算法已经成功地应用于解决旅行商问题、调度问题等等,并且取得了很好的效果。但是,蚁群的研究刚刚起步,没有坚实的数学基础,算法在收敛性、理论依据等方面还需要进一步地研究。
2、粒子群优化算法 Kenney和 Eberhart在1995年提出了一种优化技术——粒子群优化算法。它主要基于对鸟类捕食行为的研究,通过对随机产生的一组初始解进行迭代,以此来寻求最优解。粒子群算法称优化问题的解为“粒子”,每个粒子都拥有一个决定粒子飞翔的方向和距离的速度及一个由被优化的函数决定的适应值,在解空间中,所有粒子都随着最优粒子的运动进行搜索。在迭代的过程中,粒子不断地更新自己,这主要通过对两个极值的追踪来进行,一个是全局极值,即整个种群到目前为止找到的最优解,另一个是个体极值,即粒子本身找到的最优解。当然,也可以只利用部分粒子,此时的极值叫局部极值。
3、人工神经网络算法
人工神经网络是目前比较热门的一个研究领域。人工神经网络是利用生物学中的神经网络原理建立起来的一种并行分布处理系统,是一个由很多多输入、单输出的神经元组成的网络,神经元的输入会有一些连接通道,他们分别对应一个连接权系数。人工神经网络通过不断地学习来确定它的权值,这个学习过程称为训练,其学习方式分为有导师的学习和无导师的学习两种。这两种方式的不同在于是不是有目标输出与输入相对应,并且对训练是不是进行了指导。有导师的学习有目标输出和输入,并对训练进行了指导,无导师的学习则相反。
4、模拟退火算法
1953年,Metropolis提出了模拟退火的思想,Kirkpatrick在1983年将退火思想引入了组合优化的领域,并取得了成功。模拟退火算法是局部搜索算法的扩展,所以理论上来说它是一种全局最优算法。模拟退火算法基于固体物质的退火原理。物质在高温的状态下进行无序的、强烈的热运动,故开始时,让固体有较高的温度,然后缓慢降温,直到物质慢慢变为平衡且有序的状态,最后则在能量最小时达到基态,这就是所说的退火。模拟退火算法在高温下通过采用Metropolis抽样准则来搜索最优解,这个过程是随机的,在这个过程中,它利用降温的过程进行重复抽样,进而搜索相近的最优值。模拟退火算法能有效寻找全局最优解,但是也存在一定的缺点,比如:收敛速度较慢等。
5、鱼群算法
2002年,李晓磊博士提出了鱼群算法,这是人工智能的又一个典型的应用,它通过模拟鱼群的觅食行动和生存活动进行全局寻优,即算法首先构造动物的简单的底层行为,然后对个体均进行局部寻优,逐步搜寻出全局最优值。鱼群算法搜索全局极值的能力较强,能防止局部极值的产生;对搜索空间而言有较强的自适应能力;收敛速度较快,并且在精度要求不高的情况下,能很快地得到可行解;对问题要求不严格,不需要精确地描述,也不需要严格的机理模型,这使得鱼群算法的应用范围得到了更大的扩展。目前,鱼群算法已经被广泛地应用于解决连续优化问题系统辨识问题、组合优化问题、神经网络训练和电力系统无功优化等问题,并且效果良好。

三、必学知识(站在前人的肩膀上)

我在学习遗传算法的时候同样在CSDN和其他网站上参考了大量博客,因为需要复现自己找的论文,所以需要搞懂它的原理。

我找文章花了很多时间,但真正有帮助的非常有限。大部分有用的文章都是在CSDN上找的,下面给出我的一些经验和链接,看透这几篇,再用程序复现一下,我相信兄弟姐妹们基本上都能理解,能够为大家节省大量的时间。(当然也有其他很好的博客,可能目前我还未找到)

  • 理解遗传算法的原理,搞懂它到底是怎么操作的,选择、交叉、变异到底是毛意思,看知乎上的这篇:遗传算法python(含例程代码与详解)。尤其是看这篇文章的第三部分:算法实例,搞懂怎么用遗传算法求x^2的极值后,我相信你已经入门了。
  • 搞懂遗传算法的代码,学会用python代码来求极值,看这篇:遗传算法详解 附python代码实现。我找了很多写遗传算法的代码的文章,但是我觉得都不如这篇文章清晰简洁。这位大兄弟用一个求函数极值的例子来讲解,代码的原理和逻辑非常清晰,能够成功复现,我的论文复现主要就是参考这篇代码,它的通用性很强。
  • 还有其他的一些博客,可以作为补充参考:
  • 遗传算法求解香蕉函数极大值
  • 遗传算法详解python代码实现以及实例分析
  • matlab选手看这篇:遗传算法小结及算法实例(附Matlab代码)
  • python遗传算法(详解)
  • 超参数寻优 遗传算法(GA) 超参数寻优的python实现
  • 上文说到的除了常见的二进制编码法等,还有诸如格雷码、浮点编码法、符号编码法等。想要了解其他的编码方式常用的选择算子看这篇:遗传算法(Genetic Algorithms)的全面讲解及python实现

虽然上面给出了很多参考博客,但是我最推荐的还是前两个,吃透前两个博客就搞懂了遗传算法的原理和代码逻辑,其他博客的可做补充说明来看。由于本文不讲遗传算法的基础知识,请兄弟姐妹们把这些知识补齐后再看本文,本文会给出在此基础上的,我对于遗传算法的理解。

四、python论文复现

复现的论文为参考文献1中的遗传算法部分,先将文章的部分内容截图如下:
第一个大头是目标函数,文章给出的目标函数为:
在这里插入图片描述
其中, t i t_{i} ti A i A_{i} Ai是已知的,如下图所示, t i t_{i} ti表示第i个测量时刻, A i A_{i} Ai表示第i个时刻所对应的器官内的血药浓度值:
在这里插入图片描述
文章给出了大鼠三个器官分别在第10、30、60、120、360分钟的血药浓度值。因此在上述的目标函数中,对于每一个器官,我都可以建立一个模型,m=5, t i t_{i} ti A i A_{i} Ai是已知的,此时只有 p 1 p_{1} p1 p 2 p_{2} p2 q 1 q_{1} q1 q 2 q_{2} q2是未知的,也是我们要求的四个参数,因此我们能够得到一个关于 p i p_{i} pi q i q_{i} qi四元函数表达式 ξ \xi ξ( p 1 p_{1} p1, p 2 p_{2} p2, q 1 q_{1} q1, q 2 q_{2} q2),目标就是求当 ξ \xi ξ达到最小时, p 1 p_{1} p1 p 2 p_{2} p2 q 1 q_{1} q1 q 2 q_{2} q2这四个参数值。

本文本质上是求四元函数的极小值,对5组数据需要分别代入再进行求和,因此目标函数可设为:

dic_liver = {0.167: 0.681, 0.5: 0.436, 1: 0.709, 2: 0.263, 6: 0.12}  # 键表示时间(h),值表示肝内的浓度
dic_lung = {0.167: 1.069, 0.5: 0.689, 1: 0.666, 2: 0.342, 6: 0.162}  # 表示肺内的浓度
dic_stomach = {0.167: 4.827, 0.5: 3.866, 1: 1.67, 2: 1.638, 6: 0.798}  # 表示胃内的浓度的


def F(p1, p2, q1, q2):  # 设计目标函数 法一
    fun = 0
    for key, value in dic_liver.items():
        fun = ((p1 * np.exp(-q1 * key) + p2 * np.exp(-q2 * key)) - value) ** 2 + fun
    return fun


def F2(p1, p2, q1, q2):  # 设计目标函数 法二
    l1 = list(dic_liver.keys())
    l2 = list(dic_liver.values())
    result = [((p1 * np.exp(-q1 * i) + p2 * np.exp(-q2 * i)) - j) ** 2 for i, j in zip(l1, l2)]
    # result = sum(result)
    total = 0
    for i in range(len(result)):
        total = total + result[i]
    return total

简要分析下:前三个字典是文章中给出的数据,描述了时间和对应血药浓度的关系,本文用字典类型,用键表示给药时间(文章以分钟为单位,这里把它都除以60标准化,以h为单位)。我在设计目标函数时用了两种方法,都要用到字典类型,但是方法一明显更简洁些。

适应度函数可设为(这里是求最小值对应的适应度函数):

def get_fitness(pop):
    p1, p2, q1, q2 = translateDNA(pop)
    pred = F(p1, p2, q1, q2)
    return -(pred - np.max(pred)) + 1e-3  # 要加上一个很小的正数

第二个大头就是编码和解码过程,通常来说,我们应首先随机生成十进制的初始种群,然后将十进制编码为二进制进行交叉、变异等操作,完成后再将二进制解码为十进制。但是实际上,第一步(随机生成十进制数)可以去掉,因为我们可以在程序中直接随机生成二进制串,因此编码过程变得很简单,这里只需要重点关注解码过程。以我复现的这篇文章为例:

在这里插入图片描述
在这里插入图片描述

一个参数对应的二进制长度为20位,本文有4个参数,那么对于这个二进制的初始种群来说,每一行就有(20*4)80位。而本文要求初始种群包含150个个体,实际上就是有150组解( p 1 p_{1} p1, p 2 p_{2} p2, q 1 q_{1} q1, q 2 q_{2} q2),则列数为150,行数为80,因此最终应该形成一个150*80的0-1矩阵。

注:如果看了上面推荐的两篇博客就知道,一个十进制的数可以根据“除2取余,逆序排列”转换为任意位数的二进制,只要在高位上补0就行了。当二进制的长度越长,计算越复杂,但精度也越高。

在这里插入图片描述
生成二进制初试种群代码如下:

pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE * 4))  
# matrix (POP_SIZE, DNA_SIZE) POP_SIZE为150,DNA_SIZE为20

解码过程如下:

def translateDNA(pop):  # 解码 pop表示种群矩阵,一行表示一个二进制编码表示的DNA,矩阵的行数为种群数目数为每个DNA长度*DNA个数
    p1_pop = pop[:, :20]
    p2_pop = pop[:, 20:40]
    q1_pop = pop[:, 40:60]
    q2_pop = pop[:, 60:]

    # 解码过程,并将pi和qi转换到规定的范围中去
    p1 = p1_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (p1_BOUND[1] - p1_BOUND[0]) + p1_BOUND[
        0]
    p2 = p2_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (p2_BOUND[1] - p2_BOUND[0]) + p2_BOUND[
        0]
    q1 = q1_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (q1_BOUND[1] - q1_BOUND[0]) + q1_BOUND[
        0]
    q2 = q2_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (q2_BOUND[1] - q2_BOUND[0]) + q2_BOUND[
        0]
    return p1, p2, q1, q2

两个大头根据自己的实际问题修改完后,剩下的基本可以套代码了。但是要注意一点,在遗传算法中,产生交叉和变异的位置都是随机的,比如我DNA序列的长度为80,则需要将产生随机数的范围修改到(0,80)
cross_points = np.random.randint(low=0, high=DNA_SIZE * 4)

其他的长度以此类推。

下面附上完整代码,相关的初试参数设置均来源于复现论文,再次感谢遗传算法详解 附python代码实现的大兄弟,为我的复现提供了很多参考:

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

warnings.filterwarnings('ignore')

DNA_SIZE = 20  # DNA长度(二进制编码长度)
POP_SIZE = 150  # 初始种群数量
CROSSOVER_RATE = 0.95  # 交叉率
MUTATION_RATE = 0.005  # 变异率 将0.005改为0.01
N_GENERATIONS = 1000  # 进化代数 进化代数在 800—1200 之间比较适合,本文选取进化1000代
p1_BOUND = [0, 1]  # 确定参数的范围
p2_BOUND = [0, 1]
q1_BOUND = [0, 1]
q2_BOUND = [0, 1]

dic_liver = {0.167: 0.681, 0.5: 0.436, 1: 0.709, 2: 0.263, 6: 0.12}  # 键表示时间(h),值表示肝内的浓度
dic_lung = {0.167: 1.069, 0.5: 0.689, 1: 0.666, 2: 0.342, 6: 0.162}  # 表示肺内的浓度
dic_stomach = {0.167: 4.827, 0.5: 3.866, 1: 1.67, 2: 1.638, 6: 0.798}  # 表示胃内的浓度的


def F(p1, p2, q1, q2):  # 设计目标函数 法一
    fun = 0
    for key, value in dic_liver.items():
        fun = ((p1 * np.exp(-q1 * key) + p2 * np.exp(-q2 * key)) - value) ** 2 + fun
    return fun


def F2(p1, p2, q1, q2):  # 设计目标函数 法二
    l1 = list(dic_liver.keys())
    l2 = list(dic_liver.values())
    result = [((p1 * np.exp(-q1 * i) + p2 * np.exp(-q2 * i)) - j) ** 2 for i, j in zip(l1, l2)]
    # result = sum(result)
    total = 0
    for i in range(len(result)):
        total = total + result[i]
    return total

# 求最小值对应的适应度函数 
def get_fitness(pop):
    p1, p2, q1, q2 = translateDNA(pop)
    pred = F(p1, p2, q1, q2)
    return -(pred - np.max(pred)) + 1e-3  # 要加上一个很小的正数

def translateDNA(pop):  # 解码 pop表示种群矩阵,一行表示一个二进制编码表示的DNA,矩阵的行数为种群数目 即行数为150行,列数为每个DNA长度*DNA个数,即20*4=80列(150*80)
    p1_pop = pop[:, :20]
    p2_pop = pop[:, 20:40]
    q1_pop = pop[:, 40:60]
    q2_pop = pop[:, 60:]

    p1 = p1_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (p1_BOUND[1] - p1_BOUND[0]) + p1_BOUND[
        0]
    p2 = p2_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (p2_BOUND[1] - p2_BOUND[0]) + p2_BOUND[
        0]
    q1 = q1_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (q1_BOUND[1] - q1_BOUND[0]) + q1_BOUND[
        0]
    q2 = q2_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (q2_BOUND[1] - q2_BOUND[0]) + q2_BOUND[
        0]
    return p1, p2, q1, q2

# 以下函数包含两个功能,交叉和变异
def crossover_and_mutation(pop, CROSSOVER_RATE=0.95):  # 单点交叉
    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 * 4)  # 随机产生交叉的点
            child[cross_points:] = mother[cross_points:]  # 孩子得到位于交叉点后的母亲的基因
        mutation(child)  # 每个后代有一定的机率发生变异
        new_pop.append(child)

    return new_pop


# 基本位变异算子
def mutation(child, MUTATION_RATE=0.005):
    if np.random.rand() < MUTATION_RATE:  # 以MUTATION_RATE的概率进行变异
        mutate_point = np.random.randint(0, DNA_SIZE * 4)  # 随机产生一个实数,代表要变异基因的位置
        child[mutate_point] = child[mutate_point] ^ 1  # 将变异点的二进制为反转(异或运算符 1与1为0、1与0为1、0与0为0)


def select(pop, fitness):  # 描述了从np.arange(POP_SIZE)里选择每一个元素的概率,概率越高约有可能被选中,最后返回被选中的个体即可
    idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,
                           p=(fitness) / (fitness.sum()))
    return pop[idx]

# # np.random.choice()函数的用法
# arr = ['pooh', 'rabbit', 'piglet', 'Christopher']
# np.random.choice(aa_milne_arr, size=11, p=[0.5, 0.1, 0.1, 0.3])


def print_info(pop):
    fitness = get_fitness(pop)
    min_fitness_index = np.argmin(fitness)  # 表示为array的最大值/最小值对应的索引
    print("min_fitness:", fitness[min_fitness_index])
    p1, p2, q1, q2 = translateDNA(pop)
    print("最优的基因型:", pop[min_fitness_index])
    print("(p1, p2, q1, q2):",
          (p1[min_fitness_index], p2[min_fitness_index], q1[min_fitness_index], q2[min_fitness_index]))

if __name__ == "__main__":

    pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE * 4))  # matrix (POP_SIZE, DNA_SIZE) POP_SIZE为150,DNA_SIZE为20
    for _ in range(N_GENERATIONS):  # 迭代N代
        pop = np.array(crossover_and_mutation(pop, CROSSOVER_RATE))  # 进行交叉和变异
        fitness = get_fitness(pop)
        pop = select(pop, fitness)  

    print_info(pop)

相对于原博客来说,对于相对较难理解的部分,我给代码加上了必要的注释,能够方便大家理解。

以肝器官为例,程序的运行结果,最优的基因型是p1,p2,q1,q2所对应的二进制位数,下面是把它们解码为十进制后的结果:
在这里插入图片描述
虽然跟原文的结果有一定出入,但是第一步,已经能够跑出来了。之后我们再对其进行修改完善。

交叉算法有单点交叉、多点交叉、均匀交叉等,本文用的方法时多点交叉。同理,变异的操作也不只这一种,本文所用的交叉、变异都是最常用的操作。

五、遗传算法的改进(预告)

要想使得程序获得更快更好的效果,我们需要对其进行改进。各位兄弟姐妹们先把本文搞懂,下篇文章我会综合找到的一些文献来介绍对遗传算法的改进和它的python程序实现。对遗传算法的改进主要有以下三部分:

  • 适应度函数的改进
  • 交叉概率的改进
  • 变异概率的改进

不知道代码是否容易理解,若需要复现论文的讲解,可以在评论区留言或者私信我,如果人数多的话,可以做一期讲解视频,希望对各位兄弟姐妹们有所帮助!


  1. 李延梅. 一种改进的遗传算法及应用[D].华南理工大学,2012. ↩︎ ↩︎

  2. 孙亮,李君利,程建平.一种基于遗传算法和高斯-牛顿法的合成算法在放射性药物生物动力学数据分析中的应用[J].核技术,2006(12):927-931.
    遗传算法python(含例程代码与详解)
    遗传算法详解 附python代码实现
    遗传算法求解香蕉函数极大值
    遗传算法详解python代码实现以及实例分析
    遗传算法小结及算法实例(附Matlab代码)
    python遗传算法(详解)
    超参数寻优 遗传算法(GA) 超参数寻优的python实现
    遗传算法(Genetic Algorithms)的全面讲解及python实现 ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/133433.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

1214. 波动数列(推公式 + DP)

题目如下&#xff1a; 思路 or 题解&#xff1a; 我们可以设&#xff1a; 第一个数为 xxx d {a, -b} 那后续的数为&#xff1a;xd1x d_1xd1​ , xd1d)2x d_1 d_)2xd1​d)​2 … … xd1d2......dn−1x d_1 d_2 ... ... d_{n - 1}xd1​d2​......dn−1​ 根据题意和上面…

(Java高级教程)第二章Java多线程常见面试题-第三节:线程安全集合类和死锁

文章目录一&#xff1a;线程安全集合类&#xff08;1&#xff09;多线程环境下使用ArrayList&#xff08;2&#xff09;多线程环境使用队列&#xff08;3&#xff09;多线程使用哈希表二&#xff1a;死锁&#xff08;1&#xff09;概念&#xff08;2&#xff09;死锁产生的四个…

(小程序)会议OA项目-首页

目录 一、FIex布局简介 1.什么是flex布局&#xff1f; 2.flex属性 学习地址&#xff1a; 3.flex弹性布局演示 ① 容器的属性 ⑴ flex-direction属性 ⑵ flex-wrap属性 ⑶ flex-flow ⑷ justify-content属性 ⑸ align-items属性 ⑹ align-content属性 二、轮播图组件m…

微信记录怎么恢复?恢复已删除微信历史记录的4种方式

恢复已删除微信历史记录的4种方式 如何在有/没有备份的情况下在 iPhone 和 Android 上恢复旧的或已删除的微信历史记录&#xff0c;如聊天对话、语音消息、照片、图片和视频剪辑&#xff1f;参考本指南&#xff0c;祝您成功恢复微信数据。 关于微信数据恢复 “说真的&#xf…

容器化技术Docker与任务编排

Docker容器化 Docker简介 传统的Java项目部署需要自己进行打包&#xff0c;redis&#xff0c;nignx等中间件需要安装以及进行很多配置&#xff0c;稍微繁琐&#xff0c;而Docker使用了容器化的技术把这一过程封装为一条指令解决&#xff0c;而这取决于它的架构设计&#xff0c…

数值优化之函数高阶信息

本文ppt来自深蓝学院《机器人中的数值优化》 目录 1 函数高阶信息的介绍 2 函数高阶信息的计算 1 函数高阶信息的介绍 hessian矩阵是对称矩阵&#xff0c;最后一个公式是函数关于0的泰勒展开 负梯度是函数下降的最快方向 注意区分Hessian矩阵与Jacobian矩阵&#xff0c;Hess…

9_4、Java基本语法之System、Math、BigInteger与BigDecimal类的使用

一、System类的使用 1、System类代表系统&#xff0c;系统级的很多属性和控制方法都放置在该类的内部。 该类位于java.lang包。 由于该类的构造器是private的&#xff0c;所以无法创建该类的对象&#xff0c;也就是无法实 例化该类。其内部的成员变量和成员方法都是static的&am…

【算法题解】7. 反转链表

文章目录题目解法一&#xff1a;迭代解题思路代码实现复杂度分析解法二&#xff1a;递归解题思路代码实现复杂度分析题目 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。来自&#xff1a;leetcode 解法一&#xff1a;迭代 解题思路 使用…

广州车展智能卷王:集度ROBO-01的取与舍

作者 | 德新 编辑 | 王博2022年的最后两天&#xff0c;今年最后一个A级车展在广州开幕。由于种种原因&#xff0c;不少车企匆忙备战或者干脆缺席&#xff0c;这届展会不如往届热闹。但也有憋了大招的选手&#xff0c;比如集度。 12月30日上午&#xff0c;集度在一场非常短的发布…

11-内部类and 12-集合初步

文章目录11-内部类链接外部类使用.this 和.new匿名内部类12-集合初步思考1&#xff0c;List<Apple> apples new ArrayList<>();思考2&#xff0c;如何初始化一个collection思考3&#xff0c;Pet关键字思考4&#xff0c;关于ListIterator这个双向迭代器思考5&#…

保护性暂停设计模式

目录 保护性暂停设计模式 获取结果 产生结果 总代码实现 测试 增加超时效果的Guarded suspension get(long timeout) 测试 保护性暂停设计模式 Guarded Suspension 即 保护性暂停; 是一种等待唤醒机制的一种规范 ,也可以理解为使用中设计模式,Java的API很多都按照保护性…

【免费开放源码】审批类小程序项目实战(活动审批端)

第一节&#xff1a;什么构成了微信小程序、创建一个自己的小程序 第二节&#xff1a;微信开发者工具使用教程 第三节&#xff1a;深入了解并掌握小程序核心组件 第四节&#xff1a;初始化云函数和数据库 第五节&#xff1a;云数据库的增删改查 第六节&#xff1a;项目大纲以及制…

系分 - 面向对象的方法【概念】

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 系分 - &#xff08;概念&#xff09;面向对象的方法 面向对象的方法&#xff08;OO&#xff0c;Object-Oriented&#xff09;是一种基于对象模型的程序设计方法&#xff0c;包括面向对象的分析&#xff08;OOA&a…

【iOS】内存管理

文章目录前言理解引用计数引用计数原理属性存取方法中的内存管理自动释放池保留环以ARC简化引用计数使用ARC时必须遵守的命名规则变量的内存管理语义ARC如何清理实例变量覆写内存管理的方法在dealloc方法中只释放应用并解除监听前言 内存管理&#xff1a; 在Objective-C这样的…

Windows平台下的内存泄漏检测

Windows平台下的内存泄漏检测一、使用_CrtDumpMemoryLeaks定位内存泄露添加对应的头文件转储内存泄漏信息程序任意点退出指定调试信息输出二、定位具体内存泄露位置内存快照转储内存快照比较内存快照完整例子三、使用WinDbg定位获取堆信息查看指定堆的使用情况获取地址信息获取…

【Docker】初级篇

【Docker】初级篇&#xff08;一&#xff09;Docker简介【1】docker是什么【2】容器与虚拟机比较【3】能干嘛【4】去哪下&#xff08;二&#xff09;Docker安装【1】前提说明【2】Docker的基本组成【3】安装步骤&#xff08;1&#xff09;确定是CentOS7及以上版本&#xff08;2…

抽烟打电话行为识别系统 yolo

抽烟打电话行为识别系统通过yolo深度学习框架模型&#xff0c;对现场画面区域进行7*24小时实时监测&#xff0c;发现抽烟打电话等违规行为立即抓拍存档预警。YOLOv5是一种单阶段目标检测算法&#xff0c;该算法在YOLOv4的基础上添加了一些新的改进思路&#xff0c;使其速度与精…

【 shell 编程 】第4篇 数组和函数

数组和函数 文章目录数组和函数一、数组1.普通数组2.关联数组3.数组和循环二、函数1.定义函数2.调用函数一、数组 变量&#xff1a;用一个固定的字符串&#xff0c;代替一个不固定字符串。 数组&#xff1a;用一个固定的字符串&#xff0c;代替多个不固定字符串。 1.普通数组…

Python代码实现学生管理系统

Python代码实现学生管理系统 需求说明 实现一个命令行版本的学生管理系统 功能: 新增学生 显示学生 查找学生 删除学生 存档到文件 创建入口函数 使用一个全局列表 students 表示所有学生信息. 使用 menu 函数和用户交互. 这是一个自定义函数. 使用 insert , show ,…

MacOS Ventura安装失败的原因及解决方法分享

2022年10月&#xff0c;苹果公司向Mac电脑用户推送了MacOS Ventura正式版更新&#xff0c;此次更新为MacOS带来了台前调度、连续互通相机、iMessage 撤回、编辑等功能。吸引众多Mac电脑用户不由纷纷下载安装&#xff0c;但各用户在安装的过程中经常遇到更新MacOS Ventura时突然…