求解TSP问题(python)(穷举、最近邻居法、opt-2法、动态规划、插入法)

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

你可能感兴趣的:(discrete,operation)