动态规划法 - 解决TSP旅行商问题(iOS、python)

项目里突然出现了一个类似TSP旅行商的问题,稍微有点儿变动的可能是需要指定终点,而不是回到起点。

因为项目里设计规划的点的比较少(<20)又要求比较准确,所以采用了动态规划法。

动态规划算法的定义就不多做介绍了, 下面直接来到解决思路。

假设有N个城市,dp[N][N]这个二维数组保存了 各个城市之间的距离

那么问题就可以简化为 从0(p)点出发到还未走过的城市集合 S(N-1)的最短距离

定义为 min_distance(S, 0) ,

假设第二个到达城市j,那么剩下的最短距离min_distance(S-j,j)  (S-j 表示S集合去掉j点,先这么表示,后面具体再说)

就有min_distance(S, 0) = min_distance(S-j, j) + dp[j][0],这也就是我们的递归公式

如果我们要取最小值的话 上面的j城市就要遍历从 1 到 (N-1)的点 然后取最小,我们用min{}表示取{}集合里的最小值

也就是 min { min_distance(S-j, j) + dp[j][0] }

接下来我们用代码展示一下(以Python为例,后面会贴上iOS的demo)

我们用整形来表示集合S,一个整形是32位,因为S集合是没有去过的点,我们用1来表示没有去过,0表示已经去过

首先我们对S 初始化

s = 0
for i in range(1,N-1): # 从1开始
    s = s|(1 << i)
# 遍历完之后 就表示除了起点之外的所有集合

接下来定义下min_distance函数

    # 这里有个关键问题 就是当已经到i城市的话 如何从S集合里去除i ,因为我们使用位表示的s集合,所以需要s & 该位的取反 也就是 ~(1<     # 最终表示为 s & (~(1<

也就是min_distance(s, 0) =  min { min_distance(s & (~(1<

当0 为 任意一个城市的时候就有了

    def min_distance(s, p):
        min_length = 9999999 # 记录 最小距离
        for i in range(1, N): # 从1点开始遍历
            if s & (1 << i): #如果i在S集合里,也就是i是没有去过的城市
                ret = min_distance(s & (~(1<

当这段代码写出来的时候 大家也大概看出了 差不多就是穷举法,就是要把所有的距离都算出来,取最小值。

当然这还不算完,因为有大量的重复计算,举个例子

有 0 1 2 3 4 5个城市

假设已走过 0 1 2 3三个城市 现在按顺序0 - 1 -2 - 3走,求3 到S{4, 5}的最小距离。

然后我们不按顺序走 0 - 2- 1 -3 ,现在也是 求3 到S{4, 5}的最小距离,这时我们发现min_distance(s{4,5} , 3)求了两遍。

这只是举个例子,这种递归里肯定有大量的重复运算。

所以我们用一个二维数组记录已经算过的距离 visited[2^N][N]初始化所有的值为-1,然后记录所有的最小距离(2^N 与 s集合对应)

当求值的时候首先判断一下 该值是否为-1 若不是 直接返回,于是改进后

    def min_distance(s, p):
        if visited[s][p] != -1: #若之前计算过 ,直接返回
            return visited[s][p]
        min_length = 9999999 # 记录 最小距离
        for i in range(1, N): # 从1点开始遍历
            if s & (1 << i): #如果i在S集合里,也就是i是没有去过的城市
                ret = min_distance(s & (~(1<

我们还有一个需求 就是要求算出的结果要回到第N个城市  在代码里也就是第N-1个城市

也就是当S集合里只剩一个城市N-1的时候也要退出,返回值为该点到N-1城市的距离 也就是dp[N-1][p]

同时我们的遍历条件 之前是 for i in range(1,N),现在第N-1个点也不需要遍历

    def min_distance(s, p):
        if visited[s][p] != -1: #若之前计算过 ,直接返回
            return visited[s][p]
        if s == ( 1<< (N-1)): # 当是最后一个城市N的时候,直接返回该点到城市N的距离 
            return dp[N-1][p]
        min_length = 9999999 # 记录 最小距离
        for i in range(1, N-1): # 从1点开始遍历,不包括最后一个点N-1
            if s & (1 << i): #如果i在S集合里,也就是i是没有去过的城市
                ret = min_distance(s & (~(1<

还记得如何调用这个方法吗

s = 0
for i in range(1,N-1): # 从1开始
    s = s|(1 << i)
# 遍历完之后 就表示除了起点之外的所有集合

distance = min_distance(s,0)# 从0 点 - 到所有没去过的城市结合
print(distance)

到这里的话 我们只能计算出从起点到N城市的最短距离,却没有记录路径 (从0 怎么到 N的?)

那怎么记录呢?大家如果理解了以上的话,应该不是难题。

Demo来了哦

 

你可能感兴趣的:(动态规划法 - 解决TSP旅行商问题(iOS、python))