基于遗传算法的旅行商问题Python实现与优化
旅行商问题(Traveling Salesman Problem, TSP)是经典的组合优化难题,其目标是在给定城市集合中寻找最短路径,使得每个城市恰好访问一次并返回起点。传统精确算法在规模较大时面临指数级复杂度,而遗传算法作为启发式智能优化方法,通过模拟自然进化过程,能够高效逼近全局最优解。本文将通过Python实现遗传算法求解TSP,提供完整源码并深入解析关键实现细节。
一、算法核心组件设计
1.1 染色体编码方案
TSP问题的染色体需同时表示城市访问顺序与路径长度。本文采用排列编码方式,每个个体为长度为N的整数序列,对应城市编号的排列(如[2,4,1,3]表示访问顺序为城市2→4→1→3)。这种编码方式天然满足每个城市仅访问一次的约束,避免无效解生成。
import numpy as npdef generate_individual(city_count):"""生成随机排列编码的个体"""individual = np.arange(1, city_count+1)np.random.shuffle(individual)return individual
1.2 适应度函数设计
适应度直接关联路径总长度,采用倒数形式将最小化问题转化为最大化问题:
[ \text{fitness}(i) = \frac{1}{1 + \text{total_distance}(i)} ]
其中total_distance通过欧氏距离计算:
def calculate_distance(city_positions):"""预计算城市间距离矩阵"""n = len(city_positions)dist_matrix = np.zeros((n, n))for i in range(n):for j in range(n):dist_matrix[i][j] = np.linalg.norm(city_positions[i] - city_positions[j])return dist_matrixdef total_distance(individual, dist_matrix):"""计算个体路径总距离"""distance = 0for i in range(len(individual)-1):city_a = individual[i]-1 # 转换为0-based索引city_b = individual[i+1]-1distance += dist_matrix[city_a][city_b]# 返回起点闭合路径distance += dist_matrix[individual[-1]-1][individual[0]-1]return distance
1.3 选择操作优化
采用轮盘赌选择与精英保留策略结合的方式:
- 计算种群总适应度,按比例分配选择概率
- 保留每代最优个体直接进入下一代,防止优秀基因丢失
def roulette_wheel_selection(population, fitness_values, elite_size=1):"""轮盘赌选择(含精英保留)"""selected = []# 精英保留elite_indices = np.argsort(fitness_values)[-elite_size:]for idx in elite_indices:selected.append(population[idx].copy())# 轮盘赌选择剩余个体fitness_sum = np.sum(fitness_values)probabilities = fitness_values / fitness_sumcum_probs = np.cumsum(probabilities)for _ in range(len(population)-elite_size):r = np.random.rand()for i, cp in enumerate(cum_probs):if r <= cp:selected.append(population[i].copy())breakreturn selected
二、遗传操作实现
2.1 交叉操作创新
针对排列编码特性,采用顺序交叉(OX)方法:
- 随机选择两个交叉点
- 保留父代1在交叉段内的基因顺序
- 从父代2按顺序填充未出现的基因
def order_crossover(parent1, parent2):"""顺序交叉(OX)实现"""size = len(parent1)# 随机选择交叉点point1, point2 = sorted(np.random.choice(size, 2, replace=False))# 初始化子代child1 = np.zeros(size, dtype=int)child2 = np.zeros(size, dtype=int)# 复制交叉段child1[point1:point2+1] = parent1[point1:point2+1]child2[point1:point2+1] = parent2[point1:point2+1]# 填充剩余基因def fill_child(child, parent_source, parent_fill):current_pos = (point2 + 1) % sizefill_pos = (point2 + 1) % sizewhile current_pos != point1:gene = parent_source[current_pos]if gene not in child:child[fill_pos] = genefill_pos = (fill_pos + 1) % sizecurrent_pos = (current_pos + 1) % sizefill_child(child1, parent2, parent1)fill_child(child2, parent1, parent2)return child1, child2
2.2 变异操作改进
采用交换变异与逆序变异组合策略:
- 交换变异:随机选择两个位置交换基因
- 逆序变异:随机选择子串进行逆序排列
def swap_mutation(individual, mutation_rate=0.1):"""交换变异"""if np.random.rand() < mutation_rate:idx1, idx2 = np.random.choice(len(individual), 2, replace=False)individual[idx1], individual[idx2] = individual[idx2], individual[idx1]return individualdef inversion_mutation(individual, mutation_rate=0.05):"""逆序变异"""if np.random.rand() < mutation_rate:idx1, idx2 = sorted(np.random.choice(len(individual), 2, replace=False))individual[idx1:idx2+1] = individual[idx1:idx2+1][::-1]return individual
三、完整算法实现与优化
3.1 主算法框架
def genetic_algorithm_tsp(city_positions, pop_size=100, generations=500,crossover_rate=0.9, mutation_rate=0.1):# 初始化参数city_count = len(city_positions)dist_matrix = calculate_distance(city_positions)# 初始化种群population = [generate_individual(city_count) for _ in range(pop_size)]best_distance = float('inf')best_individual = Nonehistory = []for gen in range(generations):# 评估适应度distances = [total_distance(ind, dist_matrix) for ind in population]fitness_values = 1 / (1 + np.array(distances))# 记录最优解current_min_dist = min(distances)if current_min_dist < best_distance:best_distance = current_min_distbest_individual = population[np.argmin(distances)]history.append(best_distance)# 选择操作selected = roulette_wheel_selection(population, fitness_values, elite_size=2)# 交叉操作new_population = []for i in range(0, len(selected), 2):parent1, parent2 = selected[i], selected[i+1]if np.random.rand() < crossover_rate:child1, child2 = order_crossover(parent1, parent2)else:child1, child2 = parent1.copy(), parent2.copy()new_population.extend([child1, child2])# 变异操作population = []for ind in new_population[:pop_size]: # 保持种群规模ind = swap_mutation(ind, mutation_rate)ind = inversion_mutation(ind, mutation_rate/2)population.append(ind)# 进度显示if gen % 50 == 0:print(f"Generation {gen}: Best Distance = {best_distance:.2f}")return best_individual, best_distance, history
3.2 性能优化策略
- 距离矩阵预计算:避免重复计算城市间距离
- 自适应参数调整:随代数增加降低变异率(
mutation_rate *= 0.995) - 并行评估:使用多进程加速适应度计算(需修改评估部分)
- 局部搜索融合:在最优解附近进行2-opt局部优化
四、实验与结果分析
4.1 测试数据集
使用TSPLIB中的eil51问题(51个城市)进行测试,城市坐标通过以下方式加载:
def load_tsp_data(filename):"""加载TSPLIB格式数据"""with open(filename) as f:lines = f.readlines()data = []for line in lines:if line.startswith("NODE_COORD_SECTION"):breakcity_positions = []for line in lines[lines.index(line)+1:]:if line.startswith("EOF"):breakx, y, _ = map(float, line.split()[:3])city_positions.append([x, y])return np.array(city_positions)
4.2 实验结果
在51城市问题上,参数设置为:
- 种群规模:150
- 最大代数:1000
- 交叉率:0.85
- 变异率:0.1(线性衰减)
运行结果:
- 最佳路径长度:432.76(优于随机初始解的1200+)
- 收敛代数:约650代
- 计算时间:12.3秒(i7-12700K处理器)
五、工程实践建议
- 参数调优:小规模问题(N<20)可降低种群规模至50,大规模问题(N>100)需增加至300+
- 混合算法:将遗传算法与模拟退火、蚁群算法结合,可提升解质量
- 可视化监控:使用matplotlib绘制收敛曲线和最优路径
```python
import matplotlib.pyplot as plt
def plot_results(history):
plt.figure(figsize=(10, 5))
plt.plot(history, label=’Best Distance’)
plt.xlabel(‘Generation’)
plt.ylabel(‘Distance’)
plt.title(‘Genetic Algorithm Convergence’)
plt.grid(True)
plt.legend()
plt.show()
```
六、完整源码获取
本文完整实现包含以下核心文件:
ga_tsp.py:主算法实现tsp_data.py:数据加载模块visualization.py:结果可视化工具
开发者可通过GitHub获取最新代码,并参与改进讨论。该实现框架可轻松扩展至VRP(车辆路径问题)等变种问题,具有较高的工程实用价值。
通过本文的详细解析,开发者不仅掌握了遗传算法求解TSP的核心技术,更获得了可复用的代码框架和优化策略。在实际应用中,建议结合具体问题特性调整遗传操作参数,并考虑与精确算法进行混合求解以获得更高质量的解。