python实现最短路径

对于一个邮差需要送信,以出发地为起点,沿着不同城市的连通路径遍历各个城市,以便把信交到对应城市的收件热手中,问题在于在返回出发点之前,邮差如何用最短的行程将每个城市遍历一遍,而且要求是每个城市只能被遍历一遍,假定城市间的连通路径如下图:

添加图片注释,不超过 140 字(可选)

上图中的每个节点都代表了一个城市,节点间的连线的数值就是对应的城市之间的路径的长短距离,假设邮差从节点1出发,如何用最短的距离遍历每个节点然后回到节点1,同时要求每个节点最多被遍历一次,这也就是所谓的NP问完全问题,无论计算机运行有多快,只要问题的规模稍微变大一些,就会导致计算机的算力会很快就被用完,而且还得不到有效的结果。

对于这个问题首先想到的也就是暴力穷举法,也就是罗列出所有可能得遍历方式,如果有n个城市,那以其中一个城市作为起点,要罗列出其他n-1个城市的遍历次序就会产生出n-1的阶乘种可能。所以暴力穷举法不是解决该问题的有效方法,这里主要是考虑使用动态规划的思维来解决,将该问题分解成子问题,我们用集合

添加图片注释,不超过 140 字(可选)

来表示出去起始节点之外的其他节点集合,有差遍历的最短路径为

添加图片注释,不超过 140 字(可选)

假设倒数第二个节点是j,那么一定有路径以上图的最短路径。

使用python实现的代码如下:

N = 5  #节点数
D = np.zeros((N, N)).astype(int)
S = set()  #对应点的集合
for i in range(1, N):
    S.add(i)
D[0][0] = 0  #根据图5-5进行初始化
D[0][1] = 2
D[0][2] = 4
D[0][3] = 1
D[0][4] = 2
D[1][0] = 3
D[1][1] = 0
D[1][2] = 3
D[1][3] = 2
D[1][4] = 3
D[2][0] = 4
D[2][1] = 3
D[2][2] = 0
D[2][3] = 4
D[2][4] = 2
D[3][0] = 1
D[3][1] = 2
D[3][2] = 4
D[3][3] = 0
D[3][4] = 2
D[4][0] = 2
D[4][1] = 3
D[4][2] = 2
D[4][3] = 2
D[4][4] = 0
def  get_subset(point_set, n): #根据点数构造子集
    if n == 0:
        return []
    sub_sets = [] 
    for p in point_set:
        s = set()
        s.add(p)
        set_copy = point_set.copy()
        set_copy.discard(p)
        sets = get_subset(set_copy, n - 1)
        if len(sets) == 0:
            sub_sets.append(s)
        else:
            for each_set in sets:
                s_union = s.copy().union(each_set)
                sub_sets.append(s_union)
    return sub_sets
def  find_shortest_path():  #计算最短路径
    C = {}
    for point_count in range(1, N):
        sub_sets = get_subset(S.copy(), point_count)  #获得包含给定点数的子集
        for the_set in sub_sets:
            distances = {}  #记录起始点到集合内每一点的最短距离
            for the_point in the_set: #计算当前集合内的点作为回到起始点前一个点时对应的最短距离
                after_discard = the_set.copy()
                after_discard.discard(the_point)
                if len(after_discard) == 0:
                    distances[the_point] = D[0][the_point]
                else:
                    '''
                    如果集合S包含三个点{1,2,3},从节点0开始遍历集合中所有点的最短路径算法为,先找出从起始点0开始遍历集合{1,2},{1,3},{2,3}的最短路径,
                    然后用这三条路径长度分别加上D[2][3],D[3][2],D[3][1],三个结果中的最小值就是从起始点0开始,遍历集合{1,2,3}中所有点后的最短路径.
                    因此当集合为{1,2,3}时,C[{1,2,3}]对应key值为1,2,3的一个map对象,map[1]表示集合点为{1,2,3}时,遍历路径最后一个节点时1时的最短距离
                    '''
                    set_copy = the_set.copy()
                    set_copy.discard(the_point)
                    distance_to__points = C[frozenset(set_copy)]
                    d = sys.maxsize 
                    for p in set_copy:
                        if D[p][the_point] + distance_to__points[p] < d:
                            d = D[p][the_point] + distance_to__points[p] 
                    distances[the_point] = d
            C[frozenset(the_set)] = distances.copy() #记录起始点到当前集合内每个点的最短距离,根据python语法,fronzenset才能作为key
            distances.clear()
    distances = C[frozenset(S)]
    d = sys.maxsize
    for point in S: #先找到起始点到集合{1,2...n}中对应点的嘴短距离
        if distances[point] + D[point][0] < d:
            d = distances[point] + D[point][0]
    return d, C
shortest_distance, C = find_shortest_path()
print("the shortest distance for visiting all points is : ", shortest_distance)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值