Shortest Path Algorithm

Shortest Path Algorithm

1. Dijkstra's algorithm

1.1 Description

Application scope

  • It is available for both directed and non-directed networks with non-negative weights.

  • It can find a cluster of shortest paths from one single source to all other destinations.

  • It can also find a cluster of shortest paths to one destination from to all other sources.

\[V(i) = \min_{j \in P(i)} \{V(j) + d_{ji} \} \]

1.2 Procedure of algorithm

Denote:

  • \(i=0,1,2,\cdots, n-1\) : node index
    • \(i=0\) : the source node
  • \(V(i)\) : distance of shortest path \(0 \to i\) (from the source node \(0\) to the node \(i\))
  • \(P(i)\) : the predecessor node of node \(i\)
  • \([V(i), P(i)]\) : label of node \(i\)
    • \([V(i), P(i)]\) : permanent label
    • \([V(i), P(i)]^{T}\) : temporary label
  • \(S\) : set of nodes with permanent label
  • \(T\) : set of nodes with temporary label

Steps:

  • Step 0: Initiation. Label the source node (node \(0\)) with the permanent label:

    \[[V(0)=0, P(0)=-] \]

    • Set

    \[i=0, \ S= \{i\}, \ T=\varnothing \]

  • Step 1: Check the stopping criterion.

    • If every node has the permanent label, then stop. Otherwise, go to Step 2.
  • Step 2: Scan node and update the node label.

    • For each node \(j\) without the permanent label that can be reached from node \(i\), do the following two substeps:
  • Step 2.1: If node \(j\) was not labeled before, then compute the temporary labels:

    \[[V(j),P(j)]^{T} \quad \text{where } \ V(j)=V(i)+d_{ij}, \ P(j)=i, \ T=T+\{j\} \]

  • Step 2.2: If node \(j\) has a temporary label \([V(j),P(j)]^{T}\), and If \(V(j)>V(i)+d_{ij}\), then update

    \[V(j)=V(i)+d_{ij}, \ P(j)=i \]

    • Else, if node \(j\) has a temporary label \([V(j),P(j)]^{T}\), but \(V(j) \leq V(i)+d_{ij}\), don't need to update the label of node \(j\).
  • Step 3: choose the next node.

    • Select a node \(r\) in the temporary label set \(T\) with the smallest temporary label \(V(r)\)

    \[r = \arg \min_{r \in T} V(r) \quad \text{ and } \quad i = r \]

    • Node \(r\) will has the permanent label: \([V(r),P(r)]\).

    • And update sets:

    \[S=S+ \{r \}, \ T=T- \{r\} \]

    • Then, go to Step 1.

1.3 Examples

  • Example 6.3-4 in Ref [1] p.p. 256-257

  • Javascript Demo in Ref [2]

1.4 Algorithm

Denote:

  • \(R\) : set of nodes without permanent label
    • \(R = N - S\)
  • \(\mathbf{D}\) : distance matrix
  • \(d_t\) temporary label of node \(i\)
  • \(p_t\) temporary label of node \(i\)

\[\begin{align*} \hline & \text{Dijkstra}(\mathbf{D}) \\ \hline & i = 0, \ V(0) = 0, \ P(0) = - \\ & R \leftarrow R - \{0\} \\ & \text{For } \ k = 1, \cdots, n-1 \ (\text{equal to : while } R = \varnothing) : \\ & \qquad d_{t} \leftarrow +\infty, \ \ p_{t} \leftarrow j \\ & \qquad \text{For } \ j \ \text{ in } \ R : \\ & \qquad \qquad \text{If } \ V(j) > V(i) + d_{ij} : \\ & \qquad \qquad \qquad V(j) \leftarrow V(i) + d_{ij} \\ & \qquad \qquad \qquad P(j) \leftarrow i \\ & \qquad \qquad \text{If } \ d_t > V(j) : \\ & \qquad \qquad \qquad d_t \leftarrow V(j) \\ & \qquad \qquad \qquad p_t \leftarrow j \\ & \qquad i \leftarrow p_t \\ & \qquad R \leftarrow R - \{i\} \\ \hline \end{align*} \]

The complexity of this algorithm is \(\mathcal{O}(|V|^2+|E|) = \mathcal{O}(|V|^2)\), where \(|V|\) is the number of nodes, and \(|E|\) is the number of edges.

1.5 Implement by Python

Using networkx structure


Using adjacency matrix

Codes: Dijkstra's algorithm by using adjacency matrix

点击查看代码
def ForwardDPforSP(dist_mat, source=0):
    '''
    dist_mat : 2-d numpy.ndarray, shape of (n, n)
        distance matrix
    source : start node (index of 'dist_mat')
        default '0'
    '''
    n = dist_mat.shape[0]

    # minimal distance from node 0 to i 
    V = [np.inf for i in range(n)] # V(i)
    V[source] = 0.
    P = ['' for i in range(n)] 

    # temporary label set
    R = list(range(n))
    R.remove(source)

    i = source 
    for k in range(n-1):
        d_m = np.inf
        for j in R: # V(source) = 0
            if V[j] > V[i] + dist_mat[i, j]:
                V[j] = V[i] + dist_mat[i, j]
                P[j] = i
            if d_m > V[j]:
                d_m = V[j]
                p_t = j
        i = p_t
        R.remove(i)
    return P, V

Using constructed functions in NetworkX

Shortest path related document of NetworkX: website

Get weighted shortest path length and predecessors of each nodes

predecessor, distance = nx.dijkstra_predecessor_and_distance(G, source, weight='weight')
# return: dict

Get a special the shortest weighted path and lengths from specific source to target

path = nx.dijkstra_path(G, source, target, weight='weight')
dist = nx.dijkstra_path_length(G, source, target, weight='weight')

Get a cluster of shortest weighted paths and lengths from a source node.

dist, path = nx.single_source_dijkstra(G, source, target=None, weight='weight'))
path = nx.single_source_dijkstra_path(G, source, target=None, weight='weight')
dist = nx.single_source_dijkstra_path_length(G, source, target=None, weight='weight')

Get shortest weighted paths between all nodes.

path = nx.all_pairs_dijkstra_path(G, source=None, target=None, weight='weight', method='dijkstra')
dist = nx.all_pairs_shortest_path_length(G, source=None, target=None, weight='weight', method='dijkstra')
  • Parameters:

    • target: to specify the target node
      • If target=None, return a dict including all results
  • Return:

    • path : list or dict of list, a sequence of node label to indicate the path

Test Cases

Codes: test codes

点击查看代码

2. Floyd's algorithm

also called Floyd-Warshall algorithm

2.1 Description

Application scope

  • It is available for both directed and non-directed networks.
  • It is available for both non-negative and negative weight edge, but with no negative cycles.
  • It can find a cluster of shortest paths between any two node.

\[d_{ij} = \min\{d_{ij}, d_{ik} + d_{kj} \} \]

2.2 Procedure of algorithm

Denote:

  • \(\mathbf{D}\) : Distance matrix
  • \(\mathbf{S}\) : Sequence matrix, each entry represents a node
  • \(s_{ij}=k\) : \(i \to k \to j\) with distance \(d_{ij}\)
  • \(s_{ij}=j\) : \(i \to j\) node \(i\) and \(j\) are connected directly.

Steps

  • Step 0 : Define the starting distance matrix \(\mathbf{D}_0\) and node sequence matrix \(\mathbf{S}_0\) (all diagonal elements are blocked). Set \(k = 1\).

    • Let \(\mathbf{D}_0\) be the distance matrix or adjacency matrix

    • Let \(S_{ij} = j\) if \(D_{ij} \leq +\infty\) or \(S_{ij} = \text{None}\)

  • Step 1 : For each iteration \(k=1,2,\cdot,n\)

    • Define row \(k\) and column \(k\) as pivot row and pivot column. Apply the triple operation to each element \(d_{ij}\) in \(\mathbf{D}_{k-1}\), for all \(i\) and \(j\).

    • If the condition

    \[d_{ik} + d_{jk} \leq d_{ij}, \quad \forall i,j, \text{ and } i \neq k, j \neq k, i \neq j \]

    is satisfied, update \(\mathbf{D}_{k}\) and \(\mathbf{S}_{k}\):

    • (1) Create \(\mathbf{D}_{k}\) by replacing \(d_{ij}\) in \(\mathbf{D}_{k-1}\) with \(d_{ik} + d_{kj}\)

    • (2) Create \(\mathbf{S}_{k}\) by replacing \(s_{ij}\) in \(\mathbf{S}_{k-1}\) with \(k\)

  • Step 2 :Set \(k = k + 1\). If \(k = n + 1\), stop; else go to Step 2.

2.3 Algorithm

\[\begin{align*} \hline & \text{Floyd}(\mathbf{D}, \mathbf{S}) \qquad \qquad \qquad \qquad \qquad \\ \hline & \text{for } i = 1, 2, \cdots, n \\ & \qquad \text{for } j = 1, 2, \cdots, n \\ & \qquad \qquad\text{if } j = i: \text{continue} \\ & \qquad \qquad\text{for } k = 1, \cdots, n \\ & \qquad \qquad \qquad \text{if } \ k = i \ \text{ or } \ k=j : \text{continue} \\ & \qquad \qquad \qquad \text{if } \ d_{ij} > d_{ik} + d_{kj} : \\ & \qquad \qquad \qquad \qquad d_{ij} = d_{ik} + d_{kj} \\ & \qquad \qquad \qquad \qquad s_{ij} = k \\ \hline \end{align*} \]

The complexity of this algorithm is \(\mathcal{O}(|V|^3)\)

2.4 Implement by Python

2.4.1 Using adjacency matrix

def Floyd(D, S):
    n = D.shape[0]
    for i in range(n):
        for j in range(n):
            if j == i: continue
            for k in range(n):
                if k == i: continue
                if D[i,j] > D[i,k] + D[k,j]:
                    D[i,j] = D[i,k] + D[k,j]
                    S[i,j] = k
    return D, S

2.4.2 Using constructed functions in NetworkX

# Return type of dict
distance = nx.floyd_warshal(G, weight='weight')
# Return type of dict
predecessor, distance = nx.floyd_warshall_predecessor_and_distance(G, weight='weight')
# Return type of nd.array
distance = nx.floyd_warshall_numpy(G, weight='weight')

3. Bellman-Ford's Algorithm

Reference

[1] H. A. Taha, "Section 6.3 Shortest-route Problem" in Operations research: an introduction, Tenth edition, Global edition. Harlow, England London New York Boston Amsterdam Munich: Pearson Education, 2017, p.p. 251-265.

[2] Mathematical Programming, website.

[3] Shortest Paht

posted @ 2022-03-30 09:01  veager  阅读(49)  评论(0)    收藏  举报