networkx图论贝尔曼-福特Bellman Ford Algorithm最短路径,Python

x33g5p2x  于2021-11-11 转载在 Go  
字(3.1k)|赞(0)|评价(0)|浏览(388)

(1)Bellman Ford Algorithm,贝尔曼-福特算法,图中搜索遍历最短路径的一种适应性强的算法。Bellman Ford Algorithm可以应用在负值边权的图,这和dijkstra最短路径算法有差异,dijkstra不适应于存在负边权的图。Bellman Ford Algorithm是一种广度优先搜索最短路径策略。经过全部迭代后,每一个节点中保存的权值(node weight),即为从起始点(一般为0)到该节点的最小路径值。

(2)Bellman Ford Algorithm在迭代开始时候,选一个顶点V,然后在队列Q中保存与顶点V相邻接的节点,并更新相邻接的节点权值。但是,要特别注意,所谓在队列Q中保存与顶点相邻接的节点,只发生在相邻接的节点权值更新时候才保存,否则该邻接节点不加入队列Q。

比如,顶点V节点权值为5,从V(from_node)出发,与V相邻接的节点有V1,V2(这两个为 to_node)。假设V1当前权值为8,V与V1邻接的边权(edge_weight)为1,5+1=6 < 8,于是更新V1的节点权值(node_weight)为最新的6,并同时把节点V1加入队列Q。假设V2当前节点权值为6,V与V2邻接的边权(edge_weight)为4,5+4=9 > 6,所以V2节点权值不予更新,仍为6,且V2节点不能加入队列。

(3)每迭代一轮,删掉队列最左边(头部)的节点。

import random

import networkx as nx
import matplotlib.pyplot as plt

WEIGHT = 'weight'

def my_graph():
    G = nx.gnm_random_graph(n=8, m=10)

    pos = nx.spring_layout(G)
    nx.draw_networkx(G, pos,
                     node_color='green',
                     node_size=300,
                     font_size=10,
                     font_color='white',
                     edge_color='gray',
                     width=1,
                     with_labels=True)

    # 每条边增加随机权值
    for e in G.edges(data=True):
        e[2][WEIGHT] = random.randint(1, 9)

    my_edge_labels = nx.get_edge_attributes(G, WEIGHT)
    nx.draw_networkx_edge_labels(G, pos, edge_labels=my_edge_labels)

    for n in G.nodes():
        G.nodes[n][WEIGHT] = float('inf')

    V = 0
    G.nodes[V][WEIGHT] = 0

    print('G.edges(data=True)', G.edges(data=True))
    print('G.nodes(data=True)', G.nodes(data=True))

    bellman_ford_algorithm(G, V)

    plt.show()

# 基于队列,广度搜索遍历,贝尔曼-福特算法
def bellman_ford_algorithm(G, V):
    Q = [V]

    while True:
        if len(Q) == 0:
            break
        print('-----')
        print('Q-', Q)

        visit_node = Q[0]
        print('访问', visit_node)
        neighbors = nx.neighbors(G, visit_node)
        for to_node in neighbors:
            update_weight(G, visit_node, to_node, Q)

        del (Q[0])

        print('Q--', Q)

    print('nodes', G.nodes(data=True))

    path = find_short_path(G, 5)
    print('path', path)

# 寻找从0节点到v的最短路径
def find_short_path(G, v):
    path = [v]

    loop_flag = True
    while loop_flag:
        v_node_weight = G.nodes[v][WEIGHT]
        ok_node = None

        for from_node in nx.neighbors(G, v):
            from_node_weight = G.nodes[from_node][WEIGHT]
            edge_weight = G.get_edge_data(u=from_node, v=v)[WEIGHT]
            if (from_node_weight + edge_weight) == v_node_weight:
                ok_node = from_node
                break

        if ok_node != None:
            path.append(ok_node)
            if ok_node == 0:
                loop_flag = False
            else:
                v = ok_node

    list.reverse(path)
    return path

def update_weight(G, from_node, to_node, Q):
    form_node_weight = G.nodes[from_node][WEIGHT]
    edge_weight = G.get_edge_data(u=from_node, v=to_node)[WEIGHT]
    to_node_weight = G.nodes[to_node][WEIGHT]
    sum_weight = form_node_weight + edge_weight
    if (sum_weight) < to_node_weight:
        G.nodes[to_node][WEIGHT] = sum_weight
        Q.append(to_node)

if __name__ == '__main__':
    my_graph()

生成图:

运行日志:

G.edges(data=True) [(0, 4, {'weight': 7}), (0, 2, {'weight': 3}), (0, 6, {'weight': 2}), (1, 2, {'weight': 5}), (2, 3, {'weight': 6}), (2, 4, {'weight': 8}), (3, 5, {'weight': 2}), (4, 5, {'weight': 8}), (4, 6, {'weight': 5}), (6, 7, {'weight': 8})]
G.nodes(data=True) [(0, {'weight': 0}), (1, {'weight': inf}), (2, {'weight': inf}), (3, {'weight': inf}), (4, {'weight': inf}), (5, {'weight': inf}), (6, {'weight': inf}), (7, {'weight': inf})]
-----
Q- [0]
访问 0
Q-- [4, 2, 6]
-----
Q- [4, 2, 6]
访问 4
Q-- [2, 6, 5]
-----
Q- [2, 6, 5]
访问 2
Q-- [6, 5, 3, 1]
-----
Q- [6, 5, 3, 1]
访问 6
Q-- [5, 3, 1, 7]
-----
Q- [5, 3, 1, 7]
访问 5
Q-- [3, 1, 7]
-----
Q- [3, 1, 7]
访问 3
Q-- [1, 7, 5]
-----
Q- [1, 7, 5]
访问 1
Q-- [7, 5]
-----
Q- [7, 5]
访问 7
Q-- [5]
-----
Q- [5]
访问 5
Q-- []
nodes [(0, {'weight': 0}), (1, {'weight': 8}), (2, {'weight': 3}), (3, {'weight': 9}), (4, {'weight': 7}), (5, {'weight': 11}), (6, {'weight': 2}), (7, {'weight': 10})]
path [0, 2, 3, 5]

相关文章