474 lines
15 KiB
Python
474 lines
15 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
|
|
|
|
def tsp_06(distances: np.ndarray) -> List[int]:
|
|
"""
|
|
基于邻域矩阵的TSP求解算法
|
|
Args:
|
|
distances: 距离矩阵
|
|
Returns:
|
|
访问顺序列表
|
|
"""
|
|
import numpy as np
|
|
|
|
def generate_neighborhood_matrix(distance_matrix):
|
|
n = len(distance_matrix)
|
|
neighborhood_matrix = np.zeros((n, n), dtype=int)
|
|
for i in range(n):
|
|
sorted_indices = np.argsort(distance_matrix[i])
|
|
neighborhood_matrix[i] = sorted_indices
|
|
return neighborhood_matrix
|
|
|
|
def select_next_node(current_node: int, destination_node: int, unvisited_nodes: np.ndarray, distance_matrix: np.ndarray) -> int:
|
|
"""
|
|
Design a novel algorithm to select the next node in each step.
|
|
|
|
Args:
|
|
current_node: ID of the current node.
|
|
destination_node: ID of the destination node.
|
|
unvisited_nodes: Array of IDs of unvisited nodes.
|
|
distance_matrix: Distance matrix of nodes.
|
|
|
|
Return:
|
|
ID of the next node to visit.
|
|
"""
|
|
current_dist = distance_matrix[current_node, unvisited_nodes]
|
|
dest_dist = distance_matrix[destination_node, unvisited_nodes]
|
|
|
|
# Normalize distances
|
|
norm_current = current_dist / np.max(current_dist)
|
|
norm_dest = dest_dist / np.max(dest_dist)
|
|
|
|
# Weighted score (higher weight for proximity to current node)
|
|
score = 0.7 * norm_current + 0.3 * (1 - norm_dest)
|
|
|
|
return unvisited_nodes[np.argmin(score)]
|
|
|
|
n = len(distances)
|
|
neighbor_matrix = generate_neighborhood_matrix(distances)
|
|
route = np.zeros(n, dtype=int)
|
|
current_node = 0
|
|
destination_node = 0
|
|
for i in range(1, n - 1):
|
|
near_nodes = neighbor_matrix[current_node][1:]
|
|
mask = ~np.isin(near_nodes, route[:i])
|
|
unvisited_near_nodes = near_nodes[mask]
|
|
next_node = select_next_node(current_node, destination_node, unvisited_near_nodes, distances)
|
|
current_node = next_node
|
|
route[i] = current_node
|
|
mask = ~np.isin(np.arange(n), route[:n - 1])
|
|
last_node = np.arange(n)[mask]
|
|
route[n - 1] = last_node[0]
|
|
return route.tolist()
|