引言
前文 http://t.csdnimg.cn/Q4l9X 使用局部搜索求解了一个TSP问题,如果打印信息就会发现,尽管迭代了10000次,接受新解的次数只有1
因为只有在产生更优解时才接受新解,这样就容易陷入局部最优
模拟退火概念
为了能够跳出局部最优解,模拟退火是一种比较有用的方法,简而言之就是通过一定的概率接受较差的解,这个概率通常用这个公式描述:
编码
定义模拟退火接受准则
def acceptance_function(delta_energy, temperature):
if delta_energy < 0:
return True
else:
probability = np.exp(-delta_energy / temperature)
return random.uniform(0, 1) < probability
模拟退火方法
def simulatedAnnealing():
temperature = 10000
cooling_rate = 0.995
path0 = list(range(len(dist_mat)))
best = calcTSP(path0)
iterations = 100000
for i in range(iterations):
newpath = two_swap(path0)
delta = calcTSP(newpath) - best
if acceptance_function(delta, temperature):
path0 = newpath
best = calcTSP(newpath)
temperature *= cooling_rate
return best
结论
更好?
用模拟退火(SA)和邻域搜索(LS)分别迭代100000次,发现模拟退火的结果甚至更遭
SA:
LS:
原因是初始解太优秀了,现在加上一行代码,打乱初始解:
random.shuffle(path0)
再次运行
SA:
LS:
嗯?居然还是更遭,一定是启发式算法太随机了,我们运行30次取平均值
SA:
LS:
emmm,似乎是优化了一点
关于启发式算法
笔者至今对启发式算法仍存在很多疑惑的地方,因为过程太多随机,有点像暴力求解,有各种奇奇怪怪的启发式算法变体,也说不清楚对算法的优化到底有多少,只能通过多次实验去证明。
并且影响算法的原因有很多,算子,初始化策略,迭代次数,邻域类型…
当然这只是我个人的理解,如果有什么理解错误,欢迎大佬指正。
完整代码
以下是完整代码,有错误请指正。
import copy
import pandas as pd
import numpy as np
import math
import random
filepath = './benchmark/china34.tsp'
with open(filepath, 'r') as file:
lines = file.readlines()
nodes = []
for i in range(len(lines)):
if i < 6:
continue
info = lines[i].split()
if info[0] == 'EOF':
break
coor = [float(info[1]), float(info[2])]
nodes.append(coor)
D = np.radians(nodes)
city_cnt = len(nodes)
dist_mat = np.zeros((city_cnt, city_cnt))
for i in range(city_cnt):
for j in range(city_cnt):
if i == j:
# 相同城市不允许访问
dist_mat[i][j] = 0 # 修改为0表示同一个城市的距离为0
else:
# 使用 Haversine 公式计算距离
lat1, lon1 = D[i][1], D[i][0]
lat2, lon2 = D[j][1], D[j][0]
# Haversine 公式
dlat = lat2 - lat1
dlon = lon2 - lon1
a = math.sin(dlat / 2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2)**2
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
distance = 6378.14 * c # 6378.14 是地球的半径,单位:km
dist_mat[i][j] = distance
def acceptance_function(delta_energy, temperature):
if delta_energy < 0:
return True
else:
probability = np.exp(-delta_energy / temperature)
return random.uniform(0, 1) < probability
def simulatedAnnealing():
temperature = 10000
cooling_rate = 0.995
path0 = list(range(len(dist_mat)))
random.shuffle(path0)
best = calcTSP(path0)
cnt = 0
iterations = 100000
for i in range(iterations):
newpath = two_swap(path0)
delta = calcTSP(newpath) - best
if acceptance_function(delta, temperature):
cnt += 1
path0 = newpath
best = calcTSP(newpath)
temperature *= cooling_rate
print("接受新解:", cnt, "次")
print("best value:", best)
print("best path:", path0)
return best
def two_swap(path):
cp = copy.deepcopy(path)
r = random.sample(list(range(len(cp))), 2)
cp[r[0]], cp[r[1]] = cp[r[1]], cp[r[0]]
return cp
def calcTSP(path):
sum = 0
for i in range(1, len(path)):
sum += dist_mat[path[i]][path[i - 1]]
sum += dist_mat[path[0]][path[len(path) - 1]]
return sum
# simulatedAnnealing()
sum = 0
for i in range(30):
sum += simulatedAnnealing()
print(sum / 30)