414 lines
13 KiB
Python
414 lines
13 KiB
Python
![]() |
import numpy as np
|
||
|
from typing import List
|
||
|
|
||
|
def greedy_tsp(distances: np.ndarray) -> List[int]:
|
||
|
"""贪心算法求解TSP
|
||
|
|
||
|
Args:
|
||
|
distances: 距离矩阵
|
||
|
|
||
|
Returns:
|
||
|
访问顺序列表
|
||
|
"""
|
||
|
n = len(distances)
|
||
|
unvisited = set(range(1, n)) # 未访问的城市集合
|
||
|
path = [0] # 从城市0开始
|
||
|
|
||
|
while unvisited:
|
||
|
last = path[-1]
|
||
|
# 找到距离最后一个城市最近的未访问城市
|
||
|
next_city = min(unvisited, key=lambda x: distances[last][x])
|
||
|
path.append(next_city)
|
||
|
unvisited.remove(next_city)
|
||
|
|
||
|
return path
|
||
|
|
||
|
def nearest_neighbor_tsp(distances: np.ndarray) -> List[int]:
|
||
|
"""最近邻算法求解TSP
|
||
|
|
||
|
Args:
|
||
|
distances: 距离矩阵
|
||
|
|
||
|
Returns:
|
||
|
访问顺序列表
|
||
|
"""
|
||
|
n = len(distances)
|
||
|
unvisited = set(range(1, n))
|
||
|
path = [0] # 从城市0开始
|
||
|
|
||
|
while unvisited:
|
||
|
curr = path[-1]
|
||
|
# 找到距离当前城市最近的未访问城市
|
||
|
next_city = min(unvisited, key=lambda x: distances[curr][x])
|
||
|
path.append(next_city)
|
||
|
unvisited.remove(next_city)
|
||
|
|
||
|
return path
|
||
|
|
||
|
def insertion_tsp(distances: np.ndarray) -> List[int]:
|
||
|
"""插入法求解TSP
|
||
|
|
||
|
Args:
|
||
|
distances: 距离矩阵
|
||
|
|
||
|
Returns:
|
||
|
访问顺序列表
|
||
|
"""
|
||
|
n = len(distances)
|
||
|
# 初始化包含前两个城市的回路
|
||
|
path = [0, np.argmin(distances[0][1:])+1]
|
||
|
unvisited = set(range(1, n)) - {path[1]}
|
||
|
|
||
|
while unvisited:
|
||
|
min_increase = float('inf')
|
||
|
best_pos = -1
|
||
|
best_city = -1
|
||
|
|
||
|
# 尝试将每个未访问城市插入到当前路径的每个可能位置
|
||
|
for city in unvisited:
|
||
|
for i in range(len(path)):
|
||
|
j = (i + 1) % len(path)
|
||
|
# 计算插入后的路径增量
|
||
|
increase = (distances[path[i]][city] +
|
||
|
distances[city][path[j]] -
|
||
|
distances[path[i]][path[j]])
|
||
|
if increase < min_increase:
|
||
|
min_increase = increase
|
||
|
best_pos = i + 1
|
||
|
best_city = city
|
||
|
|
||
|
# 将最佳城市插入到最佳位置
|
||
|
path.insert(best_pos, best_city)
|
||
|
unvisited.remove(best_city)
|
||
|
|
||
|
return path
|
||
|
|
||
|
def tsp_01(distances: np.ndarray) -> List[int]:
|
||
|
"""动态规划+2-opt优化求解TSP(内存优化版)
|
||
|
|
||
|
Args:
|
||
|
distances: 距离矩阵
|
||
|
|
||
|
Returns:
|
||
|
访问顺序列表
|
||
|
"""
|
||
|
from itertools import combinations
|
||
|
from typing import List
|
||
|
import numpy as np
|
||
|
|
||
|
n = len(distances)
|
||
|
# 使用字典存储dp表,只保存必要的状态
|
||
|
dp = {}
|
||
|
dp[(1, 0)] = 0
|
||
|
|
||
|
# 动态规划部分,使用位运算加速
|
||
|
for size in range(2, n + 1):
|
||
|
# 提前计算所有可能的子集
|
||
|
subsets = list(combinations(range(n), size))
|
||
|
for subset in subsets:
|
||
|
mask = sum(1 << i for i in subset)
|
||
|
for v in subset:
|
||
|
if v == 0:
|
||
|
continue
|
||
|
prev_mask = mask ^ (1 << v)
|
||
|
min_dist = float('inf')
|
||
|
# 只遍历必要的前驱节点
|
||
|
for u in subset:
|
||
|
if u != v and (prev_mask & (1 << u)):
|
||
|
curr_dist = dp.get((prev_mask, u), float('inf')) + distances[u][v]
|
||
|
min_dist = min(min_dist, curr_dist)
|
||
|
if min_dist != float('inf'):
|
||
|
dp[(mask, v)] = min_dist
|
||
|
|
||
|
# 重建路径,使用贪心策略
|
||
|
final_mask = (1 << n) - 1
|
||
|
last_city = min(range(1, n),
|
||
|
key=lambda u: dp.get((final_mask, u), float('inf')) + distances[u][0])
|
||
|
path = [0]
|
||
|
curr_mask = final_mask
|
||
|
curr_city = last_city
|
||
|
|
||
|
# 使用集合加速查找
|
||
|
remaining = set(range(n))
|
||
|
remaining.remove(0)
|
||
|
|
||
|
while remaining:
|
||
|
path.append(curr_city)
|
||
|
remaining.remove(curr_city)
|
||
|
prev_mask = curr_mask ^ (1 << curr_city)
|
||
|
curr_city = min(remaining,
|
||
|
key=lambda u: dp.get((prev_mask, u), float('inf')) + distances[u][curr_city])
|
||
|
curr_mask = prev_mask
|
||
|
|
||
|
path.append(curr_city)
|
||
|
|
||
|
# 优化版2-opt
|
||
|
improved = True
|
||
|
best_dist = float('inf')
|
||
|
while improved:
|
||
|
improved = False
|
||
|
curr_dist = sum(distances[path[i]][path[i+1]] for i in range(n-1)) + distances[path[-1]][path[0]]
|
||
|
if curr_dist < best_dist:
|
||
|
best_dist = curr_dist
|
||
|
for i in range(1, n-2):
|
||
|
for j in range(i+2, n):
|
||
|
delta = (distances[path[i-1]][path[j-1]] + distances[path[i]][path[j]] -
|
||
|
distances[path[i-1]][path[i]] - distances[path[j-1]][path[j]])
|
||
|
if delta < 0:
|
||
|
path[i:j] = reversed(path[i:j])
|
||
|
improved = True
|
||
|
break
|
||
|
if improved:
|
||
|
break
|
||
|
|
||
|
return path
|
||
|
|
||
|
def tsp_02(distances: np.ndarray) -> List[int]:
|
||
|
"""动态规划+简化遗传算法求解TSP
|
||
|
|
||
|
Args:
|
||
|
distances: 距离矩阵
|
||
|
|
||
|
Returns:
|
||
|
访问顺序列表
|
||
|
"""
|
||
|
from typing import List
|
||
|
import numpy as np
|
||
|
import random
|
||
|
|
||
|
n = len(distances)
|
||
|
# 使用贪心算法生成初始解
|
||
|
path = list(range(n))
|
||
|
for i in range(n-1):
|
||
|
min_j = min(range(i+1, n),
|
||
|
key=lambda j: distances[path[i]][path[j]])
|
||
|
path[i+1], path[min_j] = path[min_j], path[i+1]
|
||
|
|
||
|
# 简化的遗传算法
|
||
|
pop_size = 20 # 减小种群大小
|
||
|
generations = 100 # 减少迭代次数
|
||
|
population = [path[:]]
|
||
|
|
||
|
# 生成初始种群
|
||
|
for _ in range(pop_size - 1):
|
||
|
new_path = path[:]
|
||
|
i, j = random.sample(range(1, n), 2)
|
||
|
new_path[i], new_path[j] = new_path[j], new_path[i]
|
||
|
population.append(new_path)
|
||
|
|
||
|
for _ in range(generations):
|
||
|
# 选择最优的一半
|
||
|
population.sort(key=lambda x: sum(distances[x[i]][x[i+1]]
|
||
|
for i in range(n-1)) + distances[x[-1]][x[0]])
|
||
|
population = population[:pop_size//2]
|
||
|
|
||
|
# 通过交叉生成新解
|
||
|
while len(population) < pop_size:
|
||
|
p1, p2 = random.sample(population, 2)
|
||
|
cut = random.randint(1, n-2)
|
||
|
child = p1[:cut]
|
||
|
child.extend(x for x in p2 if x not in child)
|
||
|
population.append(child)
|
||
|
|
||
|
return min(population,
|
||
|
key=lambda x: sum(distances[x[i]][x[i+1]]
|
||
|
for i in range(n-1)) + distances[x[-1]][x[0]])
|
||
|
|
||
|
def tsp_03(distances: np.ndarray) -> List[int]:
|
||
|
"""快速模拟退火求解TSP
|
||
|
|
||
|
Args:
|
||
|
distances: 距离矩阵
|
||
|
|
||
|
Returns:
|
||
|
访问顺序列表
|
||
|
"""
|
||
|
from typing import List
|
||
|
import numpy as np
|
||
|
import random
|
||
|
import math
|
||
|
|
||
|
n = len(distances)
|
||
|
# 使用贪心算法生成初始解
|
||
|
route = list(range(n))
|
||
|
for i in range(n-1):
|
||
|
min_j = min(range(i+1, n),
|
||
|
key=lambda j: distances[route[i]][route[j]])
|
||
|
route[i+1], route[min_j] = route[min_j], route[i+1]
|
||
|
|
||
|
current_cost = sum(distances[route[i]][route[i+1]]
|
||
|
for i in range(n-1)) + distances[route[-1]][route[0]]
|
||
|
|
||
|
# 快速降温的模拟退火
|
||
|
temp = 100.0
|
||
|
cooling = 0.95
|
||
|
iterations = 50 # 减少迭代次数
|
||
|
|
||
|
while temp > 0.1:
|
||
|
for _ in range(iterations):
|
||
|
i, j = random.sample(range(1, n), 2)
|
||
|
new_route = route[:]
|
||
|
new_route[i], new_route[j] = new_route[j], new_route[i]
|
||
|
|
||
|
new_cost = sum(distances[new_route[k]][new_route[k+1]]
|
||
|
for k in range(n-1)) + distances[new_route[-1]][new_route[0]]
|
||
|
|
||
|
if new_cost < current_cost or random.random() < math.exp((current_cost - new_cost) / temp):
|
||
|
route = new_route
|
||
|
current_cost = new_cost
|
||
|
|
||
|
temp *= cooling
|
||
|
|
||
|
return route
|
||
|
|
||
|
def two_opt(route, distances):
|
||
|
"""简化版2-opt优化"""
|
||
|
improved = True
|
||
|
while improved:
|
||
|
improved = False
|
||
|
for i in range(1, len(route) - 2):
|
||
|
if improved:
|
||
|
break
|
||
|
for j in range(i + 2, len(route)):
|
||
|
old_dist = (distances[route[i-1]][route[i]] +
|
||
|
distances[route[j-1]][route[j]])
|
||
|
new_dist = (distances[route[i-1]][route[j-1]] +
|
||
|
distances[route[i]][route[j]])
|
||
|
if new_dist < old_dist:
|
||
|
route[i:j] = reversed(route[i:j])
|
||
|
improved = True
|
||
|
break
|
||
|
return route
|
||
|
|
||
|
def calculate_cost(route, distances):
|
||
|
"""计算路径总成本"""
|
||
|
return sum(distances[route[i]][route[(i + 1) % len(route)]]
|
||
|
for i in range(len(route)))
|
||
|
|
||
|
def tsp_04(distances):
|
||
|
"""
|
||
|
高性能TSP求解算法,结合多种优化策略:
|
||
|
1. 贪心构造初始解
|
||
|
2. 快速模拟退火
|
||
|
3. 2-opt局部搜索
|
||
|
"""
|
||
|
import random
|
||
|
import math
|
||
|
n = len(distances)
|
||
|
|
||
|
# 贪心构造初始解
|
||
|
unvisited = set(range(1, n))
|
||
|
current = 0
|
||
|
route = [0]
|
||
|
while unvisited:
|
||
|
next_city = min(unvisited, key=lambda x: distances[current][x])
|
||
|
route.append(next_city)
|
||
|
unvisited.remove(next_city)
|
||
|
current = next_city
|
||
|
|
||
|
# 快速模拟退火
|
||
|
temp = 100.0
|
||
|
cooling = 0.95
|
||
|
iterations = 30
|
||
|
|
||
|
current_cost = calculate_cost(route, distances)
|
||
|
best_route = route[:]
|
||
|
best_cost = current_cost
|
||
|
|
||
|
while temp > 0.1:
|
||
|
for _ in range(iterations):
|
||
|
# 随机交换两个城市
|
||
|
i, j = random.sample(range(1, n), 2)
|
||
|
new_route = route[:]
|
||
|
new_route[i], new_route[j] = new_route[j], new_route[i]
|
||
|
|
||
|
new_cost = calculate_cost(new_route, distances)
|
||
|
|
||
|
if new_cost < current_cost or random.random() < math.exp((current_cost - new_cost) / temp):
|
||
|
route = new_route
|
||
|
current_cost = new_cost
|
||
|
if new_cost < best_cost:
|
||
|
best_route = new_route[:]
|
||
|
best_cost = new_cost
|
||
|
|
||
|
temp *= cooling
|
||
|
|
||
|
# 2-opt局部优化
|
||
|
best_route = two_opt(best_route, distances)
|
||
|
|
||
|
return best_route
|
||
|
|
||
|
def tsp_05(distances):
|
||
|
"""
|
||
|
基于插入法的TSP优化算法:
|
||
|
1. 插入法构造初始解
|
||
|
2. 快速模拟退火
|
||
|
3. 2-opt局部搜索
|
||
|
"""
|
||
|
import random
|
||
|
import math
|
||
|
n = len(distances)
|
||
|
|
||
|
# 插入法构造初始解
|
||
|
route = [0, 1] # 从城市0和1开始
|
||
|
unvisited = set(range(2, n))
|
||
|
|
||
|
while unvisited:
|
||
|
# 找到最佳插入位置和待插入城市
|
||
|
min_increase = float('inf')
|
||
|
best_pos = 0
|
||
|
best_city = 0
|
||
|
|
||
|
for city in unvisited:
|
||
|
# 尝试所有可能的插入位置
|
||
|
for i in range(len(route)):
|
||
|
if i == len(route) - 1:
|
||
|
increase = (distances[route[i]][city] +
|
||
|
distances[city][route[0]] -
|
||
|
distances[route[i]][route[0]])
|
||
|
else:
|
||
|
increase = (distances[route[i]][city] +
|
||
|
distances[city][route[i+1]] -
|
||
|
distances[route[i]][route[i+1]])
|
||
|
|
||
|
if increase < min_increase:
|
||
|
min_increase = increase
|
||
|
best_pos = i + 1
|
||
|
best_city = city
|
||
|
|
||
|
# 在最佳位置插入城市
|
||
|
route.insert(best_pos, best_city)
|
||
|
unvisited.remove(best_city)
|
||
|
|
||
|
# 快速模拟退火
|
||
|
temp = 100.0
|
||
|
cooling = 0.95
|
||
|
iterations = 30
|
||
|
|
||
|
current_cost = calculate_cost(route, distances)
|
||
|
best_route = route[:]
|
||
|
best_cost = current_cost
|
||
|
|
||
|
while temp > 0.1:
|
||
|
for _ in range(iterations):
|
||
|
# 随机交换两个城市
|
||
|
i, j = random.sample(range(1, n), 2)
|
||
|
new_route = route[:]
|
||
|
new_route[i], new_route[j] = new_route[j], new_route[i]
|
||
|
|
||
|
new_cost = calculate_cost(new_route, distances)
|
||
|
|
||
|
if new_cost < current_cost or random.random() < math.exp((current_cost - new_cost) / temp):
|
||
|
route = new_route
|
||
|
current_cost = new_cost
|
||
|
if new_cost < best_cost:
|
||
|
best_route = new_route[:]
|
||
|
best_cost = new_cost
|
||
|
|
||
|
temp *= cooling
|
||
|
|
||
|
# 2-opt局部优化
|
||
|
best_route = two_opt(best_route, distances)
|
||
|
|
||
|
return best_route
|