遗传算法理解与代码实战(一)- demo(python手写代码)

遗传算法(Genetic Algorithm, GA)是模拟自然界中生物进化的机制来搜索最优解的方法。遗传算法属于进化计算的一部分,它借鉴了达尔文的自然选择和孟德尔的遗传学原理。

1、算法背景

遗传算法的灵感来源于生物进化过程。在自然界中,生物通过基因的遗传和变异,以及环境的自然选择,逐步演化出适应环境的特征。遗传算法模拟这一过程,通过编码表示问题解的“染色体”,并利用选择、交叉(重组)、变异等操作来模拟生物进化过程,从而搜索问题的最优解。

2、算法原理

遗传算法通常包含以下几个基本步骤:

  1. 初始化:随机生成一组候选解(个体),这些解通常以二进制字符串(染色体)的形式表示。
  2. 评估:使用适应度函数评估每个个体的适应度,即解的好坏。
  3. 选择:根据适应度,从当前群体中选择优良的个体作为父本,用于产生下一代。
  4. 交叉(Crossover):随机选择交叉点,将父本的染色体交换部分基因,产生新的个体(后代)。
  5. 变异:以一定的概率随机改变个体的某些基因,引入新的遗传多样性。
  6. 迭代:用新生成的后代替换掉当前群体中的一部分或全部个体,形成新的群体。
  7. 终止:当达到一定的迭代次数或找到满足条件的解时,算法终止。

3、算法优势

遗传算法具有以下优势:

  • 全局搜索能力:通过模拟自然选择和遗传,遗传算法能够在整个搜索空间中进行全局搜索,减少陷入局部最优解的风险。
  • 鲁棒性:遗传算法对问题的初始条件要求不高,且在迭代过程中不易受到噪声等不利因素的影响。
  • 并行性:遗传算法的多个个体并行搜索,易于实现并行处理,提高搜索效率。
  • 灵活性:遗传算法不依赖于具体问题的数学模型,适应性强,适用于多种类型的问题。

4、应用场景

遗传算法广泛应用于各种优化和搜索问题,如工程优化、机器学习、经济调度、自动控制等领域。

遗传算法作为一种有效的全局搜索算法,通过模拟生物进化过程,能够在复杂问题中找到满意的解。它以其独特的搜索机制和优点,在多个领域得到了广泛的应用。然而,遗传算法也有其局限性,如算法参数的选择、计算量较大等问题,需要在实际应用中加以考虑和优化。

5、简单实例

我们哪一个非常简单的优化为例讲一下:
目标函数: y = x 2 y = x^2 y=x2
我们在一组数据中找到可以使得目标函数达到最大的值,x的取值范围是[0,31]

5.1 名词解释说明
  • 种群(Population)
    遗传算法保持大量的个体(individuals)——针对当前问题的候选解集合。由于每个个体都由染色体表示,因此这些种族的个体(individuals)可以看作是染色体集合。比如我们在这里x的取值范围是[0,31],我们随机取5个数字[2,5,11,24,7]作为一个种群,其中每一个数字就是个体;

  • 基因(Genotype)
    在自然界中,通过基因型表征繁殖,繁殖和突变,基因型是组成染色体的一组基因的集合。在遗传算法中,每个个体都由代表基因集合的染色体构成。例如,一条染色体可以表示为二进制串,其中每个位代表一个基因。比如我们上面随机取5个数字[2,5,11,24,7]作为一个种群,每一个数字就是一个个体,每个数字用二进制表示,比如数字2的二进制表示是:00010,一共有5个基因,

    我们这里x的取值范围是[0,31],也就是为了将基因的最大个数限制在5个,因为5个基因最大数值是:11111,转换为十进制就是31 。当然其实自己可是设置更大的基因个数,这里只是用来演示简单的过程。

  • 适应度函数(Fitness function)
    在算法的每次迭代中,使用适应度函数(也称为目标函数)对个体进行评估。目标函数是用于优化的函数或试图解决的问题。适应度得分更高【适应度可以根据目标函数的需求来设定是取大还是取小】的个体代表了更好的解,其更有可能被选择繁殖并且其性状会在下一代中得到表现。比如我们这里的适应度函数就是个体数值的平方,随着遗传算法的进行,解的质量会提高,适应度会增加,一旦找到具有令人满意的适应度值的解,终止遗传算法。

  • 选择(Selection)
    在计算出种群中每个个体的适应度后,使用选择过程来确定种群中的哪个个体将用于繁殖并产生下一代,具有较高值的个体更有可能被选中,并将其遗传物质传递给下一代。仍然有机会选择低适应度值的个体,但概率较低。这样,就不会完全摒弃其遗传物质。

  • 交叉(Crossover)
    为了创建一对新个体,通常将从当前代中选择的双亲样本的部分染色体互换(交叉),以创建代表后代的两个新染色体。此操作称为交叉或重组。
    在这里插入图片描述

  • 突变(Mutation)
    突变操作的目的是定期随机更新种群,将新模式引入染色体,并鼓励在解空间的未知区域中进行搜索。
    突变可能表现为基因的随机变化。变异是通过随机改变一个或多个染色体值来实现的。例如,翻转二进制串中的一位。
    在这里插入图片描述

5.2 代码参数设置
# 参数设置
CHROMOSOME_SIZE = 5  # 染色体长度
POPULATION_SIZE = 10  # 种群大小
CROSSOVER_RATE = 0.7  # 交叉率,就是有70%的概率会交叉,30%直接返回原始的基因
MUTATION_RATE = 0.01  # 变异率
GENERATIONS = 100  # 迭代次数

# 初始化种群
population = np.random.randint(2, size=(POPULATION_SIZE, CHROMOSOME_SIZE))

array([[1, 0, 0, 1, 0],
       [0, 1, 0, 0, 1],
       [0, 1, 1, 0, 1],
       [0, 0, 1, 1, 0],
       [1, 0, 0, 1, 0],
       [1, 0, 1, 1, 0],
       [1, 0, 0, 1, 1],
       [0, 1, 1, 0, 0],
       [1, 1, 0, 1, 1],
       [1, 0, 0, 0, 1]])

我们可以看到这里有10条数据,就是个体数为10 的种群。

5.3 定义适应度、选择、交叉和变异函数
# 适应度函数,计算个体的适应度
def fitness(chromosome):
    x = int(''.join(str(gene) for gene in chromosome), 2)#转换成十进制
    return x ** 2

# 选择函数,基于适应度进行选择
def select(population):
    fitness_values = np.array([fitness(individual) for individual in population])
    return population[np.random.choice(range(POPULATION_SIZE), size=POPULATION_SIZE, replace=True, p=fitness_values / fitness_values.sum())]

# 交叉函数,随机选择交叉点,进行基因交换
def crossover(parent1, parent2):
    if np.random.rand() < CROSSOVER_RATE:
        point = np.random.randint(1, CHROMOSOME_SIZE - 1)
        return np.concatenate((parent1[:point], parent2[point:])), np.concatenate((parent2[:point], parent1[point:]))
    else:
        return parent1, parent2

# 变异函数,随机翻转基因
def mutate(chromosome):
    for i in range(CHROMOSOME_SIZE):
        if np.random.rand() < MUTATION_RATE:
            chromosome[i] = 1 - chromosome[i]
    return chromosome


  • 选择函数解析:

    • population: 这是一个参数,代表当前种群的个体集合。在遗传算法中,种群是所有可能的解决方案的集合。
    • fitness_values = np.array([fitness(individual) for individual in population]):
      这里使用列表推导式来计算种群中每个个体的适应度值。适应度函数fitness(individual)是一个用户定义的函数,用于评估个体individual的适应度,即它在解决问题上的好坏。
      np.array将计算出的适应度值列表转换为NumPy数组,以便进行下一步的计算。
    • population[np.random.choice(range(POPULATION_SIZE), size=POPULATION_SIZE, replace=True, p=fitness_values / fitness_values.sum())]:
      • np.random.choice: 这是一个NumPy函数,用于根据给定的概率随机抽取样本。
        range(POPULATION_SIZE): 创建一个从0到种群大小-1的序列,用于np.random.choice作为抽样范围的参数。
      • size=POPULATION_SIZE: 指定抽样的数量等于种群的大小,这意味着我们将从当前种群中选择相同数量的个体形成新的种群。
      • replace=True: 允许重复抽样,即同一个个体可以被多次选中进入新的种群。
      • p=fitness_values / fitness_values.sum(): 指定每个个体被选中的概率,这个概率与个体的适应度值成正比。这里首先将所有个体的适应度值相加得到总和,然后将每个个体的适应度值除以这个总和,得到归一化的概率。这样,适应度更高的个体有更大的概率被选中。
  • 交叉函数

    • parent1 和 parent2: 这两个参数代表选择用于交叉操作的两个父代个体。
    • if np.random.rand() < CROSSOVER_RATE::
      • np.random.rand(): 生成一个0到1之间的随机数。
      • CROSSOVER_RATE: 这是一个预设的交叉概率,用于决定是否进行交叉操作。如果生成的随机数小于CROSSOVER_RATE,则进行交叉;否则,不进行交叉,直接返回父代个体。
        point = np.random.randint(1, CHROMOSOME_SIZE - 1):
      • np.random.randint: 用于生成一个指定范围内的随机整数。
        1, CHROMOSOME_SIZE - 1: 指定交叉点的生成范围。交叉点不能在染色体的起始或结束位置,因此范围是从1到CHROMOSOME_SIZE - 1(CHROMOSOME_SIZE是染色体的长度)。
    • return np.concatenate((parent1[:point], parent2[point:])), np.concatenate((parent2[:point], parent1[point:])):
      • np.concatenate: 用于连接两个或多个数组。
        这行代码创建了两对新的子代个体。第一对是将parent1的前半部分与parent2的后半部分连接起来,第二对是将parent2的前半部分与parent1的后半部分连接起来。这样,每个子代都会从两个父代那里继承一部分基因。
    • else: return parent1, parent2:
      如果没有进行交叉(即随机数大于等于CROSSOVER_RATE),则直接返回父代个体,不做任何改变。
  • 变异解析

    • chromosome: 这个参数代表需要进行变异的个体(染色体)。在遗传算法中,个体通常表示为二进制数组,其中的每个元素称为基因。
    • for i in range(CHROMOSOME_SIZE)::
      这行代码遍历染色体中的每个基因。
      • if np.random.rand() < MUTATION_RATE::
      • np.random.rand(): 生成一个0到1之间的随机数。
        MUTATION_RATE: 这是一个预设的变异概率,用于决定是否对当前基因进行变异。如果生成的随机数小于MUTATION_RATE,则进行变异;否则,不进行变异。
      • chromosome[i] = 1 - chromosome[i]:
        如果决定对当前基因进行变异,则将基因的值取反。在二进制表示中,这意味着将0变为1,将1变为0。
    • return chromosome:
      在遍历完所有基因并可能进行了一些变异后,返回变异后的染色体。
5.4 主函数–遗传算法迭代
# 用于存储每代的最佳适应度,用于后续的可视化
best_fitness_over_generations = []

# 遗传算法主循环
for generation in range(GENERATIONS):
    selected_population = select(population)
    next_generation = []
    for i in range(0, POPULATION_SIZE, 2):
        parent1, parent2 = selected_population[i], selected_population[i + 1]
        offspring1, offspring2 = crossover(parent1, parent2)
        offspring1 = mutate(offspring1)
        offspring2 = mutate(offspring2)
        next_generation.append(offspring1)
        next_generation.append(offspring2)
    population = np.array(next_generation)
    # 更新最佳适应度
    best_fitness = max(fitness(individual) for individual in population)
    best_fitness_over_generations.append(best_fitness)
    print(f'Generation {generation + 1}, Best Fitness: {best_fitness}')
  1. best_fitness_over_generations = []:
    • 这是一个空列表,用于存储每一代种群的最高适应度值,以便后续可以进行可视化分析。
  2. for generation in range(GENERATIONS)::
    • 这行代码开始遗传算法的主循环,GENERATIONS是预设的迭代次数,即算法将运行的代数。
  3. selected_population = select(population):
    • 调用之前定义的选择函数select,根据个体的适应度比例从当前种群中选择出新的种群。
  4. next_generation = []:
    • 初始化一个空列表,用于存储新一代的个体。
  5. for i in range(0, POPULATION_SIZE, 2)::
    • 这个循环遍历选择的种群,每次取出两个个体作为父母。(这里是每次间隔2取一次数,比如第一次取0,那么在下面代码去的是i和i+1,也就是取了0和1两个数,下一次取2 ,那么就是取了2和3两个数)
  6. parent1, parent2 = selected_population[i], selected_population[i + 1]:
    • 从选择的种群中获取两个父母个体。
  7. offspring1, offspring2 = crossover(parent1, parent2):
    • 调用之前定义的交叉函数crossover,对两个父母个体进行交叉操作,生成两个子代个体。
  8. offspring1 = mutate(offspring1)offspring2 = mutate(offspring2):
    • 调用之前定义的变异函数mutate,对两个子代个体进行变异操作。
  9. next_generation.append(offspring1)next_generation.append(offspring2):
    • 将变异后的子代个体添加到新一代种群列表中。
  10. population = np.array(next_generation):
    • 将新一代种群列表转换为NumPy数组,更新当前种群。
  11. best_fitness = max(fitness(individual) for individual in population):
    • 计算当前种群中个体的最高适应度值。
  12. best_fitness_over_generations.append(best_fitness):
    • 将当前种群的最佳适应度添加到best_fitness_over_generations列表中,用于后续分析。
  13. print(f'Generation {generation + 1}, Best Fitness: {best_fitness}'):
    • 打印当前代数和最佳适应度,以便监控算法的进度。
Generation 1, Best Fitness: 729
Generation 2, Best Fitness: 729
Generation 3, Best Fitness: 841
Generation 4, Best Fitness: 729
Generation 5, Best Fitness: 729
Generation 6, Best Fitness: 961
Generation 7, Best Fitness: 961
Generation 8, Best Fitness: 961
……

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

string 的模拟实现

string 的相关介绍&#xff1a;C&#xff1a;string相关内容的简单介绍-CSDN博客 成员变量&#xff1a; private:char* _strsize_t _sizesize_t _capacity 构造函数 string类的构造函数不仅需要完成空间的开辟&#xff0c;还需要再开辟的过程中完成字符串的拷贝&#xff0c;它…

ThreadLocal在实际开发中如何使用?

在实际开发中&#xff0c;ThreadLocal 是一个非常有用的工具&#xff0c;用于解决多线程环境下数据隔离和线程上下文数据的问题。以下是一个关于 ThreadLocal 在实际开发中使用的详细讲解&#xff0c;包括其工作原理、应用场景和实战例子。 1. 工作原理 ThreadLocal 类…

Mybatis从入门到CRUD到分页到日志到Lombok到动态SQL再到缓存

Mybatis 入门 1.导入maven依赖 <dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>x.x.x</version> </dependency>2.配置核心文件 <?xml version"1.0" encoding"U…

FISCO BCOS区块链平台上的智能合约压力测试指南

引言 在当今的分布式系统中&#xff0c;区块链技术因其去中心化、安全性和透明性而备受关注。随着区块链应用的不断扩展&#xff0c;对其性能和稳定性的要求也越来越高。因此&#xff0c;对区块链网络进行压力测试显得尤为重要。 目录 引言 1. 配置FISCO BCOS节点 2. 安装和…

Linux内核源码分析(强烈推荐收藏!)

一&#xff0c;前言 Linux内核是一个操作系统&#xff08;OS&#xff09;内核&#xff0c;本质上定义为类Unix。它用于不同的操作系统&#xff0c;主要是以不同的Linux发行版的形式。Linux内核是第一个真正完整且突出的免费和开源软件示例。Linux 内核是第一个真正完整且突出的…

Mysql - is marked as crashed and should be repaired

概述 上周发生了一个Mysql报错的问题&#xff0c;今天有时间整理一下产生的原因和来龙去脉&#xff0c;Mysql的版本是5.5,发生错误的表存储引擎都是MyISAM,产生的报错信息是Table xxxxxx is marked as crashed and should be repaired。 定位问题 产生的后果是Nginx服务没有…

MT6771 android13 自定义背光曲线

一. Android系统源码中的参数配置 MTK6771平台自己重写了背光曲线的参数&#xff0c;路径在s0_vnd/vendor/mediatek/proprietary/packages/overlay/vendor/FrameworkResOverlayExt/brightness_adaptive_support/res/values/config.xml 不过MTK的其他平台可能不是在这个路径 来看…

Linux Ubuntu部署SVN服务端结合内网穿透实现客户端公网访问

文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…

WordPress建站入门教程:如何上传安装WordPress主题?

我们成功搭建WordPress网站后&#xff0c;默认使用的是自带的最新主题&#xff0c;但是这个是国外主题&#xff0c;可能会引用一些国外的资源文件&#xff0c;所以为了让我们的WordPress网站访问速度更快&#xff0c;强烈建议大家使用国产优秀的WordPress主题。 今天boke112百…

javascript中字符串处理,常用的方法汇总

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;前端泛海 景天的主页&#xff1a;景天科技苑 文章目录 字符串对象的的相关方法1.获取字符串长度 length2.通过索引获取元素 …

让娃学习效率更高的“可视化”时间管理器

如果要问&#xff0c;老母亲在娃开学后&#xff0c;蕞着急孩子哪一种坏习惯&#xff0c;那时间管理肯定榜上有名&#xff01; 做作业的时候&#xff0c;才写了5分钟&#xff0c;已经没有耐心了&#xff0c;东摸摸西看看&#xff0c;一会说肚子疼想上厕所&#xff0c;一会又拿出…

Linux中线程的实现,线程的接口相关函数pthread_create、pthread_join、pthread_exit

目录 一.线程的概念 二.操作系统中线程的实现 三.Linux中线程的实现 四.进程与线程的区别 五.线程的接口相关函数 5.1 pthread_create 5.2 pthread_join 5.3 pthread_exit 六.代码演示 七.如何解决上述问题&#xff1f; 方案1. 方案2. 方案3. 一.线程的概念 进程是…

【数据结构】矩阵的压缩存储

矩阵的压缩存储 5.1 普通矩阵的存储 用二维数组存储 分为行优先和列优先&#xff1a; 行优先&#xff1a;优先存放一行的数据。 列优先&#xff1a;优先存放一列的数据。 注意下标是从0还是1开始的&#xff01; 5.2 对称矩阵的存储 对称矩阵定义 若n阶方阵中任意一个元素 a i …

Allure小白下载安装

1、下载官网地址&#xff1a;https://github.com/allure-framework/allure2/releases 2、下载安装包后需要解压到一个非中文名称路径下 3、配置环境变量 D:\Allure\allure-2.27.0\bin 我的电脑右键选择属性&#xff0c;高级系统设置&#xff0c;环境变量 4、CMD查看安装all…

Java | Java的输入与输出

文章目录 Java输出1、System.out.println()2、System.out.printf()3、System.out.print() Java输入1、使用Scanner类的对象获取输入&#xff08;1&#xff09;一般类型输入&#xff08;2&#xff09;字符串类型输入&#xff08;3&#xff09;char类型输入 2、使用System.in.rea…

挑战杯 基于深度学习的目标检测算法

文章目录 1 简介2 目标检测概念3 目标分类、定位、检测示例4 传统目标检测5 两类目标检测算法5.1 相关研究5.1.1 选择性搜索5.1.2 OverFeat 5.2 基于区域提名的方法5.2.1 R-CNN5.2.2 SPP-net5.2.3 Fast R-CNN 5.3 端到端的方法YOLOSSD 6 人体检测结果7 最后 1 简介 &#x1f5…

Android APP性能指标(二)

文章目录 一、响应时间1.1 数据获取1.2 响应时间指标测试点1.3 启动速度测试点1.4 响应时间测试解决方法 二、流量2.1 数据获取2.2 流量测试关注点2.3 测试标准 三、电量3.1 连接手机3.2 数据获取3.3 获取APP的UID3.3 重置电池数据收集数据3.4 电量指标测试 四、温度五、性能测…

Pyaudio的安装以及报错解决

Pyaudio是一个可以用麦克风录入声音的库&#xff0c;但我在安装时发现无论是在cmd中pip安装还是在Pycharm中安装&#xff0c;都会报一堆错误。因此写一篇我最终的解决方案&#xff0c;我的解决办法是采用离线安装的方式&#xff0c;安装pyaudio库。 一.下载离线安装包 离线安…

超详细——动态内存分配+柔性数组

☃️个人主页&#xff1a;fighting小泽 &#x1f338;作者简介&#xff1a;目前正在学习C语言和数据结构 &#x1f33c;博客专栏&#xff1a;C语言学习 &#x1f3f5;️欢迎关注&#xff1a;评论&#x1f44a;&#x1f3fb;点赞&#x1f44d;&#x1f3fb;留言&#x1f4aa;&am…

意外之失:不小心删除的文件如何寻回?

一、瞬间消失的珍贵记忆 在我们的日常电脑使用中&#xff0c;总有一些时刻让人心惊胆战——那就是不小心删除了重要的文件。或许是一个珍藏多年的照片集&#xff0c;或许是一个即将完成的项目文档&#xff0c;这些文件承载着我们的回忆、努力和成果&#xff0c;却在一次疏忽之…