【上篇】Python实现最短路问题常见求解算法——Label Correcting Algorithm(deque)

本文介绍使用Python语言实现DequeLabelCorrecting算法解决最短路径问题。该算法适用于无负环网络,能找出起点到终点的最短路径,并通过可视化展示结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于python语言,实现deque label correcting 算法对最短路问题(Shortest Path Problem)进行求解。

1. 适用场景

  • 无负环网络
  • 起点与终点间至少存在一条可行路径
  • 可求解单一起点到多点的最短路径

2. 算法说明

应用标号法求解最短路问题时有两种选择,一是采用 label setting 策略,即 label setting algorithm,如经典的 Dijkstra’s algorithm ,或采用label correcting策略,即 label correcting algorithm,如经典的 FIFO label correcting algorithmDeque label correcting algorithm。二者在最短路问题上都表现出较好的效率,但与 label setting algorithm算法相比,label correcting algorithm 算法更为灵活,易于扩展,且可以检测出含有负环的网络本文采用python语言实现Deque label correcting algorithm 对不含负环网络的最短路问题进行求解。对于含有负环网络的最短路问题将在后续文章分享。

3. 测试网络

这里以Tempe ASU网络为测试数据集,网络如下图所示,可通过osm2gmns工具包获取。
在这里插入图片描述

4. 代码实现

(1)定义网络数据结构

# define network data struct
class Network():
    def __init__(self):
        self.node_neighbors={}
        self.node_x_coord={}
        self.node_y_coord={}
        self.node_id_list=[]
        self.number_of_nodes=0
        self.link_cost={}
        self.link_list=[]

        self.source_node_id=0
        self.sink_node_id=1

(2)读取网络文件

# read network csv files
def read_network_file(node_file,link_file,network):
    # read node csv file
    with open(node_file) as node_f:
        node_reader=csv.reader(node_f)
        next(node_reader)
        for row in node_reader:
            network.node_id_list.append(int(row[0]))
            network.node_x_coord[int(row[0])] = float(row[1])
            network.node_y_coord[int(row[0])] = float(row[2])
            network.node_neighbors[int(row[0])] = []
    # read link csv file
    with open(link_file) as f:
        link_reader = csv.reader(f)
        next(link_reader)
        for row in link_reader:
            from_node_id = int(row[1])
            to_node_id = int(row[2])
            cost = float(row[3])
            network.node_neighbors[from_node_id].append(to_node_id)
            network.link_list.append([from_node_id, to_node_id])
            network.link_cost[from_node_id, to_node_id] = cost
    network.number_of_nodes = len(network.node_id_list)
    return network

(3)可视化最短路径

def show_shortest_path(net,path_node_id_list):
    for from_node_id,to_node_id in net.link_list:
        x_coords=[net.node_x_coord[from_node_id],net.node_x_coord[to_node_id]]
        y_coords=[net.node_y_coord[from_node_id],net.node_y_coord[to_node_id]]
        plt.plot(x_coords,y_coords,color='black',linewidth=0.5)
    path_x_coord=[]
    path_y_coord=[]
    for node_id in path_node_id_list:
        path_x_coord.append(net.node_x_coord[node_id])
        path_y_coord.append(net.node_y_coord[node_id])
    plt.plot(path_x_coord,path_y_coord,color='b')
    plt.xlabel('x_coord')
    plt.ylabel('y_coord')
    plt.show()

(4)保存最短路径信息

# save the shortest path information to csv file
def save_to_file(network,path_node_id_list,path_cost,node_predecessor=None,node_label_cost=None):
    outfile = open('shortest_path.csv', 'w', newline='', errors='ignore')
    write = csv.writer(outfile)

    write.writerow(['source_node_id', 'sink_node_id', 'total cost', 'path node id list'])
    path = '-'.join([str(i) for i in path_node_id_list])
    line = [network.source_node_id, network.sink_node_id,path_cost,path]
    write.writerow(line)
    # whether save the shortest path information from  the source node to other nodes
    if node_predecessor and node_label_cost:
        try:
            for node_id in network.node_id_list:
                if node_id!=network.source_node_id and node_id!=network.sink_node_id:
                    path_node_id_list = [node_id]
                    pre_node_id = node_predecessor[node_id]
                    path_cost = 0
                    if node_label_cost[node_id]<float('inf'):
                        while pre_node_id != network.source_node_id:
                            path_node_id_list.insert(0, pre_node_id)
                            path_cost += network.link_cost[path_node_id_list[0], path_node_id_list[1]]
                            pre_node_id = node_predecessor[pre_node_id]
                        path_node_id_list.insert(0, network.source_node_id)
                        path_cost += network.link_cost[path_node_id_list[0], path_node_id_list[1]]
                        path = '-'.join([str(i) for i in path_node_id_list])
                        line = [network.source_node_id, node_id, path_cost, path]
                        write.writerow(line)
                    else:
                        line = [network.source_node_id, node_id, 'inf', 'nan']
                        write.writerow(line)
        except Exception as e:
            pass
    outfile.close()

(5)采用label correcting算法的deque实现寻找最短路径

# find the shortest path from source node to sink node using deque label correcting algorithm
def find_shortest_path(network):
    node_predecessor = {}
    node_predecessor[network.source_node_id]=None
    node_label_cost = { node_id:float('inf') for node_id in network.node_id_list}
    node_label_cost[network.source_node_id] = 0
    SEList = deque()
    SEList_all = []
    SEList.append(network.source_node_id)
    SEList_all.append(network.source_node_id)
    while len(SEList)>0:
        current_node_id=SEList[0]
        SEList.popleft()
        current_node_label_cost = node_label_cost[current_node_id]
        for to_node_id in network.node_neighbors[current_node_id]:
            new_label=current_node_label_cost+network.link_cost[current_node_id,to_node_id]
            if new_label<node_label_cost[to_node_id]:
                node_label_cost[to_node_id]=new_label
                node_predecessor[to_node_id]=current_node_id
                if to_node_id in SEList_all:
                    SEList.insert(0,to_node_id)
                else:
                    SEList.append(to_node_id)
                SEList_all.append(to_node_id)

    path_node_id_list = [network.sink_node_id]
    pre_node_id = node_predecessor[network.sink_node_id]
    path_cost = 0
    if node_label_cost[network.sink_node_id]<float('inf'):
        while pre_node_id != network.source_node_id:
            path_node_id_list.insert(0, pre_node_id)
            path_cost += network.link_cost[path_node_id_list[0], path_node_id_list[1]]
            pre_node_id = node_predecessor[pre_node_id]
        path_node_id_list.insert(0, network.source_node_id)
        path_cost += network.link_cost[path_node_id_list[0], path_node_id_list[1]]
        path = '-'.join([str(i) for i in path_node_id_list])
        print("the trave cost from node id=%s to node id=%s is: %s" % (network.source_node_id, network.sink_node_id, path_cost))
        print("the shortest path from node id=%s to node id=%s is: %s" % (network.source_node_id, network.sink_node_id, path))

        show_shortest_path(network, path_node_id_list)
        save_to_file(network, path_node_id_list, path_cost,node_predecessor,node_label_cost)
    else:
        print("there is no feasible path from node id=%s to node id=%s"%(network.source_node_id,network.sink_node_id))

(6)主程序

if __name__=='__main__':
    # init network data struct
    network=Network()
    # setting source node  id and sink node id
    network.source_node_id=4177
    network.sink_node_id=3881
    # read network files
    read_network_file('./node.csv','./link.csv',network)
    # check whether the source node id and sink node id is in the network
    if network.source_node_id not in network.node_id_list:
        print(" %s not found"%network.source_node_id)
        sys.exit(0)
    if network.sink_node_id not in network.node_id_list:
        print(" %s not found"%network.sink_node_id)
        sys.exit(0)
    # find the shortest path
    find_shortest_path(network)

5. 求解结果

在这里插入图片描述

6. 代码及数据文件

相关代码及数据文件可从github主页获取:

https://github.com/PariseC/Shortest_Path_Algorithm/tree/master/label_correcting_algorithm

参考

  1. R.K. Ahuja, T.L. Magnanti and J.B. Orlin, Network Flows: Theory, Algorithms, and Applications. Prentice-Hall, 1993. A comprehensive recent survey of the topic.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Better.C

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值