用SA算法求解旅行商问题
学号:23020201153794
系别:计算机科学系
课程名称:计算智能
一、问题描述
旅行商问题(Travelling Salesman Problem, 简记TSP,亦称货郎担问题):设有n个城市和距离矩阵D=[dij],其中dij表示城市i到城市j的距离,i,j=1,2 … n,则问题是要找出遍访每个城市恰好一次的一条回路并使其路径长度为最短。
二、问题分析
1.对TSP的分析
旅行商问题属于所谓的NP完全问题。精确的解决TSP只能通过穷举所有的路径组合,其时间复杂度是O(N!) 。而使用模拟退火算法则可以较快速算法一条近似的最优路径。
TSP的一个解可表述为一个循环排列
π= (π1, π2, π3 … πn),即 π1 → π2 → … → πn → π1。 有(n-1)!/2 种不同方案,若使用穷举法,当n很大时计算量是不可接受的。旅行商问题综合了一大类组合优化问题的典型特征,属于NP难题,不能在多项式时间内进行检验。若使用动态规划的方法时间复杂性和空间复杂性都保持为n的指数函数。
本次作业基于中国31个城市实现模拟退火算法。
2.模拟退火算法SAA
(1)SAA简介
模拟退火算法(Simulated Annealing Algorithm,简称SA算法),源于对固体退火过程的模拟,采用Metropolis接受准则,并用一组称为冷却表的参数控制算法进程,使算法在多项式时间里给出一个近似最优解。以爬山搜索为代表的局部搜索算法都是仅适用于某类组合优化问题,所得到的近似解的质量通常较差。这类方法最致命的缺点是无法跳离局部最优的“陷阱”,最终停留在某个局部最优解上。为了克服这些弱点,人们开始超脱纯数学思维,到一些自然物理过程中寻找灵感。模拟退火算法就是一个成功的典范,其思想比方法本身更为重要。
(2)SAA过程
固体退火是先将固体加热至熔化,再徐徐冷却使之凝固成规则晶体的热力学过程。退火过程必须徐徐进行,使系统在每一温度下都达到平衡态,退火过程中系统能量随温度降低趋于最小值。 冷却时若急剧降低温度,则将引起淬火效应,固体只能冷凝为非均匀的亚稳态,系统能量也不会达到最小值。
(3)SAA过程性质
若用Pi表示温度为T时固体处于能量为Ei的微观态i 的概率,计算得出Pi的分布称为Gibbs正则分布。 根据Gibbs正则分布,固体处于能量较低的状态的概率较大。也就是说,当温度降低时,那些能量
相比较低的状态最可能出现,当温度趋于0时,固体只能处于能量为最小值的基态上。
1953年Metropolis提出重要性采样法产生固体状态序列,固体状态的概率分布趋于上述Gibbs正则
分布,该方法称为Metropolis准则。通过对固体退火过程的研究,人们得到了新的启示,1982年,Kirkpatrick等人首先意识到固体退火过程与组合优化问题之间存在的类似性,将Metropolis准则引入到组合优化过程中来,得到了一种对Metropolis算法进行迭代的组合优化算法,因为这种算法模拟固体退火过程,故称之为“模拟退火算法”。
(3)Metropolis算法
模拟退火算法用Metropolis算法产生组合优化问题解的序列。并由Metropolis准则对应的转
移概率P
确定是否接受从当前解 i 到 新解 j 的转移 。 式中 t ∈ R+ 表示控制参数。开始让 t 取较大的值,
在进行足够多的转移后, 缓慢减小 t 的值,如此重复直至满足某个停止准则时算法终止。
模拟退火算法依据Metropolis准则接受新解,除接受优化解外,还在一个限定范围内接受恶化解。
开始时t值较大,可能接受较差的恶化解,随着t值的减小,只能接受较好的恶化解;当t值趋于零值
时,就不再接受任何恶化解。这就使得算法可以跳出局部最优陷阱。在算法执行期间,随着控制参数t值的减小,算法返回某个整体最优解得概率单调增大,返回某个非最优解的概率单调减小。
三、算法设计
1.源代码
import random
import time
import math
T0 = 50000. # 初始温度
T_end = 1e-8
L = 1000 # 链长每个温度迭代次数
N = 31 # 31个城市
q = 0.98 # 退货系数
city_list = [] # 一个解
# 中国31个城市坐标
city_pos = [
[1304, 2312], [3639, 1315], [4177, 2244], [3712, 1399],
[3488, 1535], [3326, 1556], [3238, 1229], [4196, 1004],
[4312, 790], [4386, 570], [3007, 1970], [2562, 1756],
[2788, 1491], [2381, 1676], [1332, 695],
[3715, 1678], [3918, 2179], [4061, 2370],
[3780, 2212], [3676, 2578], [4029, 2838],
[4263, 2931], [3429, 1908], [3507, 2367],
[3394, 2643], [3439, 3201], [2935, 3240],
[3140, 3550], [2545, 2357], [2778, 2826],
[2370, 2975]];
print('城市坐标:')
print(city_pos)
# 计算两个城市距离
def distance(city1, city2):
x1 = city1[0]
y1 = city1[1]
x2 = city2[0]
y2 = city2[1]
dis = ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) ** 0.5;
return dis;
# 计算一条路径长度
def path_len(arr):
path = 0.; # 初始化路径长度
index = arr[0]; # 定位到第一个数字(城市序号)
for i in range(0, N - 1):
index1 = (arr[i])
index2 = (arr[i + 1])
dis = distance(city_pos[index1 - 1], city_pos[index2 - 1])
path += dis
last_index = (arr[N - 1]); # 最后一个城市序号
first_index = arr[0]; # 第一个城市序号
last_dis = distance(city_pos[last_index - 1],
city_pos[first_index - 1]);
path = path + last_dis;
return path; # 返回总的路径长度
# 随机交换两个位置的方式产生新的解
def create_new():
pos1 = random.randint(0, N - 1)
pos2 = random.randint(0, N - 1)
temp = city_list[pos1];
city_list[pos1] = city_list[pos2];
city_list[pos2] = temp; # 交换两个点
# 程序运行开始计时
begin = time.time()
count = 0 # 记录降温次数
T = T0 # 初始温度
# 初始化一个解
for i in range(0, N):
city_list.append(i + 1)
# 用于保存原始解
city_list_copy = [N]
# f1为初始解目标函数值,#f2为新解目标函数值,df为二者差值
f1 = f2 = df = 0.
r = 0. # 0-1之间的随机数,用来决定是否接受新解
while (T > T_end): # 当温度低于结束温度时,退火结束
for i in range(0, L):
# 复制数组
city_list_copy = city_list[:]
create_new() # 产生新解
f1 = path_len(city_list_copy)
f2 = path_len(city_list)
df = f2 - f1
# 以下是Metropolis准则
if (df >= 0):
if (math.exp(-df / T) <= random.random()): # 保留原来的解,random.random()返回 [0.0, 1.0) 范围内的下一个随机浮点数
city_list = city_list_copy[:]
T *= q # 降温
count = count + 1
end = time.time() # 退火过程结束
duration = end - begin # 计算时间
print("模拟退火算法:\n初始温度T0=%.2f,降温系数q=%.2f,每个温度迭代%d次,共降温%d次,得到的TSP最优路径为:\n" % (T0, q, L, count));
for i in range(0, N - 1): # 输出最优路径
print("%d--->" % city_list[i], end="")
print("%d" % city_list[N - 1])
len = path_len(city_list) # 最优路径长度
print("最优路径长度为:%lf" % len)
print("程序运行耗时:%lf秒." % duration)
2.运行结果
因为模拟退火算法是随机算法,得到的结果并不是确定的,3次运行结果如下。
(1)结果1
(2)结果2
(3)结果3
四、心得体会
1.模拟退火算法是一种随机的算法,它有一定概率求得全局最优解,能较快地找出问题的近似最优解。
2.python运行速度比C++慢很多,相同的程序结构运行速度可能要慢几十倍。