问题描述
旅行商问题(Travelling Salesman Problem, 简记TSP,亦称货郎担问题):设有n个城市和距离矩阵D=[dij],其中dij表示城市i到城市j的距离,i,j=1,2 … n,则问题是要找出遍访每个城市恰好一次的一条回路并使其路径长度为最短。
一、动态规划解决旅行商问题
要使用动态规划,需要问题本身有最优子结构,我们需要找到要解决的问题的子问题。题目要求,从0(a)出发,经过[1(b),2©,3(d)]这几个城市,然后回到0,使得花费最少。要实现这个要求,需要从下面三个实现方案中选择花费最少的方案。
从0出发,到1,然后再从1出发,经过[2,3]这几个城市,然后回到0,使得花费最少。
从0出发,到2,然后再从2出发,经过[1,3]这几个城市,然后回到0,使得花费最少。
从0出发,到3,然后再从3出发,经过[1,2]这几个城市,然后回到0,使得花费最少。
可以发现,三个小的解决方案的最优解,构成了大的解决方案,所以这个问题具有最优子结构,可以用动态规划来实现。
设置一个二维的动态规划表dp,定义符号{1,2,3}表示经过[1,2,3]这几个城市,然后回到0。
设置一个二维数组l保存两个城市之间的距离。
那么题目就是求dp[0][{1,2,3}]。将{1,2,3}表示成二进制,就是111,对应10进制的7,所以题目是在求dp[0][7];
要求三个方案的最小值意味:
dp[0][{1,2,3}] = min{l[0][1]+dp[1][{2,3}] ,l[0][2]+dp[2][{1,3}] ,l[0][3]+dp[3][{1,2}]}
dp[1][{2,3}] = min{ l[1][2]+dp[2][{3}] ,l[1][3]+dp[3][{2}]}
dp[2][{3}] = l[2][3]+dp[3][{}]
dp[3][{}]就是从3出发,不经过任何城市,回到0的花费,所以dp[3][{}] = l[3][0]
1.1 相关代码:
#确定结点个数和权值
def prepare(self):
# 初始化dp和权值矩阵value
Max = 1000
temp = []
tmp = []
for i in range(Max):
for j in range(Max):
temp.append(0x7ffff)
tmp.append(0)
# print(temp)
self.dp.append(copy.deepcopy(temp))
self.value.append(copy.deepcopy(tmp))
temp.clear()
tmp.clear()
self.value[0][1] = self.value[1][0] = 2
self.value[0][2] = self.value[2][0] = 5
self.value[0][3] = self.value[3][0] = 7
self.value[1][2] = self.value[2][1] = 8
self.value[1][3] = self.value[3][1] = 3
self.value[2][3] = self.value[3][2] = 1
#动态规划求解旅行商问题
'''
dp[0][{1,2,3}] = min{l[0][1]+dp[1][{2,3}] ,l[0][2]+dp[2][{1,3}] ,l[0][3]+dp[3][{1,2}]}
dp[1][{2,3}] = min{ l[1][2]+dp[2][{3}] ,l[1][3]+dp[3][{2}]}
dp[2][{3}] = l[2][3]+dp[3][{}] = l[2][3]+ l[3][0]
'''
def dp_method(self):
#初始化dp数组第一列
for i in range(self.n):
self.dp[i][0] = self.value[i][0]
col = 1<< self.n - 1
print(col)
for j in range(1,col):
for i in range(self.n):
#i为走过的城市,如果j中包含该城市,就跳过
if i != 0 and j >> (i-1) & 1:
continue
'''
从2出发,要去{1,3}。
先看去1的路,去了1集合{1,3}中只剩下{3} ,{3}对应4,所以要求的dp表就是dp[1][4],这个4可以通过(101) ^ (1)得到,(1) = 1<<(1-1)
再看去2的路,5 = 101的第二位是0,所以不能去2。判断第二位为1,用(5>>(2-1)) &1==1。而且也由于从2出发,就更不能去了。
最后看去3的路,去了3集合{1,3}中只剩下{1},{1}对应这1,所以要求的dp表就是dp[3][1],1通过(101) ^ (100)得到。(100) = 1<<(3-1)
'''
for k in range(1,col):
#判断是否要经过k城市,不能经过就跳过
if j>>(k-1)&1 == 0:
continue
if self.dp[i][j] > self.value[i][k] + self.dp[k][j ^ (1 << (k-1))]:
self.dp[i][j] = self.value[i][k] + self.dp[k][j ^ (1 << (k - 1))]
1.2 运行结果:

二、爬山法解决旅行商问题
爬山算法是一种局部择优的方法,采用启发式方法,是对深度优先搜索的一种改进,它利用反馈信息帮助生成解的决策。 该算法每次从当前解的临近解空间中选择一个最优解作为当前解,直到达到一个局部最优解。属于人工智能算法的一种。
爬山算法实现很简单,其主要缺点是会陷入局部最优解,而不一定能搜索到全局最优解。
#爬山算法思路
#1、随机生成一个城市排序,计算该顺序下所耗总路程,并假设为最短距离
#2、设定爬山算法总代数
#3、若当前代数小于总次数,则随机交换两个城市的顺序,计算总路程,否则第5步
#4、若该次总路程小于假设的最短距离,则更新最短距离,否则继续第3步
#5、输出此时的最短距离,和城市访问顺序
2.1 相关代码:
def prepare_for_hill_climbing_and_genetic_algorithm(self):
#读取34个城市坐标
#存放城市名和坐标的字典,valuse存放的是元组
data = xlrd.open_workbook('中国省会城市坐标.xlsx')
table = data.sheets()[0]
row = table.nrows
#城市数量
self.num = row
self.citys = {
}
self.citys_name = []
for i in range(row):
self.citys[table.cell_value(i,0)] = (table

本文探讨了如何使用动态规划、爬山算法(包括随机爬山法和遗传算法)解决旅行商问题。动态规划适合少量城市,但面对大量城市时内存消耗大;爬山算法易陷局部最优,而遗传算法在解决复杂TSP问题上更具优势。通过实例展示了如何在实际场景中运用这些算法。
最低0.47元/天 解锁文章
4405

被折叠的 条评论
为什么被折叠?



