算法导论 第二十五章 所有节点对的最短路径问题

基于单源最短路径问题的讨论,解决所有最短路径问题,对每个节点求其最短路径:

算法导论 第二十五章 所有节点对的最短路径问题_第1张图片

25.1 最短路径和矩阵乘法

用动态规划的方法来实现之前的思路:

算法导论 第二十五章 所有节点对的最短路径问题_第2张图片

算法导论 第二十五章 所有节点对的最短路径问题_第3张图片

如下示例:

算法导论 第二十五章 所有节点对的最短路径问题_第4张图片

使用公式L[i][j] = min(L[i][j], L'[i][k] + W[k][j])求解第一行的过程如下:

i = 1, j = 1
l'(1,1)= inf; l(1,1) + w(1,1) = 0; l(1,2) + w(2,1) = inf; 
l(1,3) + w(3,1) = inf; l(1,4) + w(4,1) = inf; l(5,1) + w(5,1) = inf;
=> l'(1,1) = 0

i = 1, j = 2
l'(1,2)= inf; l(1,1) + w(1,2) = 3; l(1,2) + w(2,2) = 3; 
l(1,3) + w(3,2) = 12; l(1,4) + w(4,2) = inf; l(5,1) + w(5,2) = inf;
=> l'(1,2) = 3

i = 1, j = 3
l'(1,3)= inf; l(1,1) + w(1,3) = 8; l(1,2) + w(2,3) = inf; 
l(1,3) + w(3,3) = 8; l(1,4) + w(4,3) = inf; l(5,1) + w(5,3) = inf;
=> l'(1,3) = 8

i = 1, j = 4
l'(1,4)= inf; l(1,1) + w(1,4) = inf; l(1,2) + w(2,4) = 4; 
l(1,3) + w(3,4) = inf; l(1,4) + w(4,4) = inf; l(5,1) + w(5,4) = 2;
=> l'(1,3) = 2

i = 1, j = 5
l'(1,5)= inf; l(1,1) + w(1,5) = -4; l(1,2) + w(2,5) = 10; 
l(1,3) + w(3,5) = inf; l(1,4) + w(4,5) = inf; l(5,1) + w(5,5) = -4;
=> l'(1,5) = -4
这里的求解过程与矩阵乘法的求解过程在形式上是一样的,所以我们可以用矩阵乘法中的符号来表示求l[i][j]。

算法导论 第二十五章 所有节点对的最短路径问题_第5张图片

在形式上转化为


并发现结合律在求min中同样起作用,借用矩阵冥的求解优化来求最短路径:

算法导论 第二十五章 所有节点对的最短路径问题_第6张图片

以上实现和优化的代码实现如下:

def PRINT_GRAPH_MATRIX(G):
    format_str = "%-8.2f" * len(G)
    for row in G:
        print(format_str % tuple(row))

def CREATE_GRAPH_MATRIX(n, val = None):
    g = [[val for i in range(n)] for j in range(n)]
    return g

def COPY_GRAPH_MATRIX(G):
    n = len(G)
    g = CREATE_GRAPH_MATRIX(n)
    for i in range(n):
        for j in range(n):
            g[i][j] = G[i][j]
    return g

def GRAPH_CONNECT(G, i, j, weight):
    G[i-1][j-1] = weight

def EXTEND_SHORTEST_PATHS(L, W):
    n = len(L)
    rL = CREATE_GRAPH_MATRIX(n, float("inf"))
    for i in range(n):
        for j in range(n):
            for k in range(n):
                #print(i, j, k, rL[i][j], L[i][k], W[k][j])
                rL[i][j] = min(rL[i][j], L[i][k] + W[k][j])
                #print(rL[i][j])
            #print("-------------")
    return rL

def SLOW_ALL_PAIRS_SHORTEST_PATHS(W):
    n = len(W)
    L = COPY_GRAPH_MATRIX(W)
    for m in range(1, n):
        L = EXTEND_SHORTEST_PATHS(L, W)
        #PRINT_GRAPH_MATRIX(L)
        #print("=================")
    return L

def FASTER_ALL_PAIRS_SHORTEST_PATHS(W):
    n = len(W)
    L = COPY_GRAPH_MATRIX(W)
    m = 1
    while m < n:
        L = EXTEND_SHORTEST_PATHS(L, L)
        m = 2*m
    return L


if __name__ == "__main__":
    G = CREATE_GRAPH_MATRIX(5, float("inf"))
    GRAPH_CONNECT(G, 1, 1, 0)
    GRAPH_CONNECT(G, 2, 2, 0)
    GRAPH_CONNECT(G, 3, 3, 0)
    GRAPH_CONNECT(G, 4, 4, 0)
    GRAPH_CONNECT(G, 5, 5, 0)
    GRAPH_CONNECT(G, 1, 2, 3)
    GRAPH_CONNECT(G, 1, 3, 8)
    GRAPH_CONNECT(G, 1, 5, -4)
    GRAPH_CONNECT(G, 2, 4, 1)
    GRAPH_CONNECT(G, 2, 5, 7)
    GRAPH_CONNECT(G, 3, 2, 4)
    GRAPH_CONNECT(G, 4, 1, 2)
    GRAPH_CONNECT(G, 4, 3, -5)
    GRAPH_CONNECT(G, 5, 4, 6)

    PRINT_GRAPH_MATRIX(G)
    print("====================")
    L = SLOW_ALL_PAIRS_SHORTEST_PATHS(G)
    PRINT_GRAPH_MATRIX(L)
    print("====================")
    L = FASTER_ALL_PAIRS_SHORTEST_PATHS(G)
    PRINT_GRAPH_MATRIX(L)

显而易见算法效率分别为theta(n^4)和theta(n^3lgn).

25.2 Floyd-Warshall 算法

依然是动态规划,但是对于最优解结构的不同处理会导致不同的结果:

算法导论 第二十五章 所有节点对的最短路径问题_第7张图片

算法导论 第二十五章 所有节点对的最短路径问题_第8张图片

其实思路就是看看经过每个节点,看看是否对最短路径是否有改善。实现如下:

def PRINT_GRAPH_MATRIX(G):
    format_str = "%-8.2f" * len(G)
    for row in G:
        print(format_str % tuple(row))

def CREATE_GRAPH_MATRIX(n, val = None):
    g = [[val for i in range(n)] for j in range(n)]
    return g

def COPY_GRAPH_MATRIX(G):
    n = len(G)
    g = CREATE_GRAPH_MATRIX(n)
    for i in range(n):
        for j in range(n):
            g[i][j] = G[i][j]
    return g

def GRAPH_CONNECT(G, i, j, weight):
    G[i-1][j-1] = weight


def INIT_PI_FORM_WEIGHT(W):
    n = len(W)
    pi =  CREATE_GRAPH_MATRIX(n)
    for i in range(n):
        for j in range(n):
            if i == j:
                continue
            elif W[i][j] != float("inf"):
                pi[i][j] = i
    return pi

def FLOYD_WARSHALL(W):
    n = len(W)
    D1 = COPY_GRAPH_MATRIX(W)
    P1 = INIT_PI_FORM_WEIGHT(W)
    for k in range(n):
        D2 = CREATE_GRAPH_MATRIX(n)
        P2 = CREATE_GRAPH_MATRIX(n)
        for i in range(n):
            for j in range(n):
                if D1[i][j] <= D1[i][k] + D1[k][j]:
                    P2[i][j] = P1[i][j]
                    D2[i][j] = D1[i][j]
                else:
                    P2[i][j] = P1[k][j]
                    D2[i][j] = D1[i][k] + D1[k][j]
                
        D1 = D2
        P1 = P2
    return D1, P1


if __name__ == "__main__":
    G = CREATE_GRAPH_MATRIX(5, float("inf"))
    GRAPH_CONNECT(G, 1, 1, 0)
    GRAPH_CONNECT(G, 2, 2, 0)
    GRAPH_CONNECT(G, 3, 3, 0)
    GRAPH_CONNECT(G, 4, 4, 0)
    GRAPH_CONNECT(G, 5, 5, 0)
    GRAPH_CONNECT(G, 1, 2, 3)
    GRAPH_CONNECT(G, 1, 3, 8)
    GRAPH_CONNECT(G, 1, 5, -4)
    GRAPH_CONNECT(G, 2, 4, 1)
    GRAPH_CONNECT(G, 2, 5, 7)
    GRAPH_CONNECT(G, 3, 2, 4)
    GRAPH_CONNECT(G, 4, 1, 2)
    GRAPH_CONNECT(G, 4, 3, -5)
    GRAPH_CONNECT(G, 5, 4, 6)

    PRINT_GRAPH_MATRIX(G)
    print("====================")
    P = INIT_PI_FORM_WEIGHT(G)
    print(P)
    print("====================")
    G,P = FLOYD_WARSHALL(G)
    print(P)
    print("====================")
    PRINT_GRAPH_MATRIX(G)
    print("====================")
有向图的闭包传递就是可到达性的问题。用布尔运算可以减少存储需求。

25.3 用于稀疏图的Johnson算法

算法导论 第二十五章 所有节点对的最短路径问题_第9张图片

算法导论 第二十五章 所有节点对的最短路径问题_第10张图片

算法导论 第二十五章 所有节点对的最短路径问题_第11张图片

算法实现如下:

def PARENT(i):
    return (i-1) // 2

def LEFT(i):
    return i*2 + 1

def RIGHT(i):
    return i*2 + 2

def MIN_HEAPIFY(A, i, size):
    l = LEFT(i)
    r = RIGHT(i)
    
    if l < size and A[l] < A[i]:
        largest = l
    else:
        largest = i
    if r < size and A[r] < A[largest]:
        largest = r

    if largest != i:
        temp = A[i]
        A[i] = A[largest]
        A[largest] = temp
        MIN_HEAPIFY(A, largest, size)
    
    
def BUILD_MIN_HEAP(A):
    size = len(A)
    for i in range(len(A)//2, -1, -1):
        MIN_HEAPIFY(A, i, size)

def HEAP_MINIMUM(A):
    return A[0]

def HEAP_EXTRACT_MIN(A, size):
    assert(size > 0)
    iMIN = A[0]
    size = size - 1
    A[0] = A[size]
    MIN_HEAPIFY(A, 0, size)
    return iMIN

def HEAP_DECREASE_KEY(A, i, key):
    assert(A[i] == key)
    A[i] = key
    while i > 0 and PARENT(i) >= 0 and A[PARENT(i)] > A[i]:
        temp = A[i]
        A[i] = A[PARENT(i)]
        A[PARENT(i)] = temp
        i = PARENT(i)

def MIN_HEAP_INSERT(A, key):
    A.append(float("inf"))
    HEAP_DECREASE_KEY(A, len(A)-1, key)


#模拟获取index的功能,假设为常数时间
def HEAP_INDEX(A, size, x):
    for i in range(0, size):
        if A[i] == x:
            return i

#======================================================
WHITE, GRAY, BLACK = (0, 1, 2)
def PRINT_GRAPH_MATRIX(G):
    format_str = "%-8.2f" * len(G)
    for row in G:
        print(format_str % tuple(row))

def CREATE_GRAPH_MATRIX(n, val = None):
    g = [[val for i in range(n)] for j in range(n)]
    return g

class Vertex:  
    def __init__(self, u):  
        self.value = u
        self.vertexs = []
        self.isInGraph = False
        self.pi = None
        self.d = float("inf")
        self.h = float("inf")
        self.color = WHITE

    def __lt__(self, u):
        return self.d < u.d
  
class Edge:  
    def __init__(self, u, v, w):  
        self.fromV = u  
        self.toV = v
        self.weight = w

class Graph:
    def __init__(self):  
        self.vertexs = []
        self.edges = []

def weight(edges, u, v):
    for e in edges:
        if e.fromV == u and e.toV == v:
            return e.weight
    print(u.value, v.value)
    return None

def INITGRAPH(G, edges):
    for e in edges:
        if not e.fromV.isInGraph:
            G.vertexs.append(e.fromV)
            e.fromV.isInGraph = True
        if not e.toV.isInGraph:
            G.vertexs.append(e.toV)
            e.toV.isInGraph = True
        
        G.edges.append(e)
        e.fromV.vertexs.append(e.toV)

def INITIALIZE_SINGLE_SOURCE(G, s):
    for v in G.vertexs:
        v.d = float("inf")
        v.pi = None
    s.d = 0
    s.pi = None

def DIJKSTRA(G, s):
    def RELAX(Q, size, u, v, edges):
        if v.d > u.d + weight(edges, u, v):
            index = HEAP_INDEX(Q, size, v)
            v.d = u.d + weight(edges, u, v)
            HEAP_DECREASE_KEY(Q, index, v)
            v.pi = u
            print("relax", u.value, v.value, v.d)
    
    INITIALIZE_SINGLE_SOURCE(G, s)
    S = []
    Q = []
    for v in G.vertexs:
        Q.append(v)
    size = len(Q)
    BUILD_MIN_HEAP(Q)
    while size != 0:
        u = HEAP_EXTRACT_MIN(Q, size)
        size = size - 1
        S.append(u)
        for v in u.vertexs:
            RELAX(Q, size, u, v, G.edges)

def BELLMAN_FORD(G, s):
    def RELAX(u, v, edges):
        if v.d > u.d + weight(edges, u, v):
            v.d = u.d + weight(edges, u, v)
            v.pi = u
            print(u.value, v.value, v.d)
    INITIALIZE_SINGLE_SOURCE(G, s)
    for i in range(1, len(G.vertexs)-1):
        for edge in G.edges:
            RELAX(edge.fromV, edge.toV, G.edges)
    for edge in G.edges:
        if edge.toV.d > edge.fromV.d + weight(G.edges, edge.fromV, edge.toV):
            return False
    return True

def INSERT_SOURCE_VERTEX(G):
    s = Vertex(0)
    for v in G.vertexs:
        G.edges.append(Edge(s, v, 0))
        s.vertexs.append(v)
    s.isInGraph = True
    G.vertexs.append(s)
    return s

def CREATE_NEW_WEIGHT_GRAPH(G):
    new_edges = []
    old_edges = G.edges
    for v in G.vertexs:
        v.h = v.d
    for e in old_edges:
        w = e.weight + e.fromV.h - e.toV.h
        new_edges.append(Edge(e.fromV, e.toV, w))

    G.edges = new_edges
    return old_edges

def JOHNSON(G):
    n = len(G.vertexs)
    s = INSERT_SOURCE_VERTEX(G)
    if BELLMAN_FORD(G, s):
        CREATE_NEW_WEIGHT_GRAPH(G)
        D = CREATE_GRAPH_MATRIX(n, float("inf"))
        for u in G.vertexs:
            if u == s:
                continue
            DIJKSTRA(G, u)
            for v in G.vertexs:
                if v == s:
                    continue
                D[u.value-1][v.value-1] = v.d + v.h - u.h
        return D

if __name__ == "__main__":
    node1 = Vertex(1)
    node2 = Vertex(2)
    node3 = Vertex(3)
    node4 = Vertex(4)
    node5 = Vertex(5)

    edges = []
    edges.append(Edge(node1, node2, 3))
    edges.append(Edge(node1, node3, 8))
    edges.append(Edge(node1, node5, -4))
    edges.append(Edge(node2, node4, 1))
    edges.append(Edge(node2, node5, 7))
    edges.append(Edge(node3, node2, 4))
    edges.append(Edge(node4, node1, 2))
    edges.append(Edge(node4, node3, -5))
    edges.append(Edge(node5, node4, 6))

    G = Graph()  
    INITGRAPH(G, edges)
    D = JOHNSON(G)
    PRINT_GRAPH_MATRIX(D)

习题解答




你可能感兴趣的:(算法笔记)