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

Python人工智能算法学习 禁忌搜索算法求解旅行商问题(TSP)的研究与实现

禁忌搜索算法求解旅行商问题(TSP)的研究与实现

摘要

本文针对旅行商问题(TSP),设计并实现了一种基于禁忌搜索(Tabu Search, TS)的智能优化算法。通过构建包含50个城市的TSP实例,采用禁忌表、邻域搜索和特赦准则等核心机制,有效求解大规模组合优化问题。实验结果表明,该算法在合理时间内能够找到高质量的近似最优解,并通过收敛曲线验证了算法的有效性。

1. 引言

旅行商问题(TSP)是组合优化领域的经典NP-hard问题,其目标是在给定城市集合及距离矩阵的条件下,寻找一条总距离最短的闭合回路。随着城市规模扩大,精确算法难以在合理时间内求解,因此启发式算法成为研究热点。禁忌搜索算法通过模拟人类记忆机制,能够有效避免局部最优,适用于大规模TSP问题求解。

本文旨在:

  1. 设计并实现一种基于禁忌搜索的TSP求解算法
  2. 分析算法关键要素(禁忌表、邻域结构、参数设置)对性能的影响
  3. 在50个城市规模问题上验证算法有效性
  4. 通过可视化分析算法收敛过程

2. 问题描述与数学建模

2.1 TSP问题定义

给定 n n n个城市集合 C = { c 1 , c 2 , . . . , c n } C=\{c_1,c_2,...,c_n\} C={c1,c2,...,cn} 及距离矩阵 D = [ d i j ] n × n D=[d_{ij}]_{n \times n} D=[dij]n×n ,其中 d i j d_{ij} dij 表示城市 c i c_i ci c j c_j cj 的欧氏距离。要求构造一条哈密顿回路 π = ( π 1 , π 2 , . . . , π n ) \pi=(\pi_1,\pi_2,...,\pi_n) π=(π1,π2,...,πn) ,使得总距离 ∑ i = 1 n d π i π i + 1 \sum_{i=1}^{n}d_{\pi_i \pi_{i+1}} i=1ndπiπi+1 最小,其中 π n + 1 = π 1 \pi_{n+1}=\pi_1 πn+1=π1

2.2 实例生成

本文采用随机生成方式构建50个城市坐标,通过公式计算欧氏距离矩阵:
d i j = ( x i − x j ) 2 + ( y i − y j ) 2 d_{ij} = \sqrt{(x_i-x_j)^2 + (y_i-y_j)^2} dij=(xixj)2+(yiyj)2
其中 ( x i , y i ) (x_i,y_i) (xi,yi)为城市 i i i的坐标,范围在 [ 0 , 100 ] [0,100] [0,100]区间内均匀分布。

3. 禁忌搜索算法设计

3.1 算法流程

算法包含以下核心模块:

  1. 初始解生成
  2. 邻域结构构建
  3. 禁忌表管理
  4. 特赦准则判断
  5. 迭代终止条件

算法流程图如下:

在这里插入图片描述

3.2 关键函数实现与参数设置

3.2.1 初始解生成(generate_initial_solution
def generate_initial_solution(n_cities):path = list(range(n_cities))np.random.shuffle(path)if path[0] != 0:idx = path.index(0)path[0], path[idx] = path[idx], path[0]return path

设计理由:强制以0号城市为起点,保证解的空间包含所有可能路径,避免因随机起点导致的解空间偏移。

3.2.2 邻域生成(generate_neighborhood
def generate_neighborhood(path, num_neighbors=100):neighborhood = []n = len(path)for _ in range(num_neighbors):i = np.random.randint(1, n)j = np.random.randint(1, n)while i == j:j = np.random.randint(1, n)new_path = path.copy()new_path[i], new_path[j] = new_path[j], new_path[i]swap = tuple(sorted((path[i], path[j])))neighborhood.append((swap, new_path))return neighborhood

设计原理

  • 邻域操作:随机交换两个非起点城市(保持起点固定)
  • 邻域大小:100个候选解(平衡探索广度与计算效率)
  • 生成方式:蒙特卡洛随机采样

有效性分析

  1. 交换操作时间复杂度低( O ( 1 ) O(1) O(1)
  2. 非起点城市交换保证解的有效性
  3. 随机采样覆盖解空间的关键区域
3.2.3 禁忌表管理
3.2.3.1 禁忌表存储

具体代码

tabu_list = {}  # 禁忌表存储结构
tabu_list[swap] = tabu_tenure  # 禁忌期限设置为7

要素说明

  • 禁忌对象:以交换的城市对作为禁忌对象(如交换城市A和B,记录为元组(A,B))
  • 更新策略:每次迭代后,禁忌表中元素的剩余期限减1,期限为0时自动移除
  • 禁忌期限:设为7
3.2.3.2禁忌表更新机制

更新准则

  1. 递减机制:每代所有禁忌项期限减1
  2. 淘汰机制:期限≤0的禁忌项被移除
  3. 新增机制:当前采用的交换操作加入禁忌表

设计理由

  • 动态管理禁忌表大小,避免内存膨胀
  • 确保禁忌对象有明确的生命周期
  • 优先保留近期操作的禁忌状态

代码实现

# 更新禁忌表
for key in list(tabu_list.keys()):tabu_list[key] -= 1  # 禁忌期限递减if tabu_list[key] <= 0:del tabu_list[key]  # 移除过期项# 添加新禁忌项
tabu_list[swap] = tabu_tenure
3.2.4 特赦准则(Aspiration Criterion)
if swap in tabu_list:if new_distance < best_distance:# 允许打破禁忌

设计理由:当候选解的质量显著优于当前最优解时,即使其对应操作被禁忌,仍允许接受该解,避免错过全局最优。

4. 实验与分析

4.1 参数设置依据

参数设置值
禁忌期限7
邻域规模100
最大迭代次数1000

4.2 实验结果

  • 最优路径:总距离747.55(如图1所示)
  • 收敛曲线:如图2所示,算法在200次迭代内快速收敛,后续迭代保持稳定

在这里插入图片描述
图1 禁忌搜索TSP路径图

在这里插入图片描述

图2 禁忌搜索收敛曲线

5. 结论

本文实现的禁忌搜索算法通过以下创新点有效求解大规模TSP:

  1. 采用强制起点策略保证解空间完整性
  2. 设计动态禁忌表管理机制平衡探索与开发
  3. 引入特赦准则避免优质解丢失

实验表明,算法在50城市规模下能够快速收敛至高质量解,验证了算法的有效性。未来可研究自适应参数调整机制以进一步提升性能。

6.附录:完整代码

import numpy as np
import matplotlib.pyplot as pltdef generate_initial_solution(n_cities):"""生成初始可行解(旅行商路径)Args:n_cities (int): 城市数量Returns:list: 初始路径列表,保证以0号城市为起点"""# 生成0~n-1的完整排列path = list(range(n_cities))# 随机打乱路径顺序np.random.shuffle(path)# 确保路径以0号城市为起点(强制调整)if path[0] != 0:idx = path.index(0)path[0], path[idx] = path[idx], path[0]return pathdef calculate_path_distance(path, distance_matrix):"""计算路径总距离(含闭环)Args:path (list): 城市访问顺序列表distance_matrix (ndarray): 距离矩阵(n x n)Returns:float: 总路径距离"""total_distance = 0n = len(path)for i in range(n):# 计算当前城市到下一个城市的距离(i+1取模实现闭环)total_distance += distance_matrix[path[i]][path[(i+1)%n]]return total_distancedef generate_neighborhood(path, num_neighbors=100):"""生成邻域解集合(通过2-opt交换)Args:path (list): 当前路径num_neighbors (int): 生成邻域解数量Returns:list: 包含(swap_pair, new_path)元组的列表"""neighborhood = []n = len(path)for _ in range(num_neighbors):# 随机选择两个非起点城市进行交换(索引从1开始)i = np.random.randint(1, n)j = np.random.randint(1, n)while i == j:  # 确保选择不同城市j = np.random.randint(1, n)new_path = path.copy()new_path[i], new_path[j] = new_path[j], new_path[i]# 记录交换的城市对(排序后作为禁忌对象)city_i, city_j = path[i], path[j]swap = tuple(sorted((city_i, city_j)))neighborhood.append((swap, new_path))return neighborhooddef tabu_search(distance_matrix, max_iterations=1000, tabu_tenure=7, neighborhood_size=100):"""禁忌搜索算法主函数Args:distance_matrix (ndarray): 距离矩阵max_iterations (int): 最大迭代次数tabu_tenure (int): 禁忌期限(禁忌表存活周期)neighborhood_size (int): 每次迭代生成的邻域解数量Returns:tuple: (最优路径, 最优距离, 收敛历史记录)"""n_cities = distance_matrix.shape[0]# 初始化当前解和全局最优解current_solution = generate_initial_solution(n_cities)current_distance = calculate_path_distance(current_solution, distance_matrix)best_solution = current_solution.copy()best_distance = current_distance# 禁忌表结构:{swap_pair: remaining_tenure}tabu_list = {}history = []  # 记录每次迭代的最优距离for iteration in range(max_iterations):# 生成当前解的邻域候选解candidates = generate_neighborhood(current_solution, neighborhood_size)candidate_list = []for swap, new_path in candidates:# 计算每个候选解的路径距离distance = calculate_path_distance(new_path, distance_matrix)candidate_list.append((swap, new_path, distance))# 按距离升序排序候选解candidate_list.sort(key=lambda x: x[2])best_candidate = None# 遍历候选解寻找最优解(考虑禁忌表)for candidate in candidate_list:swap, new_path, new_distance = candidateif swap in tabu_list:# 特赦准则:当候选解优于全局最优时,打破禁忌if new_distance < best_distance:best_candidate = candidatebest_solution = new_path.copy()best_distance = new_distancebreakelse:# 选择第一个非禁忌的最优候选解best_candidate = candidatebreakif best_candidate:# 更新当前解和禁忌表swap, new_path, new_distance = best_candidatecurrent_solution = new_pathcurrent_distance = new_distance# 更新禁忌表:所有条目期限减1,过期条目移除for key in list(tabu_list.keys()):tabu_list[key] -= 1if tabu_list[key] <= 0:del tabu_list[key]# 添加新禁忌对象tabu_list[swap] = tabu_tenure# 更新全局最优解if new_distance < best_distance:best_solution = new_path.copy()best_distance = new_distancehistory.append(best_distance)# 每100次迭代输出当前最优if iteration % 100 == 0:print(f"Iteration {iteration}, Best Distance: {best_distance:.2f}")return best_solution, best_distance, history# 生成随机距离矩阵(50个城市)
n_cities = 50
np.random.seed(42)  # 固定随机种子保证可重复性
coordinates = np.random.rand(n_cities, 2) * 100  # 生成0-100范围的坐标
distance_matrix = np.zeros((n_cities, n_cities))
for i in range(n_cities):for j in range(n_cities):if i != j:dx = coordinates[i][0] - coordinates[j][0]dy = coordinates[i][1] - coordinates[j][1]distance_matrix[i][j] = np.sqrt(dx**2 + dy**2)# 运行禁忌搜索
best_path, best_dist, history = tabu_search(distance_matrix)# 可视化结果(改进版)
plt.figure(figsize=(10, 6))
x = [coordinates[city][0] for city in best_path + [best_path[0]]]
y = [coordinates[city][1] for city in best_path + [best_path[0]]]# 绘制路径连线
plt.plot(x, y, '--', color='gray', alpha=0.6, linewidth=1)# 单独绘制起点/终点(0号城市)
start_end_x = coordinates[0][0]
start_end_y = coordinates[0][1]
plt.plot(start_end_x, start_end_y, 'p',  # 五角星标记color='red', markersize=12,markeredgecolor='black',markeredgewidth=1,label='Start/End (City 0)')# 绘制其他城市点
other_cities_x = [coordinates[i][0] for i in range(n_cities) if i != 0]
other_cities_y = [coordinates[i][1] for i in range(n_cities) if i != 0]
plt.plot(other_cities_x, other_cities_y, 'o',  # 圆圈标记color='dodgerblue',markersize=6,alpha=0.8,label='Other Cities')# 添加路径方向箭头(每隔10个城市添加一个箭头)
for i in range(0, len(best_path), max(1, len(best_path)//10)):plt.annotate('',xytext=(x[i], y[i]),xy=(x[i+1], y[i+1]),arrowprops=dict(arrowstyle='->', color='gray', lw=0.5),fontsize=8)
# 路径曲线
plt.title(f"Optimal TSP Path (Distance: {best_dist:.2f})", fontsize=12)
plt.xlabel("X Coordinate", fontsize=10)
plt.ylabel("Y Coordinate", fontsize=10)
plt.grid(True, linestyle='--', alpha=0.3)
plt.legend(loc='upper right', fontsize=9)
plt.tight_layout()
plt.show()# 绘制收敛曲线
plt.figure(figsize=(10, 6))
plt.plot(history, linewidth=1)
plt.title("Convergence Curve of Tabu Search")
plt.xlabel("Iteration")
plt.ylabel("Best Distance")
plt.grid(True)
plt.show()

相关文章:

  • 央国企迁移国产数据库:数据迁移5步法与4项管理准则
  • 【周输入】517周阅读推荐-3
  • opencv + jpeg_turbo(启用SIMD加速)
  • 【每天一个知识点】LangChain
  • 消息队列从入门到实战:用外卖订单理解高并发系统的核心设计
  • AMBA-AHB的控制信号
  • 水域应急救援可视化平台
  • Ai书签管理工具开发全记录(一):项目总览与技术蓝图
  • c/c++的opencv图像金字塔缩放
  • 在 Linux 中,目录权限,mkdir -m 选项,用法
  • 扇形 圆形 面积公式
  • java读取excel数据中字段是否为金额格式
  • 图像锐化和图像运算
  • Vue CLI创建vue项目,安装插件
  • 前端面试准备-1
  • 算法 Arrays.sort()函数自定义排序(Comparator 接口)
  • XCTF-web-fileinclude
  • 在 MATLAB 2015a 中如何调用 Python
  • Golang | gRPC demo
  • JS 逆向太费劲,试试 JS 注入!
  • 万表网欧米茄/网站seo方案撰写
  • 网站托管服务合同/网络营销课程介绍
  • 个人网站建设 优帮云/广东新闻今日大件事
  • 提卡的网站怎么做/永久免费的电销外呼系统
  • 做订购网站/线上商城推广软文
  • 怎么制作网站图片不显示/北京seo顾问服务公司