Bellman-Ford algorithm

This commit is contained in:
Antti H S Laaksonen 2017-01-07 20:08:47 +02:00
parent ca5ac0d7a7
commit d4dd36b4f4
1 changed files with 131 additions and 127 deletions

View File

@ -1,50 +1,48 @@
\chapter{Shortest paths} \chapter{Shortest paths}
\index{lyhin polku@lyhin polku} \index{shortest path}
Lyhimmän polun etsiminen alkusolmusta loppusolmuun Finding the shortest path between two nodes
on keskeinen verkko-ongelma, joka esiintyy usein is an important graph problem that has many
käytännön tilanteissa. applications in practice.
Esimerkiksi tieverkostossa For example, a natural problem in a road network
luonteva ongelma on selvittää, is to calculate the length of the shorthest route
mikä on lyhin reitti kahden kaupungin välillä, between two cities, given the lengths of the roads.
kun tiedossa ovat kaupunkien väliset tiet ja niiden pituudet.
Jos verkon kaarilla ei ole painoja, In an unweighted graph, the length of a path equals
polun pituus on sama kuin kaarten the number of edges in the path and we can
määrä polulla, jolloin lyhimmän polun simply use breadth-first search for finding
voi etsiä leveyshaulla. the shortest path.
Tässä luvussa keskitymme kuitenkin However, in this chapter we concentrate on
tapaukseen, jossa kaarilla on painot. weighted graphs.
Tällöin lyhimpien polkujen etsimiseen In this case we need more sophisticated algorithms
tarvitaan kehittyneempiä algoritmeja. for finding shortest paths.
\section{BellmanFordin algoritmi} \section{BellmanFord algorithm}
\index{BellmanFordin algoritmi} \index{BellmanFord algorithm}
\key{BellmanFordin algoritmi} etsii The \key{BellmanFordin algoritmi} finds the
lyhimmän polun alkusolmusta shortest path from a starting node to all
kaikkiin muihin verkon solmuihin. other nodes in the graph.
Algoritmi toimii kaikenlaisissa verkoissa, The algorithm works in all kinds of graphs,
kunhan verkossa ei ole sykliä, provided that the graph doesn't contain a
jonka kaarten yhteispaino on negatiivinen. cycle with negative length.
Jos verkossa on negatiivinen sykli, If the graph contains a negative cycle,
algoritmi huomaa tilanteen. the algorithm can detect this.
Algoritmi pitää yllä etäisyysarvioita The algorithm keeps track of estimated distances
alkusolmusta kaikkiin muihin verkon solmuihin. from the starting node to other nodes.
Alussa alkusolmun etäisyysarvio on 0 Initially, the estimated distance is 0
ja muiden solmujen etäisyys\-arvio on ääretön. to the starting node and infinite to all other nodes.
Algoritmi parantaa arvioita The algorithm improves the estimates by finding
etsimällä verkosta kaaria, edges that shorten the paths until it is not
jotka lyhentävät polkuja, possible to improve any estimate.
kunnes mitään arviota ei voi enää parantaa.
\subsubsection{Esimerkki} \subsubsection{Example}
Tarkastellaan BellmanFordin Let's consider how the BellmanFord algorithm
algoritmin toimintaa seuraavassa verkossa: works in the following graph:
\begin{center} \begin{center}
\begin{tikzpicture} \begin{tikzpicture}
\node[draw, circle] (1) at (1,3) {1}; \node[draw, circle] (1) at (1,3) {1};
@ -66,15 +64,13 @@ algoritmin toimintaa seuraavassa verkossa:
\path[draw,thick,-] (1) -- node[font=\small,label=above:7] {} (4); \path[draw,thick,-] (1) -- node[font=\small,label=above:7] {} (4);
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
Verkon jokaiseen solmun viereen on merkitty etäisyysarvio. Each node in the graph is assigned an estimated distance.
Alussa alkusolmun etäisyysarvio on 0 Initially, the distance is 0 to the starting node
ja muiden solmujen etäisyysarvio on and infinite to all other nodes.
ääretön.
Algoritmi etsii verkosta kaaria, The algorithm searches for edges that improve the
jotka parantavat etäisyysarvioita. estimated distances.
Aluksi kaikki solmusta 0 lähtevät kaaret First, all edges from node 1 improve the estimates:
parantavat arvioita:
\begin{center} \begin{center}
\begin{tikzpicture} \begin{tikzpicture}
\node[draw, circle] (1) at (1,3) {1}; \node[draw, circle] (1) at (1,3) {1};
@ -100,8 +96,9 @@ parantavat arvioita:
\path[draw=red,thick,->,line width=2pt] (1) -- (4); \path[draw=red,thick,->,line width=2pt] (1) -- (4);
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
Sitten kaaret $2 \rightarrow 5$ ja $3 \rightarrow 4$ After this, edges
parantavat arvioita: $2 \rightarrow 5$ and $3 \rightarrow 4$
improve the estimates:
\begin{center} \begin{center}
\begin{tikzpicture} \begin{tikzpicture}
\node[draw, circle] (1) at (1,3) {1}; \node[draw, circle] (1) at (1,3) {1};
@ -126,7 +123,7 @@ parantavat arvioita:
\path[draw=red,thick,->,line width=2pt] (3) -- (4); \path[draw=red,thick,->,line width=2pt] (3) -- (4);
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
Lopuksi tulee vielä yksi parannus: Finally, there is one more improvment:
\begin{center} \begin{center}
\begin{tikzpicture} \begin{tikzpicture}
\node[draw, circle] (1) at (1,3) {1}; \node[draw, circle] (1) at (1,3) {1};
@ -151,16 +148,15 @@ Lopuksi tulee vielä yksi parannus:
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
Tämän jälkeen mikään kaari After this, no edge improves the estimates.
ei paranna etäisyysarvioita. This means that the distances are final
Tämä tarkoittaa, että etäisyydet and we have successfully
ovat lopulliset, eli joka solmussa calculated the shortest distance
on nyt pienin etäisyys alkusolmusta from the starting node to all other nodes.
kyseiseen solmuun.
Esimerkiksi pienin etäisyys 3 For example, the smallest distance 3
solmusta 1 solmuun 5 toteutuu käyttämällä from node 1 to node 5 corresponds to
seuraavaa polkua: the following path:
\begin{center} \begin{center}
\begin{tikzpicture} \begin{tikzpicture}
@ -188,26 +184,27 @@ seuraavaa polkua:
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
\subsubsection{Toteutus} \subsubsection{Implementation}
Seuraava BellmanFordin algoritmin toteutus The following implementation of the
etsii lyhimmät polut solmusta $x$ BellmanFord algorithm finds the shortest paths
kaikkiin muihin verkon solmuihin. from a node $x$ to all other nodes in the graph.
Koodi olettaa, että verkko on tallennettuna The code assumes that the graph is stored
vieruslistoina taulukossa as adjacency lists in array
\begin{lstlisting} \begin{lstlisting}
vector<pair<int,int>> v[N]; vector<pair<int,int>> v[N];
\end{lstlisting} \end{lstlisting}
niin, että parissa on ensin kaaren kohdesolmu so that each pair contains the target node
ja sitten kaaren paino. and the edge weight.
Algoritmi muodostuu $n-1$ kierroksesta, The algorithm consists of $n-1$ rounds,
joista jokaisella algoritmi käy läpi kaikki and on each round the algorithm goes through
verkon kaaret ja koettaa parantaa etäisyysarvioita. all nodes in the graph and tries to improve
Algoritmi laskee taulukkoon \texttt{e} the estimated distances.
etäisyyden solmusta $x$ kuhunkin verkon solmuun. The algorithm builds an array \texttt{e}
Koodissa oleva alkuarvo $10^9$ kuvastaa that will contain the distance from $x$
ääretöntä. to all nodes in the graph.
The initial value $10^9$ means infinity.
\begin{lstlisting} \begin{lstlisting}
for (int i = 1; i <= n; i++) e[i] = 1e9; for (int i = 1; i <= n; i++) e[i] = 1e9;
@ -221,27 +218,26 @@ for (int i = 1; i <= n-1; i++) {
} }
\end{lstlisting} \end{lstlisting}
Algoritmin aikavaativuus on $O(nm)$, The time complexity of the algorithm is $O(nm)$
koska se muodostuu $n-1$ kierroksesta ja because it consists of $n-1$ rounds and
käy läpi jokaisen kierroksen aikana kaikki $m$ kaarta. iterates through all $m$ nodes during a round.
Jos verkossa ei ole negatiivista sykliä, If there are no negative cycles in the graph,
kaikki etäisyysarviot ovat lopulliset $n-1$ all distances are final after $n-1$ rounds
kierroksen jälkeen, koska jokaisessa lyhimmässä because each shortest path can contain at most $n-1$ edges.
polussa on enintään $n-1$ kaarta.
Käytännössä kaikki lopulliset etäisyysarviot In practice, the final distances can usually
saadaan usein laskettua selvästi alle $n-1$ kierroksessa, be found much faster than in $n-1$ rounds.
joten mahdollinen tehostus algoritmiin on lopettaa heti, Thus, a possible way to make the algorithm more efficient
kun mikään etäisyysarvio ei parane kierroksen aikana. is to stop the algorithm if we can't
improve any distance during a round.
\subsubsection{Negatiivinen sykli} \subsubsection{Negative cycle}
\index{negatiivinen sykli@negatiivinen sykli} \index{negative cycle}
BellmanFordin algoritmin avulla voi myös tarkastaa, Using the BellmanFord algorithm we can also
onko verkossa sykliä, check if the graph contains a cycle with negative length.
jonka pituus on negatiivinen. For example, the graph
Esimerkiksi verkossa
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.9] \begin{tikzpicture}[scale=0.9]
@ -258,45 +254,52 @@ Esimerkiksi verkossa
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
\noindent \noindent
on negatiivinen sykli $2 \rightarrow 3 \rightarrow 4 \rightarrow 2$, contains a negative cycle
jonka pituus on $-4$. $2 \rightarrow 3 \rightarrow 4 \rightarrow 2$
with length $-4$.
Jos verkossa on negatiivinen sykli, If the graph contains a negative cycle,
sen kautta kulkevaa polkua voi lyhentää äärettömästi we can shorten a path that contains the cycle
toistamalla negatiivista sykliä uudestaan ja uudestaan, infinitely many times by repeating the cycle
minkä vuoksi lyhimmän polun käsite ei ole mielekäs. again and again.
Thus, the concept of a shortest path
is not meaningful here.
Negatiivisen syklin voi tunnistaa A negative cycle can be detected
BellmanFordin algoritmilla using the BellmanFord algorithm by
suorittamalla algoritmia $n$ kierrosta. running the algorithm for $n$ rounds.
Jos viimeinen kierros parantaa jotain If the last round improves any distance,
etäisyysarviota, verkossa on negatiivinen sykli. the graph contains a negative cycle.
Huomaa, että algoritmi etsii negatiivista sykliä Note that this algorithm searches for
koko verkon alueelta alkusolmusta välittämättä. a negative cycle in the whole graph
regardless of the starting node.
\subsubsection{SPFA-algoritmi} \subsubsection{SPFA algorithm}
\index{SPFA-algoritmi} \index{SPFA algorithm}
\key{SPFA-algoritmi} (''Shortest Path Faster Algorithm'') The \key{SPFA algoritmi} (''Shortest Path Faster Algorithm'')
on BellmanFordin algoritmin muunnelma, is a variation for the BellmanFord algorithm,
joka on usein alkuperäistä algoritmia tehokkaampi. that is often more efficient than the original algorithm.
Se ei tutki joka kierroksella koko verkkoa läpi It doesn't go through all the edges on each round,
parantaakseen etäisyysarvioita, vaan valitsee but instead, it chooses the edges to be examined
tutkittavat kaaret älykkäämmin. in a more intelligent way.
Algoritmi pitää yllä jonoa solmuista, The algorithm maintains a queue of nodes that might
joiden kautta saattaa pystyä parantamaan etäisyysarvioita. be used for improving the distances.
Algoritmi lisää jonoon aluksi alkusolmun $x$ First, the algorithm adds the starting node $x$
ja valitsee aina seuraavan to the queue.
tutkittavan solmun $a$ jonon alusta. Then, the algorithm always processes the
Aina kun kaari $a \rightarrow b$ parantaa first node in the queue, and when an edge
etäisyysarviota, algoritmi lisää jonoon solmun $b$. $a \rightarrow b$ improves a distance,
node $b$ is added to the end of the queue.
Seuraavassa toteutuksessa jonona on \texttt{queue}-rakenne The following implementation uses a
\texttt{q}. Lisäksi taulukko \texttt{z} kertoo, \texttt{queue} structure \texttt{q}.
onko solmu valmiina jonossa, jolloin algoritmi ei In addition, array \texttt{z} indicates
lisää solmua jonoon uudestaan. if a node is already in the queue,
in which case the algorithm doesn't add
the node to the queue again.
\begin{lstlisting} \begin{lstlisting}
for (int i = 1; i <= n; i++) e[i] = 1e9; for (int i = 1; i <= n; i++) e[i] = 1e9;
@ -314,12 +317,13 @@ while (!q.empty()) {
} }
\end{lstlisting} \end{lstlisting}
SPFA-algoritmin tehokkuus riippuu verkon rakenteesta: The efficiency of the SPFA algorithm depends
algoritmi on keskimäärin hyvin tehokas, mutta on the structure of the graph:
sen pahimman tapauksen aikavaativuus on edelleen the algorithm is usually very efficient,
$O(nm)$ ja on mahdollista but its worst case time complexity is still
laatia syötteitä, jotka saavat algoritmin yhtä hitaaksi $O(nm)$ and it is possible to create inputs
kuin tavallisen BellmanFordin algoritmin. that make the algorithm as slow as the
standard BellmanFord algorithm.
\section{Dijkstran algoritmi} \section{Dijkstran algoritmi}