遗传算法原理详细讲解(算法+Python源码)

简介: 遗传算法原理详细讲解(算法+Python源码)

一、遗传算法

遗传算法的概念最早由约翰·霍兰德(John Holland)在20世纪60年代提出。霍兰德是一位美国的电气工程师和计算机科学家,他对生物学中的自然选择和遗传机制产生了浓厚兴趣,并试图将这些生物学原理应用于解决优化和搜索问题。霍兰德的观点: 约翰·霍兰德在20世纪60年代初期,通过研究自然选择和遗传机制,提出了一种新颖的优化算法的思想。他认为,自然选择的过程中,通过基因的遗传和变异,使得物种逐渐适应环境,并找到更好的生存策略。他意识到这一思想可以应用于解决工程和计算问题。 霍兰德于1975年出版了一本名为《自然和人工系统中的适应性》(Adaptation in Natural and Artificial Systems)的书,其中详细介绍了他的遗传算法理论。这本书被认为是遗传算法的奠基之作。

基本原理: 遗传算法的基本原理是通过模拟自然选择和遗传机制进行优化。个体(解决方案)被编码为基因型,通过遗传操作(交叉和变异)来生成新的个体。适应度函数用于评估个体的优劣,更适应环境的个体有更高的概率被选中进入下一代。

演化过程: 遗传算法的演化过程模拟了生物进化的过程,包括选择、交叉和变异。适应度高的个体更有可能被选中,同时,基因交叉和变异引入了新的基因组合,增加了搜索空间的多样性。

应用: 随着计算机科学和人工智能的发展,遗传算法被应用于解决各种优化问题,如组合优化、函数优化、机器学习等。它在搜索空间庞大、复杂问题中的全局搜索能力使其受到了广泛关注。

二、常见的遗传算法变体

  1. 标准遗传算法(Standard Genetic Algorithm, SGA):
    主要特点是通用,适用于各种优化问题。
  2. 进化策略(Evolutionary Strategies, ES):
    优点: 强调个体的变异,通过适应性进化来探索搜索空间。在高维、大规模问题中表现较好。
  3. 遗传规划算法(Genetic Programming, GP):
    优点: 用于自动发现计算机程序或表达式,适用于符号回归、符号分类等问题。能够发现复杂的解决方案。
  4. 遗传局部搜索算法(Genetic Local Search, GLS):
    优点: 结合了遗传算法和局部搜索的优点,通过遗传算法进行全局搜索,然后通过局部搜索进行精细调整。在解空间的不同区域都能有效搜索。
  5. 多目标遗传算法(Multi-Objective Genetic Algorithm, MOGA):
    优点: 用于处理多目标优化问题,同时优化多个目标函数。通过 Pareto 最优前沿来表示和保留多个解决方案。
  6. 协同演化(Co-Evolution):
  • 优点: 多种群体之间进行协同演化,每个群体演化出一部分解决方案。适用于解决复杂问题,能够处理多个相互影响的子问题。
  1. 遗传局部优化算法(Genetic Local Optimization, GLO):
    优点: 结合了全局搜索和局部优化的特点,利用全局搜索来探索解空间,再通过局部优化进行细致调整。
  2. 差分进化算法(Differential Evolution, DE):
    优点: 强调差分操作,通过对个体之间的差异进行搜索。在全局优化问题中表现良好,对参数敏感性较小。
  3. 自适应遗传算法(Adaptive Genetic Algorithm):
    优点: 能够动态调整算法参数,适应问题的特性。在问题变化较大或参数选择困难时具有较好的适应性。
  4. 混合遗传算法(Hybrid Genetic Algorithm):
    优点: 结合遗传算法与其他优化方法,如局部搜索、模拟退火等。在不同问题场景中综合利用不同算法的优势。

三、遗传算法操作步骤

1. 开始

2. 初始化种群

3. 评估种群中每个个体的适应度

4. 是否满足停止条件?

  - 是:结束,输出最优解

  - 否:

     5. 选择操作:根据适应度选择父代个体

     6. 交叉操作:对选定的父代进行基因交叉,生成新个体

     7. 变异操作:对新生成的个体进行基因变异

     8. 评估新生成的个体的适应度

     9. 替换操作:将新生成的个体替换掉原种群中适应度较差的个体

     10. 回到步骤4

11. 结束

1. 基因编码(Representation):

  • 个体表示: 解决方案被编码成一个个体,通常称为染色体。染色体由基因组成,而基因则是问题的解决方案的一部分。
  • 编码方式: 基因可以用二进制、实数、整数等方式进行编码,具体取决于问题的性质。

2. 初始化种群(Initialization):

  • 种群生成: 随机生成初始的个体群体,即种群。每个个体代表问题的一个可能解。

3. 适应度评估(Evaluation):

  • 适应度函数: 为每个个体计算适应度值,该值表示个体解决问题的优劣程度。适应度函数是根据问题的特性而定义的。

4. 选择(Selection):

  • 轮盘赌选择: 个体被选择的概率与其适应度成正比。适应度较高的个体更有可能被选中,以模拟自然选择的过程。

5. 交叉(Crossover):

  • 基因交叉: 选定一对父代个体,通过某种方式将它们的基因组合生成新的个体。常见的交叉方式包括单点交叉、多点交叉、均匀交叉等。

6. 变异(Mutation):

  • 基因变异: 对个体的某些基因进行随机变动。变异操作引入了新的基因信息,有助于保持种群的多样性。

7. 替换(Replacement):

  • 新一代形成: 通过选择、交叉和变异生成的新个体替代原来种群中的一部分个体。替换操作保持种群规模不变。

8. 重复迭代(Iteration):

  • 演化过程: 通过反复进行选择、交叉、变异和替代,逐渐进化种群。迭代次数可以根据问题的复杂性和算法性能进行调整。

9. 收敛检测:

  • 停止条件: 当达到预定的停止条件(如迭代次数达到设定值或找到满意的解)时,遗传算法终止。

10. 最优解提取:

  • 解的提取: 在遗传算法运行结束后,从最终的种群中提取具有最佳适应度的个体,即优秀的解决方案。

其核心思想就是通过模拟自然选择、遗传机制,遗传算法能够在搜索空间中自适应地寻找问题的优秀解。主要是通过交叉和变异引入新的组合解。

四、算法演示(Python)

  1. 问题描述: 该问题涉及通过遗传算法优化四个参数(p1、p2、q1、q2),使得目标函数的值最小化。目标函数用于拟合一些实际数据,其中包括肝、肺、胃内的浓度数据。
  2. 编码和解码:
  • 编码: 每个个体(种群中的一个成员)通过二进制编码表示四个参数。
  • 解码: 通过解码操作将二进制编码转换为实际参数值。
  1. 目标函数设计:
  • 提供了两种不同的目标函数(F和F2)供选择。
  • 目标函数的计算基于实际数据和模型预测值之间的误差。
  1. 适应度函数:
  • 适应度函数根据目标函数的值计算个体的适应度。适应度越高,个体越有可能被选择为父代。
  1. 遗传算法操作:
  • 选择: 根据适应度选择个体,采用轮盘赌选择。
  • 交叉和变异: 采用单点交叉操作,以一定的概率发生交叉,并对基因进行变异。
  • 替换: 将新生成的个体替换掉原种群中适应度较差的个体。
  1. 迭代: 通过多次迭代,不断演化种群,寻找最优解。
  2. 结果输出: 打印最终种群中适应度最好的个体及其对应的参数值。
import numpy as np
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)

五、总结

遗传算法通过模拟自然选择和遗传机制,能够在搜索空间中找到较好的解决方案,尤其在复杂问题和大规模搜索空间中表现出色。但是存在最大缺点是需要巨大计算资源,对于及时响应存在不足问题。目前对于遗传算法还有待深入研究,尤其是对于不同实际问题需求,数据特点等,通过引入随机操作等降低算法时间和空间复杂度是一项值得深入的研究方向。

大家点赞、收藏、关注、评论啦 !

谢谢哦!如果不懂,欢迎大家下方讨论学习哦。


相关文章
|
8天前
|
Python
用python进行视频剪辑源码
这篇文章提供了一个使用Python进行视频剪辑的源码示例,通过结合moviepy和pydub库来实现视频的区间切割和音频合并。
24 2
|
9天前
|
机器学习/深度学习 人工智能 算法
【新闻文本分类识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台
文本分类识别系统。本系统使用Python作为主要开发语言,首先收集了10种中文文本数据集("体育类", "财经类", "房产类", "家居类", "教育类", "科技类", "时尚类", "时政类", "游戏类", "娱乐类"),然后基于TensorFlow搭建CNN卷积神经网络算法模型。通过对数据集进行多轮迭代训练,最后得到一个识别精度较高的模型,并保存为本地的h5格式。然后使用Django开发Web网页端操作界面,实现用户上传一段文本识别其所属的类别。
22 1
【新闻文本分类识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台
|
6天前
|
大数据 UED 开发者
实战演练:利用Python的Trie树优化搜索算法,性能飙升不是梦!
在数据密集型应用中,高效搜索算法至关重要。Trie树(前缀树/字典树)通过优化字符串处理和搜索效率成为理想选择。本文通过Python实战演示Trie树构建与应用,显著提升搜索性能。Trie树利用公共前缀减少查询时间,支持快速插入、删除和搜索。以下为简单示例代码,展示如何构建及使用Trie树进行搜索与前缀匹配,适用于自动补全、拼写检查等场景,助力提升应用性能与用户体验。
21 2
|
9天前
|
算法 Python
震惊!Python 算法设计背后,时间复杂度与空间复杂度的惊天秘密大起底!
在 Python 算法设计中,理解并巧妙运用时间复杂度和空间复杂度的知识,是实现高效、优雅代码的必经之路。通过不断地实践和优化,我们能够在这两个因素之间找到最佳的平衡点,创造出性能卓越的程序。
26 4
|
11天前
|
算法 搜索推荐 开发者
别再让复杂度拖你后腿!Python 算法设计与分析实战,教你如何精准评估与优化!
在 Python 编程中,算法的性能至关重要。本文将带您深入了解算法复杂度的概念,包括时间复杂度和空间复杂度。通过具体的例子,如冒泡排序算法 (`O(n^2)` 时间复杂度,`O(1)` 空间复杂度),我们将展示如何评估算法的性能。同时,我们还会介绍如何优化算法,例如使用 Python 的内置函数 `max` 来提高查找最大值的效率,或利用哈希表将查找时间从 `O(n)` 降至 `O(1)`。此外,还将介绍使用 `timeit` 模块等工具来评估算法性能的方法。通过不断实践,您将能更高效地优化 Python 程序。
27 4
|
8天前
|
算法 程序员 Python
程序员必看!Python复杂度分析全攻略,让你的算法设计既快又省内存!
在编程领域,Python以简洁的语法和强大的库支持成为众多程序员的首选语言。然而,性能优化仍是挑战。本文将带你深入了解Python算法的复杂度分析,从时间与空间复杂度入手,分享四大最佳实践:选择合适算法、优化实现、利用Python特性减少空间消耗及定期评估调整,助你写出高效且节省内存的代码,轻松应对各种编程挑战。
20 1
|
9天前
|
算法 计算机视觉 Python
Python并查集大揭秘:让你在算法界呼风唤雨,秒杀一切复杂场景!
在编程与算法的广袤天地中,总有一些工具如同神兵利器,能够助你一臂之力,在复杂的问题前游刃有余。今天,我们就来深入探讨这样一件神器——Python并查集(Union-Find),看看它是如何让你在算法界呼风唤雨,轻松应对各种复杂场景的。
22 2
|
9天前
|
机器学习/深度学习 人工智能 算法
【果蔬识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台
【果蔬识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台。果蔬识别系统,本系统使用Python作为主要开发语言,通过收集了12种常见的水果和蔬菜('土豆', '圣女果', '大白菜', '大葱', '梨', '胡萝卜', '芒果', '苹果', '西红柿', '韭菜', '香蕉', '黄瓜'),然后基于TensorFlow库搭建CNN卷积神经网络算法模型,然后对数据集进行训练,最后得到一个识别精度较高的算法模型,然后将其保存为h5格式的本地文件方便后期调用。再使用Django框架搭建Web网页平台操作界面,实现用户上传一张果蔬图片识别其名称。
28 0
【果蔬识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台
|
13天前
|
缓存 算法 数据处理
时间&空间复杂度,Python 算法的双重考验!如何优雅地平衡两者,打造极致性能?
在Python算法中,时间与空间复杂度的平衡至关重要。时间复杂度反映算法执行时间随输入规模的变化趋势,空间复杂度则关注额外存储空间的需求。优秀的算法需兼顾两者,如线性搜索时间复杂度为O(n),空间复杂度为O(1);二分查找在时间效率上显著提升至O(log n),空间复杂度保持为O(1);动态规划通过牺牲O(n)空间换取O(n)时间内的高效计算。实际应用中,需根据具体需求权衡,如实时数据处理重视时间效率,而嵌入式系统更关注空间节约。通过不断优化,我们能在Python中找到最佳平衡点,实现高性能程序。
34 3
下一篇
无影云桌面