基于遗传算法 (Genetic Algorithm, GA) 实现N个城市环游最优路径规划计算

遗传算法(Genetic Algorithm,GA)是一种基于生物进化过程的优化算法,通过模拟自然界的遗传和进化机制,寻找问题的最优解。在解决复杂和多变量的优化问题中,遗传算法表现出良好的鲁棒性和全局搜索能力。

遗传算法的流程包括以下步骤:

  1. 初始化种群:创建由个体组成的初始种群。每个个体表示问题的一个潜在解,并用染色体(Chromosome)表示。

  2. 评估适应度:通过适应度函数评估种群中每个个体的适应度,即个体解的优劣程度。适应度函数根据问题不同而定,可以是目标函数值的最大化或最小化。

  3. 选择操作:根据适应度值,选择一部分适应度较高的个体作为“父代”,用于产生下一代。高适应度个体被选择的概率较高,以保留良好特性。

  4. 交叉操作:从父代中选择两个个体,通过交叉操作生成新的个体。交叉操作模拟了基因在生物繁殖过程中的交叉。

  5. 变异操作:对新生成的个体进行变异,以增加种群的多样性。变异操作模拟了基因突变过程。

  6. 更新种群:用新生成的个体替换原有的个体,形成下一代种群。

  7. 终止条件:重复进行步骤2到步骤6,直到满足终止条件,如达到最大迭代次数或达到特定的目标解。

遗传算法的优点包括:

  1. 全局搜索能力:遗传算法通过维护一个多样性的种群,能够在解空间中进行全局搜索,找到更优解。

  2. 鲁棒性:遗传算法不依赖于问题的具体形式,适用于多种优化问题。它可以处理连续、离散或混合型的变量,并且对问题的约束灵活适应。

  3. 并行计算能力:由于遗传算法是通过对个体进行并行评估和操作,它能够有效利用并行计算能力,加速优化过程。

然而,遗传算法也存在一些缺点:

  1. 参数设置:遗传算法的性能表现高度依赖于参数的设置,如种群大小、交叉率、变异率等。不同的问题可能需要不同的参数设置。

  2. 收敛速度:由于遗传算法通过随机搜索进行优化,它的收敛速度相对较慢。对于复杂问题,可能需要较长的时间才能找到较好的解。

  3. 问题依赖性:尽管遗传算法适用于多种问题,但对于特定的问题,可能存在更适合的优化算法。

遗传算法是一种强大的优化算法,能够解决复杂的优化问题,并具有全局搜索能力和鲁棒性。但它也有一些限制,对参数设置敏感,收敛速度较慢。在实践中,可以根据具体问题的特点和限制,选择合适的优化算法进行求解。

早上的时候遇上一个问题就是想要能够自动化地求解N个地点遍历最优的路径规划,这时候想到了使用遗传算法GA,GA可以说是比较早比较经典的算法了,上面对其进行了简单的总结和回顾,这里,我们来看下具体的实现。

为了方便说明,这里简化问题,数据都是随机生成的,当然了你也可以实际使用地图获取真实的城市坐标,都是可以的:

# 城市坐标
city_pos_list = np.random.rand(config.city_num, config.pos_dimension)
print("city_pos_list: ", city_pos_list)

接下来需要构建计算城市两两之间的距离,形成距离矩阵,如下所示:

def build_dist_mat(data):
    """
    构建距离矩阵
    """
    n = config.city_num
    dist_mat = np.zeros([n, n])
    for i in range(n):
        for j in range(i + 1, n):
            d = data[i, :] - data[j, :]
            dist_mat[i, j] = np.dot(d, d)
            dist_mat[j, i] = dist_mat[i, j]
    return dist_mat

我们这里设定城市数量为5,看下实例结果输出:

city_pos_list:  
[[0.4857694  0.37717288]
 [0.12982822 0.92253033]
 [0.63096373 0.89184198]
 [0.70368269 0.59460617]
 [0.8681248  0.58574649]]


city_dist_mat:
[0.0, 0.4241088699265403, 0.2859656753224421, 0.09476343738540502, 0.18969860731199]
[0.4241088699265403, 0.0, 0.25207857105384973, 0.43684320650858754, 0.6585051993116404]
[0.2859656753224421, 0.25207857105384973, 0.0, 0.09363717469660143, 0.1499398246120906]
[0.09476343738540502, 0.43684320650858754, 0.09363717469660143, 0.0, 0.02711970301160461]
[0.18969860731199, 0.6585051993116404, 0.1499398246120906, 0.02711970301160461, 0.0]

GA算法的实现可以直接参考开源社区即可,网上有很多非常好的讲解,这里我给出自己的实例实现,如下所示:

class Individual:
    """
    个体类
    """
    def __init__(self, genes=None):
        # 随机生成序列
        if genes is None:
            genes = [i for i in range(gene_len)]
            random.shuffle(genes)
        self.genes = genes
        self.fitness = self.evaluate_fitness()

    def evaluate_fitness(self):
        # 计算个体适应度
        fitness = 0.0
        for i in range(gene_len - 1):
            # 起始城市和目标城市
            from_idx = self.genes[i]
            to_idx = self.genes[i + 1]
            fitness += city_dist_mat[from_idx, to_idx]
        # 连接首尾
        fitness += city_dist_mat[self.genes[-1], self.genes[0]]
        return fitness


class Ga:
    def __init__(self, input_):
        global city_dist_mat
        city_dist_mat = input_
        self.best = None  # 每一代的最佳个体
        self.individual_list = []  # 每一代的个体列表
        self.result_list = []  # 每一代对应的解
        self.fitness_list = []  # 每一代对应的适应度

    def cross(self):
        new_gen = []
        random.shuffle(self.individual_list)
        for i in range(0, individual_num - 1, 2):
            # 父代基因
            genes1 = copy_list(self.individual_list[i].genes)
            genes2 = copy_list(self.individual_list[i + 1].genes)
            index1 = random.randint(0, gene_len - 2)
            index2 = random.randint(index1, gene_len - 1)
            pos1_recorder = {value: idx for idx, value in enumerate(genes1)}
            pos2_recorder = {value: idx for idx, value in enumerate(genes2)}
            # 交叉
            for j in range(index1, index2):
                value1, value2 = genes1[j], genes2[j]
                pos1, pos2 = pos1_recorder[value2], pos2_recorder[value1]
                genes1[j], genes1[pos1] = genes1[pos1], genes1[j]
                genes2[j], genes2[pos2] = genes2[pos2], genes2[j]
                pos1_recorder[value1], pos1_recorder[value2] = pos1, j
                pos2_recorder[value1], pos2_recorder[value2] = j, pos2
            new_gen.append(Individual(genes1))
            new_gen.append(Individual(genes2))
        return new_gen

    def mutate(self, new_gen):
        for individual in new_gen:
            if random.random() < mutate_prob:
                # 翻转切片
                old_genes = copy_list(individual.genes)
                index1 = random.randint(0, gene_len - 2)
                index2 = random.randint(index1, gene_len - 1)
                genes_mutate = old_genes[index1:index2]
                genes_mutate.reverse()
                individual.genes = old_genes[:index1] + genes_mutate + old_genes[index2:]
        # 两代合并
        self.individual_list += new_gen

    def select(self):
        # 筛选
        group_num = 10  # 小组数
        group_size = 10  # 每小组人数
        group_winner = individual_num // group_num  # 每小组获胜人数
        winners = []  # 锦标赛结果
        for i in range(group_num):
            group = []
            for j in range(group_size):
                # 随机组成小组
                player = random.choice(self.individual_list)
                player = Individual(player.genes)
                group.append(player)
            group = Ga.rank(group)
            # 取出获胜者
            winners += group[:group_winner]
        self.individual_list = winners

    @staticmethod
    def rank(group):
        # 冒泡排序
        for i in range(1, len(group)):
            for j in range(0, len(group) - i):
                if group[j].fitness > group[j + 1].fitness:
                    group[j], group[j + 1] = group[j + 1], group[j]
        return group

    def next_gen(self):
        # 交叉
        new_gen = self.cross()
        # 变异
        self.mutate(new_gen)
        # 选择
        self.select()
        # 获得这一代的结果
        for individual in self.individual_list:
            if individual.fitness < self.best.fitness:
                self.best = individual

    def train(self):
        # 初代种群
        self.individual_list = [Individual() for _ in range(individual_num)]
        self.best = self.individual_list[0]
        # 迭代
        for i in range(gen_num):
            self.next_gen()
            # 连接首尾
            result = copy_list(self.best.genes)
            result.append(result[0])
            self.result_list.append(result)
            self.fitness_list.append(self.best.fitness)
        return self.result_list, self.fitness_list

接下来就可以喂入数据执行算法计算了:

# 遗传算法运行
ga = Ga(city_dist_mat)
result_list, fitness_list = ga.train()

为了直观呈现效果,这里对其结果进行了可视化,如下所示:

【5个城市】

【10个城市】

【15个城市】

【20个城市】

【30个城市】

【50个城市】

【100个城市】

随着城市数量的增多,路线显得比较混乱,不过还是能看出来轮廓的,感兴趣的话都可以自行动手尝试一下。

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

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

相关文章

C++ deque底层原理

deque底层原理 一、目的二、底层实现三、原理图四、类结构五、push_back六、pop_back 一、目的 实现双端数组 二、底层实现 双向开口的连续线性空间 三、原理图 四、类结构 class deque : protected Deque base _Deque_base._Deque_impl M_map 指针数组 _M_map_size …

Spring——Spring基础

文章目录 1. Spring架构2. RestController vs Controller3. Autowired和Resource的区别是啥4. Spring IOC & AOP4.1 谈谈自己对于 Spring IoC 和 AOP 的理解IoCAOP 4.2 Spring AOP 和 AspectJ AOP 有什么区别&#xff1f; 5. Spring bean5.1 Spring 中的 bean 的作用域有哪…

如何能使mp3的音量变大?

如何能使mp3的音量变大&#xff1f;我们经常在日常生活中使用的一种音频格式是MP3。许多朋友在下载音乐后&#xff0c;都会选择MP3格式进行播放。然而&#xff0c;在我们的日常生活中&#xff0c;我们有时会遇到音量太小的问题。这时候&#xff0c;我们听歌可能会感到很不舒服。…

MySQL项目迁移华为GaussDB PG模式指南

文章目录 0. 前言1. 数据库模式选择&#xff08;B/PG&#xff09;2.驱动选择2.1. 使用postgresql驱动2.1. 使用opengaussjdbc驱动 3. 其他考虑因素4. PG模式4.1 MySQL和OpenGauss不兼容的语法处理建议4.2 语法差异 6. 高斯数据库 PG模式JDBC 使用示例验证6. 参考资料 本章节主要…

Jmeter(二十七):BeanShell PostProcessor跨线程全局变量使用

在性能测试中&#xff0c;两个相关联的接口不一定都在同一个线程组&#xff0c;遇见这种情况时&#xff0c;我们要进行跨线程组传参&#xff0c;此处用登录和查询配送单两个请求举例&#xff1b; 1、登录请求中配置json提取器&#xff0c;将接口返回的token保存在变量中&#…

短视频矩阵源码saas开发搭建

一、 短视频矩阵系统源码开发部署步骤分享 确定开发环境&#xff1a;务必准备好项目的开发环境&#xff0c;包括操作系统、IDE、数据库和服务器等。 下载源码&#xff1a;从官方网站或者Github等平台下载短视频矩阵系统源码&#xff0c;并进行解压。 配置数据库&#xff1a;根…

Python金币小游戏

游戏规则&#xff1a;移动挡板接住金币 游戏截图&#xff1a; 详细代码如下&#xff1a; import pygame.freetype import sys import randompygame.init() screen pygame.display.set_mode((600, 400)) pygame.display.set_caption(game) p 0 i1 0 s 0 t 0 f1 pygame.f…

【C++深入浅出】初识C++下篇(auto关键字、范围for、nullptr指针)

目录 一. 前言 二. auto关键字 2.1 auto的引入 2.2 auto简介 2.3 auto的使用细则 2.4 auto不能推导的场景 三. 基于范围的for循环(C11) 3.1 范围for的语法 3.2 范围for的原理 3.3 范围for的使用条件 四. 指针空值nullptr(C11) 一. 前言 上期我们介绍了c新增的两个重…

Javascript 中的 debugger 拦截

debugger 指令&#xff0c;一般用于调试&#xff0c;在如浏览器调试执行环境中&#xff0c;可以在 JavaScript 代码中产生中断。 如果想要拦截 debugger&#xff0c;是不容易的&#xff0c;常用的函数替代、proxy 方法均对它无效&#xff0c;如&#xff1a; window.debugger …

[ZenTao]源码阅读:加载自定义任务类型

www/index.php config/config.php framework/base/router.class.php tmp/model/common.php module/common/model.php framework/router.class.php

lib61850 学习笔记一 (概念)

IEC61850 定义60多种服务满足变电站通信需求。支持在线获取数据模型&#xff0c;也支持IED水平通信&#xff08;GOOSE报文&#xff09; 术语定义 间隔 bay: 变电站由据应公共功能紧密连接的子部分组成。 例如 介于进线或者 出线 和母线之间的断路器&#xff1b;二条母线之间…

IntelliJ 中如何配置 Tomcat 调试

Tomcat 在 IntelliJ 中的配置要求首先你要下载 Tomcat。 设置服务器 在 IntelliJ 下面先选择 Run&#xff0c;然后选择配置运行配置。 在弹出的界面中&#xff0c;有一个编辑配置的选项。 然后在弹出的页面中选择添加。 选择 Tomcat 在弹出的添加页面中选择添加 Tomcat&…

QGIS-计算几何内部点(一定在几何内)

在提取几何图像的中心点相关的X Y时&#xff0c;我们往往希望提取的点在几何内部&#xff0c;因为对于不规则图形而言&#xff0c;特别是凹几何&#xff0c;提取的点可能在图形外&#xff0c;QGIS中提供了相关的函数用于提取点中心点&#xff1a; 打开图形的属性列表&#xff…

《Flink学习笔记》——第五章 DataStream API

一个Flink程序&#xff0c;其实就是对DataStream的各种转换&#xff0c;代码基本可以由以下几部分构成&#xff1a; 获取执行环境读取数据源定义对DataStream的转换操作输出触发程序执行 获取执行环境和触发程序执行都属于对执行环境的操作&#xff0c;那么其构成可以用下图表示…

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)

文件上传漏洞-upload靶场1-2关 通过笔记&#xff08;区分前段验证和后端验证&#xff09; 前言 upload是一个文件上传的专用靶场&#xff0c;搭设也非常简单&#xff0c;只需要把相关源码文件放到apache的网站目录下即可使用&#xff0c;或者去github下载一键绿化包进行安装链…

路由转发(详细理解+实例精讲)

系列文章目录 华为数通学习&#xff08;5&#xff09; 目录 华为数通学习&#xff08;5&#xff09; 前言 一&#xff0c;最长匹配原则 实例1&#xff1a; 实例2&#xff1a; 二&#xff0c;路由转发流程&#xff1a; 三&#xff0c;IP路由表小结&#xff1a; 总结 前…

【同步异步可并发日志系统】设计及实现

1. 项⽬介绍2. 开发环境3. 项目核⼼技术4. 环境搭建5. ⽇志系统介绍5.1 为什么需要⽇志系统5.2⽇志系统技术实现5.2.1 同步写⽇志5.2.2 异步写⽇志 6. ⽇志系统框架设计6.1 各模块测试代码 7. 代码设计7.1 实⽤类设计7.2 ⽇志等级类设计7.3 ⽇志消息类设计7.4 ⽇志格式化输出设…

Easy Rules规则引擎(2-细节篇)

目录 一、序言二、规则引擎参数配置实例1、skipOnFirstAppliedRules示例(1) FizzRule(2) BuzzRule(3) FizzBuzzRule(4) NonFizzBuzzRule(5) FizzBuzzRulesLauncher 2、skipOnFirstNonTriggeredRule示例3、skipOnFirstFailedRule示例 三、组合规则1、UnitRuleGroup组合规则2、Ac…

java八股文面试[多线程]——synchronized 和lock的区别

其他差别&#xff1a; synchronized是隐式的加锁,lock是显式的加锁; synchronized底层采用的是objectMonitor,lock采用的AQS; synchronized在进行加锁解锁时,只有一个同步队列和一个等待队列, lock有一个同步队列,可以有多个等待队列; synchronized使用了object类的wait和noti…

经典问题解析四

关于动态内存分配 new 和 malloc 的区别是什么&#xff1f; delete 和 free 的区别是什么&#xff1f; new 关键字与 malloc 函数的区别 new 关键字是 C 的一部分 malloc 是由 C 库函数提供的函数 new 是以具体类型为单位进行内存分配 malloc 以字节为单位进行内存分配 …
最新文章