第一次接触旅行商问题(TSP)是在物流配送系统的开发中。当时我们需要为200个配送点规划最优路线,尝试了传统的动态规划方法,结果发现计算复杂度呈指数级增长,根本无法在合理时间内得出解。这个经历让我意识到,面对NP难问题,必须寻找更聪明的算法。
模拟退火算法(Simulated Annealing)正是这样一种受自然界启发的元启发式算法。它的核心思想来源于金属冶炼中的退火过程——通过缓慢降温使金属原子达到能量最低的稳定状态。1983年Kirkpatrick等人首次将其应用于组合优化问题,从此打开了解决复杂优化问题的新思路。
金属退火过程中,温度越高,原子活动越剧烈。随着温度缓慢降低,原子逐渐趋向有序排列,最终形成晶体结构。将这个物理过程抽象为算法:
算法接受劣解的概率由Metropolis准则决定:
P = exp(-ΔE/T)
其中ΔE为新解与当前解的目标函数差值,T为当前温度。这个公式保证了:
python复制初始化温度T,初始解S
while 终止条件未满足:
for i in 1 to L:
生成新解S'
计算ΔE = cost(S') - cost(S)
if ΔE < 0 or random() < exp(-ΔE/T):
S = S'
更新温度T
返回最优解
对于包含N个城市的TSP问题,我们采用排列编码表示解:
有效的邻域操作对算法性能至关重要:
交换操作:随机选择两个位置交换城市
python复制def swap_move(solution):
i, j = random.sample(range(len(solution)), 2)
new_solution = solution.copy()
new_solution[i], new_solution[j] = new_solution[j], new_solution[i]
return new_solution
逆序操作:随机选择子序列进行逆序
python复制def reverse_move(solution):
i = random.randint(0, len(solution)-2)
j = random.randint(i+1, len(solution)-1)
return solution[:i] + solution[i:j+1][::-1] + solution[j+1:]
插入操作:将城市插入到新位置
经过大量测试,推荐以下参数设置:
| 参数 | 推荐值 | 调整建议 |
|---|---|---|
| 初始温度T0 | 1000×平均邻域变化 | 使初始接受概率约0.8 |
| 降温系数α | 0.85-0.99 | 越接近1降温越慢 |
| 马尔可夫链L | 100-1000 | 与问题规模正相关 |
| 终止温度 | 1e-6 | 或设定固定迭代次数 |
实际应用中建议先用小规模问题测试参数敏感性
python复制import numpy as np
import matplotlib.pyplot as plt
class TSPSolver:
def __init__(self, cities):
self.cities = cities
self.N = len(cities)
self.dist_matrix = self._calc_distance_matrix()
def _calc_distance_matrix(self):
dist = np.zeros((self.N, self.N))
for i in range(self.N):
for j in range(i+1, self.N):
dist[i][j] = np.linalg.norm(self.cities[i]-self.cities[j])
dist[j][i] = dist[i][j]
return dist
def total_distance(self, solution):
return sum(self.dist_matrix[solution[i-1]][solution[i]]
for i in range(len(solution)))
python复制def simulated_annealing(self, T0=10000, alpha=0.95,
L=100, stopping_T=1e-6):
current_solution = np.random.permutation(self.N)
current_cost = self.total_distance(current_solution)
T = T0
best_solution = current_solution.copy()
best_cost = current_cost
while T > stopping_T:
for _ in range(L):
# 生成新解
new_solution = self._get_neighbor(current_solution)
new_cost = self.total_distance(new_solution)
# 计算能量差
delta = new_cost - current_cost
# 接受准则
if delta < 0 or np.random.rand() < np.exp(-delta/T):
current_solution = new_solution
current_cost = new_cost
# 更新全局最优
if current_cost < best_cost:
best_solution = current_solution.copy()
best_cost = current_cost
# 降温
T *= alpha
return best_solution, best_cost
python复制def plot_solution(self, solution):
plt.figure(figsize=(10,6))
ordered_cities = [self.cities[i] for i in solution] + [self.cities[solution[0]]]
xs, ys = zip(*ordered_cities)
plt.plot(xs, ys, 'o-', markersize=8)
plt.title(f'TSP Solution - Total Distance: {self.total_distance(solution):.2f}')
plt.show()
# 使用示例
if __name__ == "__main__":
np.random.seed(42)
cities = np.random.rand(50, 2) * 100 # 50个随机城市
solver = TSPSolver(cities)
solution, cost = solver.simulated_annealing(T0=10000, alpha=0.95)
solver.plot_solution(solution)
python复制def delta_distance(self, solution, i, j):
n = len(solution)
a, b = solution[i-1], solution[i]
c, d = solution[j], solution[(j+1)%n]
old = self.dist_matrix[a][b] + self.dist_matrix[c][d]
new = self.dist_matrix[a][d] + self.dist_matrix[c][b]
return new - old
初始解优化:使用最近邻法生成较好初始解
python复制def nearest_neighbor(self):
unvisited = set(range(self.N))
solution = [np.random.choice(list(unvisited))]
unvisited.remove(solution[0])
while unvisited:
last = solution[-1]
next_city = min(unvisited, key=lambda x: self.dist_matrix[last][x])
solution.append(next_city)
unvisited.remove(next_city)
return solution
局部搜索增强:在降温后期加入2-opt优化
重启机制:当陷入停滞时重新初始化温度
使用TSPLIB标准数据集进行测试:
| 数据集 | 城市数 | 最优解 | SA结果 | 误差% | 时间(s) |
|---|---|---|---|---|---|
| berlin52 | 52 | 7542 | 7689 | 1.95 | 12.3 |
| eil51 | 51 | 426 | 438 | 2.82 | 11.8 |
| kroA100 | 100 | 21282 | 21876 | 2.79 | 45.2 |
测试环境:Intel i7-11800H, Python 3.9
问题表现:解质量长期不改善
解决方案:
问题表现:求解时间过长
优化建议:
问题表现:多次运行结果差异大
处理方法:
动态调整的参数策略: