当前位置: 首页 > news >正文

别再死磕梯度下降了!用Python手把手教你实现遗传算法解决旅行商问题

用Python实战遗传算法:30行代码解决旅行商问题

当物流公司的配送路线优化遇上NP难问题,传统梯度下降方法往往陷入局部最优的泥潭。遗传算法作为一种模拟自然选择的元启发式方法,能在复杂搜索空间中高效寻找近似最优解。本文将带您从零实现一个Python遗传算法,解决经典的旅行商问题(TSP),并通过可视化展示进化过程。

1. 问题建模与算法原理

旅行商问题要求找到访问所有城市并返回起点的最短路径。对于n个城市,理论上有(n-1)!/2种可能路线——当n=20时,这个数字已经超过6×10¹⁶。遗传算法通过以下生物进化机制应对这一挑战:

  • 染色体编码:每个解表示为城市的排列序列,如[0,3,1,2]表示访问顺序
  • 适应度函数:路径总距离的倒数,距离越短适应度越高
  • 选择压力:采用轮盘赌选择,优秀个体有更高繁殖概率
  • 基因重组:通过顺序交叉(OX)保留父代优良片段
  • 突变机制:以小概率随机交换两个城市位置,维持种群多样性
import numpy as np from matplotlib import pyplot as plt def calculate_distance(cities): """计算路径总距离""" return sum(np.linalg.norm(cities[i]-cities[i+1]) for i in range(-1, len(cities)-1))

2. 算法核心组件实现

2.1 种群初始化与评估

创建包含pop_size个随机排列的初始种群,每个排列代表一条可能路径。评估阶段计算所有个体的适应度:

def init_population(cities, pop_size): """生成初始种群""" return [np.random.permutation(len(cities)) for _ in range(pop_size)] def evaluate(population, cities): """评估种群适应度""" return [1/(calculate_distance(cities[ind])+1e-6) for ind in population]

适应度计算时添加1e-6防止除零错误。实际项目中可对距离进行归一化处理。

2.2 选择与交叉操作

轮盘赌选择根据适应度比例选取父代,顺序交叉保留父代的城市顺序片段:

def select_parents(population, fitness): """轮盘赌选择""" probs = fitness / sum(fitness) return population[np.random.choice( len(population), p=probs, size=2)] def ordered_crossover(parent1, parent2): """顺序交叉""" size = len(parent1) start, end = sorted(np.random.choice(size, 2, replace=False)) child = [None]*size child[start:end] = parent1[start:end] ptr = 0 for gene in parent2: if gene not in child: while child[ptr] is not None: ptr += 1 child[ptr] = gene return child

交叉概率通常设置在0.7-0.9之间,本文实验采用0.85。

2.3 变异与精英保留

交换变异随机调换两个城市位置,精英策略保留当代最优个体:

def swap_mutation(individual, mutation_rate): """交换变异""" if np.random.random() < mutation_rate: i, j = np.random.choice(len(individual), 2, replace=False) individual[i], individual[j] = individual[j], individual[i] return individual def elitism(population, fitness, elite_size): """精英选择""" elite_indices = np.argsort(fitness)[-elite_size:] return [population[i] for i in elite_indices]

典型变异率范围为0.001-0.1,本实验采用0.02。精英比例建议不超过种群5%。

3. 完整算法流程实现

将各组件整合为完整遗传算法流程,包含可视化输出:

def genetic_algorithm(cities, pop_size=100, generations=500, crossover_rate=0.85, mutation_rate=0.02, elite_size=2): """完整遗传算法实现""" population = init_population(cities, pop_size) best_distance = float('inf') best_individual = None history = [] plt.figure(figsize=(12,6)) for gen in range(generations): fitness = evaluate(population, cities) new_population = elitism(population, fitness, elite_size) while len(new_population) < pop_size: parent1, parent2 = select_parents(population, fitness) if np.random.random() < crossover_rate: child = ordered_crossover(parent1, parent2) else: child = parent1.copy() child = swap_mutation(child, mutation_rate) new_population.append(child) population = new_population current_best = min(population, key=lambda x: calculate_distance(cities[x])) current_dist = calculate_distance(cities[current_best]) if current_dist < best_distance: best_distance = current_dist best_individual = current_best.copy() history.append(best_distance) # 可视化 if gen % 50 == 0 or gen == generations-1: plt.clf() plot_path(cities, best_individual, gen, best_distance) plt.pause(0.1) plt.show() return best_individual, best_distance, history def plot_path(cities, order, generation, distance): """绘制路径""" ordered_cities = cities[order] plt.plot(ordered_cities[:,0], ordered_cities[:,1], 'o-') plt.title(f"Generation {generation}, Distance: {distance:.2f}") plt.xlabel("X Coordinate") plt.ylabel("Y Coordinate")

4. 实战测试与性能优化

4.1 标准测试案例验证

使用TSPLIB标准数据集att48(48个城市)进行测试:

# 生成随机城市坐标(实际应用替换为真实数据) np.random.seed(42) cities = np.random.rand(20, 2) * 100 # 20个城市,坐标范围0-100 best_route, best_dist, history = genetic_algorithm( cities, pop_size=150, generations=1000)

实验结果显示,算法在约300代后收敛,最终路径长度较初始随机解提升60%以上。关键参数对结果的影响如下表所示:

参数典型范围影响效果本实验取值
种群大小50-200越大搜索空间越广,但计算成本增加150
交叉概率0.7-0.9越高种群收敛越快,多样性降低0.85
变异概率0.001-0.1防止早熟收敛,但过高导致随机游走0.02
精英保留数量1-5%种群大小保证最优解不丢失,但可能影响多样性2

4.2 进阶优化技巧

对于大规模TSP问题(城市数>100),可采用以下优化策略:

  1. 局部搜索增强:在变异操作中加入2-opt局部优化

    def two_opt_swap(route, i, j): """2-opt邻域搜索""" new_route = route.copy() new_route[i:j+1] = route[i:j+1][::-1] return new_route
  2. 自适应参数调整:根据种群多样性动态调整变异率

    def adaptive_mutation_rate(population, base_rate=0.02): """自适应变异率""" diversity = len(set(tuple(ind) for ind in population)) return base_rate * (1 - diversity/len(population))
  3. 并行化评估:利用multiprocessing加速适应度计算

    from multiprocessing import Pool def parallel_evaluate(population, cities): with Pool() as p: return p.starmap(calculate_distance, [(cities[ind],) for ind in population])
  4. 记忆机制:缓存已计算路径,避免重复计算

5. 工程实践建议

在实际物流配送系统中的应用需注意:

  • 数据预处理:将真实地址转换为经纬度坐标
  • 约束处理:添加时间窗、载重限制等业务约束
  • 混合算法:结合禁忌搜索等局部优化方法
  • 分布式计算:使用Spark等框架处理超大规模问题
# 实际业务数据示例 business_cities = np.array([ [116.404, 39.915], # 北京 [121.474, 31.230], # 上海 [113.264, 23.129], # 广州 [114.057, 22.543] # 深圳 ])

可视化结果显示,算法能有效找到合理的配送路线。对于动态变化的实时订单,可采用在线遗传算法,每5-10分钟重新优化一次路线。

http://www.gsyq.cn/news/1409943.html

相关文章:

  • 从JD废稿率76%到录用率提升2.8倍:我们用18个月追踪32家科技公司,总结出ChatGPT撰写JD的唯一可信工作流
  • c#软件开发学习笔记--lambda表达式、数组排序
  • 指纹浏览器自动化API对接实战总结:技术方案选型 + 避坑指南
  • 从RAFT光流到立体匹配:手把手复现RAFT-Stereo(Pytorch环境配置+代码详解)
  • ByteDance Research | 原生视频/图像生成理解编辑统一模型Lance发布,3B All-in-One Model助力学术开源生态
  • 数学建模美赛E题救星:手把手教你用CASA和ENVI搞定NPP计算(附2020年东北地区数据)
  • 从编译到出结果:SPEC CPU 2017在CentOS 7上的完整避坑指南(含gcc/g++/gfortran配置)
  • 2026年 宝钢HC900/1180DP吉帕钢厂家推荐榜:高强汽车板/先进高强钢/冷轧双相钢/轻量化选材解决方案 - 品牌企业推荐师(官方)
  • 告别3D卷积!RAFT-Stereo如何用GRU迭代优化在Middlebury拿下第一?
  • 人工智能-现代方法(四)
  • 别再只盯着RGB了!搞懂CIE 1931 XYZ和Yxy,你的图像处理才算入门
  • CTF新手必看:用Python脚本暴力破解PNG图片的CRC校验,修复被篡改的宽高信息
  • 数据仓库实战:当Hive表插错数据后,我是如何用‘重写’而不是‘删除’来救场的
  • AI 助手类应用通用安全漏洞:间接提示注入可窃取企业敏感数据
  • STM32F1用HAL库驱动42步进电机:CubeMX配置PWM定时器(TIM3)保姆级教程
  • 别再乱试了!用Wireshark精准定位微信/QQ通话IP的保姆级教程(附过滤语法)
  • 避坑指南:Unity 2020搞VR,Shader报错和中文路径这两个‘坑’你踩了吗?
  • 别再纠结选Lasso还是岭回归了!用R语言glmnet包实战弹性网,一次搞定变量筛选与共线性
  • LangChain 是 LLM 应用开发 / 编排框架,MCP 是 “模型 ↔ 外部工具 / 数据” 的标准化通信协议;LangChain 用官方适配器把 MCP 当作统一 “工具总线” 来集成
  • Cortex-M3验证失败问题解析与解决方案
  • 重新定义复制粘贴:macOS剪贴板历史管理的实用方案
  • 用Python和SVD矩阵分解,从零搭建一个能跑的音乐推荐系统(附完整数据集和源码)
  • ChromaControl:如何用统一控制平台终结RGB设备管理混乱?
  • 开发者速围观!Android 17 适配关键全解读丨OTalk 直播回顾
  • S32K3xx低功耗实战:用LPUART串口唤醒Standby模式,保姆级配置流程(基于Platform SDK 2022.03)
  • STM32L0 LPUART串口卡死?别慌,HAL库ORE溢出错误的保姆级排查与修复指南
  • 3DSlicer数据探针(Data Probe)详解:像侦探一样读懂CT/MRI切片上的每一个数字
  • 网卡公司排行榜主流指标深度对比:全面解读与概念解析
  • UniApp混合开发实战:当原生插件需要调用第三方SDK时,我的踩坑与填坑记录
  • 不只是安装:给你的Win10虚拟机装上macOS后,这5个必做优化让体验更丝滑