Bellman-Ford algorithm
This commit is contained in:
parent
ca5ac0d7a7
commit
d4dd36b4f4
258
luku13.tex
258
luku13.tex
|
@ -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{Bellman–Fordin algoritmi}
|
\section{Bellman–Ford algorithm}
|
||||||
|
|
||||||
\index{Bellman–Fordin algoritmi}
|
\index{Bellman–Ford algorithm}
|
||||||
|
|
||||||
\key{Bellman–Fordin algoritmi} etsii
|
The \key{Bellman–Fordin 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 Bellman–Fordin
|
Let's consider how the Bellman–Ford 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 Bellman–Fordin algoritmin toteutus
|
The following implementation of the
|
||||||
etsii lyhimmät polut solmusta $x$
|
Bellman–Ford 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}
|
||||||
|
|
||||||
Bellman–Fordin algoritmin avulla voi myös tarkastaa,
|
Using the Bellman–Ford 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
|
||||||
Bellman–Fordin algoritmilla
|
using the Bellman–Ford 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 Bellman–Fordin algoritmin muunnelma,
|
is a variation for the Bellman–Ford 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 Bellman–Fordin algoritmin.
|
that make the algorithm as slow as the
|
||||||
|
standard Bellman–Ford algorithm.
|
||||||
|
|
||||||
\section{Dijkstran algoritmi}
|
\section{Dijkstran algoritmi}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue