TSP问题(python)
排序问题
读取文件格式:第一行为城市数目,剩余行为各城市坐标
(1) 城市全排列,在所有解决方案中选择最好的一个(解决20个城市的时候会有困难了(见维基百科))
# 生成全排列列表的函数
def permutation(xs):
if len(xs) == 0 or len(xs) == 1:
return [xs]
result = []
for i in xs:
temp_list = xs[:]
temp_list.remove(i)
temp = permutation(temp_list)
for j in temp:
j[0:0] = [i]
result.append(j)
return result
def solve_it(input_data):
# parse the input
lines = input_data.split('\n')
nodeCount = int(lines[0])
points = []
for i in range(1, nodeCount+1):
line = lines[i]
parts = line.split()
points.append(Point(float(parts[0]), float(parts[1])))
solution_sta = list(range(0,nodeCount))
#在全排列中挑选最小的
solutions = permutation(solution_sta)
obj_best = float("inf")
i = 0
for s in solutions:
obj = length(points[s[-1]], points[s[0]])
i += 1
for index in range(0, nodeCount-1):
obj += length(points[s[index]], points[s[index+1]])
if obj < obj_best:
obj_best = obj
solution = s
# prepare the solution in the specified output format
output_data = '%.2f' % obj + ' ' + str(0) + '\n'
output_data += ' '.join(map(str, solution))
return output_data
(2)贪心算法
每次寻找与当前结点最近的一个结点。从图的角度来看,也可看做是寻找一条最短的Hamilton回路。
@requires_authorization
import math
from collections import namedtuple
Point = namedtuple("Point", ['x', 'y'])
def length(point1, point2):
return math.sqrt((point1.x - point2.x)**2 + (point1.y - point2.y)**2)
def solve_it(input_data):
# parse the input
lines = input_data.split('\n')
nodeCount = int(lines[0])
points = []
for i in range(1, nodeCount+1):
line = lines[i]
parts = line.split()
points.append(Point(float(parts[0]), float(parts[1])))
# greedy algorithm
flag = [0] * nodeCount # noted the node
flag[0] = 1
solution = [0] * nodeCount
obj = 0
i = 0
index = 0
while i < nodeCount-1:
dis_min = float("inf")
for j in range(0, nodeCount):
if flag[j] == 0 and j != index:
dis = length(points[index], points[j])
if dis < dis_min:
dis_min = dis
index = j
solution[i+1] = index
flag[index] = 1
obj += dis_min
i += 1
obj += length(points[0], points[index])
output_data = '%.2f' % obj + ' ' + str(0) + '\n'
output_data += ' '.join(map(str, solution))
return output_data
(3) opt-2 算法
(参考博客:https://blog.csdn.net/qq_33256688/article/details/75642525)
step1 随机选择一条路径;
step2 设置算法停止参数countmax
step3 随机选择两个节点,将结点之间的路径翻转,若新路径更优,则 迭 代次数count+1,更新路径; 否则置0.
step4 若迭代次数小于countmax,停止;否则,重复step3。
也可考虑将贪心算法所求结果作为初始解,减少迭代次数?迭代次数并未减少。
import math
from collections import namedtuple
import numpy as np
Point = namedtuple("Point", ['x', 'y'])
def length(point1, point2):
return math.sqrt((point1.x - point2.x)**2 + (point1.y - point2.y)**2)
def generatePath(path):
l = len(path)
a = np.random.randint(0, l)
while True:
b = np.random.randint(0, l)
if np.abs(a - b) > 1:
break
if a > b:
return b, a, path[b:a+1]
else:
return a, b, path[a:b+1]
def reversePath(path):
re_path = path.copy()
re_path[1:-1] = re_path[-2:0:-1]
return re_path
def calPath(path, dis):
s = 0.0
l = len(path)
for i in range(0, l-1):
s += dis[path[i]][path[i+1]]
return s
def comparePath(path1, path2, dis):
if calPath(path1, dis) < calPath(path2, dis):
return True
else:
return False
def solve_it(input_data):
# parse the input
lines = input_data.split('\n')
nodeCount = int(lines[0])
points = []
for i in range(1, nodeCount+1):
line = lines[i]
parts = line.split()
points.append(Point(float(parts[0]), float(parts[1])))
##greedy algorithm(hamilton circle)
# distance matrix (上三角矩阵,方便后续处理)
# 完全图,任意两地都可到达
n = float("inf")
dis = [[n for col in range(nodeCount)] for row in range(nodeCount)]
for i in range(nodeCount-1):
for j in range(i+1, nodeCount):
dis[i][j] = length(points[i],points[j])
for i in range(1, nodeCount):
for j in range(0, i):
dis[i][j] = dis[j][i]
# opt-2
# build a trivial solution
bestPath = list(range(0, nodeCount))
bestPath.append(0)
if nodeCount == 51:
MAXCOUNT = 200
else:
MAXCOUNT = 10
count = 0
while count < MAXCOUNT:
start, end, path = generatePath(bestPath) #产生随机路径
rePath = reversePath(path)
if comparePath(path, rePath, dis):
count += 1
continue
else:
count = 0
bestPath[start:end+1] = rePath
obj = 0.0
obj += calPath(bestPath, dis)
bestPath.pop()
solution = bestPath
output_data = '%.2f' % obj + ' ' + str(0) + '\n'
output_data += ' '.join(map(str, solution))
return output_data
(3)动态规划算法
参考博客: http://blog.csdn.net/gfaiswl/article/details/4749713
将城市用二进制数表示。
#dis 为距离矩阵
cnt = 2 ** (nodeCount-1)
dp = [[n for col in range(cnt)] for row in range(nodeCount+1)]
for i in range(1, nodeCount):
dp[i][0] = dis[i][0]
for i in range(1, cnt-1): #列标(集合)
for j in range(1, nodeCount): #行标
if i&(1<<(j-1)) == 0: # j不在集合中
for k in range(1, nodeCount):
if (1<<(k-1))&i: # k在集合中
dp[j][i] = min(dp[j][i], dis[j][k]+dp[k][i-(1<<(k-1))])
for k in range(1, nodeCount):
if (1<<(k-1))&(cnt-1): # k在集合中
dp[0][cnt-1] = min(dp[0][cnt-1], dis[0][k]+dp[k][cnt-1-(1<<(k-1))])
obj = dp[0][cnt-1]
print(obj)
(5)插入法(O(n^3))
step1.寻找插入结点(选择最远的一个结点为插入结点)
step2.确定插入位置(使总距离最小)
假设距离矩阵dis = [[n, 3, 93, 13, 33, 9], [4, n, 77, 42, 21, 16],
[45, 17, n, 36, 16,28],[39, 90, 80, n, 56, 7],
[28, 46, 88, 33, n, 25], [3, 88, 18, 46, 92, n]],(n为infinite)
随机选择一个结点为初始结点s,假设s=0,则dist=[n, 3, 93, 13, 33, 9],
距离最远的一个城市为2,即插入节点为2,设为f。
cij=wif + wfj - wij。(i,j)为现有的边。选择最小的cij,将结点f插入到(i,j)中。更改距离矩阵
import numpy as np
n = float("inf")
dis = [[n, 3, 93, 13, 33, 9], [4, n, 77, 42, 21, 16],
[45, 17, n, 36, 16, 28],[39, 90, 80, n, 56, 7],
[28, 46, 88, 33, n, 25], [3, 88, 18, 46, 92, n]]
nodeCount = len(dis)
s = np.random.randint(nodeCount) # 随机选择初始结点
V = list(range(nodeCount)) # 所有结点集合
Vt = [s] # 已访问结点
Vr = list(set(V)^set(Vt)) # 未访问结点
Et = [(s,s)] # 边
w = [[0 for col in range(nodeCount)] for row in range(nodeCount)]
for i in range(nodeCount):
for j in range(nodeCount):
if j != i:
w[i][j] = dis[i][j]
tweight = 0
dist = dis[s]
while len(Vt) < nodeCount:
# selection
max_ = -n
for i in range(nodeCount):
if dist[i] > max_ and dist[i] != n:
max_ = dist[i]
f = i
# insert
min_ = n
for edge in Et:
c = w[edge[0]][f] + w[f][edge[1]] - w[edge[0]][edge[1]]
if c < min_:
min_ = c
row = edge[0]
col = edge[1]
Et.remove((row, col))
Et.append((row, f))
Et.append((f, col))
Vt.append(f)
Vr.remove(f)
tweight += min_
dist[f] = n
# 更改距离矩阵
for x in Vr:
dist[x] = min(dist[x], w[f][x])
route = [0] * nodeCount
for i in range(1, nodeCount):
for j in range(nodeCount):
if route[i-1] == Et[j][0]:
route[i] = Et[j][1]
break