【问题标题】:Dummy node for Traveling Salesman Problem旅行商问题的虚拟节点
【发布时间】:2021-07-09 22:10:09
【问题描述】:

最近我读了很多关于 TSP 的文章,我需要创建一个 TSP 的变体

  1. 我不关心起点(可以是任何城市)和
  2. 结束城市不必与起始城市相同

显然,这可以使用虚拟节点来实现 - 到每个其他节点的距离为 0:source 这是否意味着输入:cityAcityBcityCcityDcityE 矩阵表示应如下所示:

[
[0,9,6,1,3]
[9,0,4,2,1]
[6,4,0,9,1]
[1,2,9,0,8]
[3,1,1,8,0]
[0,0,0,0,0]
]

这是正确的方法,如果不是,那为什么?我仍然对理解为什么额外的虚拟节点工作以获取我的变体的路径感到困惑。谢谢

【问题讨论】:

  • 您的数组是否应该有一个额外的全 0 列?
  • 除此之外,我不认为虚拟节点是绝对必要的。我想说创建一个 DP 解决方案,在尝试从当前位置的所有路径后返回最短路径。退出条件将是一旦所有城市都被访问返回 0. 或返回 0, [] 如果你也想建立路径。然后为每个可能的起始位置运行一次 DP 功能。使用虚拟节点与此等价,只是您只需运行一次 DP 函数并选择虚拟节点作为开始。

标签: python arrays list algorithm


【解决方案1】:

根据上面的评论,这是一个没有虚拟节点的示例:

import functools
import math

def shortest_path(arr):
    
    n = len(arr)
    bitmask = [1 << i for i in range(n)]
    target = (1 << n) - 1
    
    @functools.lru_cache(None)
    def helper(city, visited):
        nonlocal target, n
        
        if visited == target:
            return 0, [city]
        
        best = math.inf, []
        for neigh in range(n):
            if not (visited & bitmask[neigh]):
                cost, path = helper(neigh, visited | bitmask[neigh])
                cost += arr[city][neigh]
                path = [city] + path
                if cost < best[0]:
                    best = cost, path
        return best
    
    best, best_path = math.inf, []
    for start in range(n):
        total_distance, path = helper(start, bitmask[start])
        if total_distance < best:
            best, best_path = total_distance, path
    
    return best, best_path

def shortest_path_padded(arr):
    n = len(arr)
    bitmask = [1 << i for i in range(n)]
    target = (1 << n) - 1
    
    @functools.lru_cache(None)
    def helper(city, visited):
        nonlocal target, n
        
        if visited == target:
            return 0, [city]
        
        best = math.inf, []
        for neigh in range(n):
            if not (visited & bitmask[neigh]):
                cost, path = helper(neigh, visited | bitmask[neigh])
                cost += arr[city][neigh]
                path = [city] + path
                if cost < best[0]:
                    best = cost, path
        return best
    
    return helper(0, bitmask[0])


if __name__ == "__main__":
    arr = [
            [0,9,6,1,3],
            [9,0,4,2,1],
            [6,4,0,9,1],
            [1,2,9,0,8],
            [3,1,1,8,0]
          ]
    arr2 = [[0]*(len(arr[0])+1)] + [[0] + row for row in arr]
    
    print(shortest_path(arr))
    print(shortest_path_padded(arr2))
Out: (5, [0, 3, 1, 4, 2])
Out: (5, [0, 1, 4, 2, 5, 3]) # city names + 1 because city 0 is dummy city

使用虚拟节点与尝试每个城市作为起始城市有什么不同?

没什么,如果你从一个距离任何其他城市的虚拟节点 0 开始,它的第一选择就是选择第一个去的城市。

这个没有for循环和零填充数组的解决方案与使用for循环和数组的解决方案相同。


#NO LIBRARIES
def shortest_path_padded_no_libs(arr):
    n = len(arr)
    bitmask = [1 << i for i in range(n)]
    target = (1 << n) - 1
    
    def helper(city, visited):
        nonlocal target, n
        
        h = (city, visited)
        if h in memo:
            return memo[h]
        
        if visited == target:
            return 0, [city]
        
        best = float('inf'), []
        for neigh in range(n):
            if not (visited & bitmask[neigh]):
                cost, path = helper(neigh, visited | bitmask[neigh])
                cost += arr[city][neigh]
                path = [city] + path
                if cost < best[0]:
                    best = cost, path
                    
        memo[h] = best
        return best
    
    memo = {}
    
    return helper(0, bitmask[0])


if __name__ == "__main__":
    arr = [
            [0,9,6,1,3],
            [9,0,4,2,1],
            [6,4,0,9,1],
            [1,2,9,0,8],
            [3,1,1,8,0]
          ]
    arr2 = [[0]*(len(arr[0])+1)] + [[0] + row for row in arr]
    print(shortest_path_padded_no_libs(arr2))

【讨论】:

  • 我明白了,谢谢,我要添加一个满为 0 的列,使其成为 6x6 矩阵,对此,我正在使用 ant heuristics 来优化问题,这里是一个代码链接我正在使用:github.com/Vampboy/Ant-Colony-Optimization/blob/master/…
  • 那么在这种情况下,我将如何进行“迭代”以从任何城市开始?
  • 只要您将邻接矩阵传递到shortest_path 中,迭代部分就应该自行处理。请注意,我还添加了shortest_path_padded(第一行和第一列全为零),它使用您提到的虚拟节点方法找到最短路径。它们应该是等价的。哪个对你来说更容易实现应该没问题。
  • 非常感谢您,我能问一下您的算法的计算成本是多少 - 因为这不是蛮力,20 多个城市是可行的?
  • 如果您已经熟悉如何制作最小生成树和树的基本前/中/后顺序遍历,那么 MST 加遍历方法肯定会更容易。这是一个讨论它的链接:aswani.ieor.berkeley.edu/teaching/FA13/151/lecture_notes/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
  • 2021-08-27
  • 2018-03-26
  • 2019-07-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多