Corrections

This commit is contained in:
Antti H S Laaksonen 2017-02-06 02:04:46 +02:00
parent 6bb73a5c3e
commit 8781c5972f
1 changed files with 84 additions and 86 deletions

View File

@ -3,10 +3,10 @@
In this chapter, we focus on two classes of directed graphs: In this chapter, we focus on two classes of directed graphs:
\begin{itemize} \begin{itemize}
\item \key{Acyclic graph}: \item \key{Acyclic graphs}:
There are no cycles in the graph, There are no cycles in the graph,
so there is no path from any node to itself. so there is no path from any node to itself.
\item \key{Successor graph}: \item \key{Successor graphs}:
The outdegree of each node is 1, The outdegree of each node is 1,
so each node has a unique successor. so each node has a unique successor.
\end{itemize} \end{itemize}
@ -21,8 +21,8 @@ on the special properties of the graphs.
A \key{topological sort} is a ordering A \key{topological sort} is a ordering
of the nodes of a directed graph of the nodes of a directed graph
where node $a$ is always before node $b$ such that if there is a path from node $a$ to node $b$,
if there is a path from node $a$ to node $b$. then node $a$ appears before node $b$ in the ordering.
For example, for the graph For example, for the graph
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.9] \begin{tikzpicture}[scale=0.9]
@ -63,22 +63,21 @@ $[4,1,5,2,3,6]$:
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
A topological sort always exists An acyclic graph always has a topological sort.
if the graph is acyclic.
However, if the graph contains a cycle, However, if the graph contains a cycle,
it is not possible to find a topological sort it is not possible to form a topological sort,
because no node in the cycle can appear because no node in the cycle can appear
before other nodes in the cycle in the ordering. before other nodes in the cycle.
It turns out that we can use depth-first search It turns out that depth-first search can be used
to both construct a topological sort or find out to both check if a directed graph contains a cycle
that it is not possible because the graph contains a cycle. and, if it does not contain a cycle, to construct a topological sort.
\subsubsection{Algorithm} \subsubsection{Algorithm}
The idea is to go through the nodes in the graph The idea is to go through the nodes in the graph
and always begin a depth-first search if the and always begin a depth-first search at the current node
node has not been processed yet. if it has not been processed yet.
During each search, the nodes have three possible states: During the searches, the nodes have three possible states:
\begin{itemize} \begin{itemize}
\item state 0: the node has not been processed (white) \item state 0: the node has not been processed (white)
@ -88,18 +87,18 @@ During each search, the nodes have three possible states:
Initially, the state of each node is 0. Initially, the state of each node is 0.
When a search reaches a node for the first time, When a search reaches a node for the first time,
the state of the node becomes 1. its state becomes 1.
Finally, after all neighbors of a node have Finally, after all successors of the node have
been processed, the state of the node becomes 2. been processed, the state of the node becomes 2.
If the graph contains a cycle, we will realize this If the graph contains a cycle, we will find out this
during the search because sooner or later during the search, because sooner or later
we will arrive at a node whose state is 1. we will arrive at a node whose state is 1.
In this case, it is not possible to construct a topological sort. In this case, it is not possible to construct a topological sort.
If the graph doesn't contain a cycle, we can construct If the graph does not contain a cycle, we can construct
a topological sort by adding each node to the end of a list a topological sort by maintaining a list of nodes and
when its state becomes 2. adding each node to the list when the state of the node becomes 2.
This list in reverse order is a topological sort. This list in reverse order is a topological sort.
\subsubsection{Example 1} \subsubsection{Example 1}
@ -129,7 +128,7 @@ from node 1 to node 6:
\end{center} \end{center}
Now node 6 has been processed, so it is added to the list. Now node 6 has been processed, so it is added to the list.
After this, the search returns back: After this, also nodes 3, 2 and 1 are added to the list:
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.9] \begin{tikzpicture}[scale=0.9]
@ -150,7 +149,7 @@ After this, the search returns back:
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
At this point, the list contains values $[6,3,2,1]$. At this point, the list is $[6,3,2,1]$.
The next search begins at node 4: The next search begins at node 4:
\begin{center} \begin{center}
@ -204,9 +203,9 @@ but there can be several topological sorts for a graph.
\subsubsection{Example 2} \subsubsection{Example 2}
Let's consider another example where we can't Let us now consider a graph for which we
construct a topological sort because there is a cannot construct a topological sort,
cycle in the graph: because there is a cycle in the graph:
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.9] \begin{tikzpicture}[scale=0.9]
@ -226,7 +225,7 @@ cycle in the graph:
\path[draw,thick,->,>=latex] (3) -- (6); \path[draw,thick,->,>=latex] (3) -- (6);
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
Now the search proceeds as follows: The search proceeds as follows:
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.9] \begin{tikzpicture}[scale=0.9]
\node[draw, circle,fill=gray!20] (1) at (1,5) {$1$}; \node[draw, circle,fill=gray!20] (1) at (1,5) {$1$};
@ -246,31 +245,29 @@ Now the search proceeds as follows:
\path[draw=red,thick,->,line width=2pt] (5) -- (2); \path[draw=red,thick,->,line width=2pt] (5) -- (2);
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
The search reaches node 2 whose state is 1 The search reaches node 2 whose state is 1,
which means the graph contains a cycle. which means the graph contains a cycle.
In this case, the cycle is $2 \rightarrow 3 \rightarrow 5 \rightarrow 2$. In this example, the cycle is $2 \rightarrow 3 \rightarrow 5 \rightarrow 2$.
\section{Dynamic programming} \section{Dynamic programming}
If a directed graph is acyclic, If a directed graph is acyclic,
dynamic programming can be applied to it. dynamic programming can be applied to it.
For example, we can solve the following For example, we can efficiently solve the following
problems concerning paths from a starting node problems concerning paths from a starting node
to an ending node efficiently in $O(n+m)$ time: to an ending node:
\begin{itemize} \begin{itemize}
\item how many different paths are there? \item how many different paths are there?
\item what is the shortest/longest path? \item what is the shortest/longest path?
\item what is the minimum/maximum number of edges in a path? \item what is the minimum/maximum number of edges in a path?
\item which nodes certainly appear in the path? \item which nodes certainly appear in any path?
\end{itemize} \end{itemize}
\subsubsection{Counting the number of paths} \subsubsection{Counting the number of paths}
As an example, let's calculate the number of paths As an example, let us calculate the number of paths
from a starting node to an ending node from node 4 to node 6 in the following graph:
in a directed, acyclic graph.
For example, in the graph
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.9] \begin{tikzpicture}[scale=0.9]
@ -290,7 +287,7 @@ For example, in the graph
\path[draw,thick,->,>=latex] (3) -- (6); \path[draw,thick,->,>=latex] (3) -- (6);
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
there are 3 paths from node 4 to node 6: There are a total of three such paths:
\begin{itemize} \begin{itemize}
\item $4 \rightarrow 1 \rightarrow 2 \rightarrow 3 \rightarrow 6$ \item $4 \rightarrow 1 \rightarrow 2 \rightarrow 3 \rightarrow 6$
\item $4 \rightarrow 5 \rightarrow 2 \rightarrow 3 \rightarrow 6$ \item $4 \rightarrow 5 \rightarrow 2 \rightarrow 3 \rightarrow 6$
@ -298,8 +295,8 @@ there are 3 paths from node 4 to node 6:
\end{itemize} \end{itemize}
The idea is to go through the nodes in a topological sort, The idea is to go through the nodes in a topological sort,
and calculate for each node the total number of paths and calculate for each node the total number of paths
that reach the node from different directions. that arrive at the node from different directions.
In this case, the topological sort is as follows: A topological sort for the above graph is as follows:
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.9] \begin{tikzpicture}[scale=0.9]
@ -319,7 +316,7 @@ In this case, the topological sort is as follows:
\path[draw,thick,->,>=latex] (3) -- (6); \path[draw,thick,->,>=latex] (3) -- (6);
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
The numbers of paths are as follows: Hence, the numbers of paths are as follows:
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.9] \begin{tikzpicture}[scale=0.9]
@ -347,11 +344,10 @@ The numbers of paths are as follows:
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
For example, there is an edge to node 2 from nodes 1 and 5. For example, there are two paths from node 4 to node 2,
There is one path from node 4 to both node 1 and 5, because we can arrive at node 2 from node 1 or node 5,
so there are two paths from node 4 to node 2. there is one path from node 4 to node 1
Correspondingly, there is an edge to node 3 from nodes 2 and 5 and there is one path from node 4 to node 5.
that correspond to two and one paths from node 4.
\subsubsection{Extending Dijkstra's algorithm} \subsubsection{Extending Dijkstra's algorithm}
@ -361,7 +357,7 @@ A by-product of Dijkstra's algorithm is a directed, acyclic
graph that shows for each node in the original graph graph that shows for each node in the original graph
the possible ways to reach the node using a shortest path the possible ways to reach the node using a shortest path
from the starting node. from the starting node.
Dynamic programming can be applied also to this graph. Dynamic programming can be applied to this graph.
For example, in the graph For example, in the graph
\begin{center} \begin{center}
\begin{tikzpicture} \begin{tikzpicture}
@ -380,8 +376,7 @@ For example, in the graph
\path[draw,thick,-] (2) -- node[font=\small,label=above:2] {} (3); \path[draw,thick,-] (2) -- node[font=\small,label=above:2] {} (3);
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
the following edges can belong to the shortest paths shortest paths from node 1 may use the following edges:
from node 1:
\begin{center} \begin{center}
\begin{tikzpicture} \begin{tikzpicture}
\node[draw, circle] (1) at (0,0) {$1$}; \node[draw, circle] (1) at (0,0) {$1$};
@ -438,7 +433,7 @@ using coins
$\{c_1,c_2,\ldots,c_k\}$. $\{c_1,c_2,\ldots,c_k\}$.
In this case, we can construct a graph where In this case, we can construct a graph where
each node corresponds to a sum of money, each node corresponds to a sum of money,
and the edges show how we can choose coins. and the edges show how the coins can be chosen.
For example, for coins $\{1,3,4\}$ and $x=6$, For example, for coins $\{1,3,4\}$ and $x=6$,
the graph is as follows: the graph is as follows:
\begin{center} \begin{center}
@ -481,11 +476,11 @@ equals the total number of solutions.
\index{functional graph} \index{functional graph}
For the rest of the chapter, For the rest of the chapter,
we concentrate on \key{successor graphs} we will concentrate on \key{successor graphs}
where the outdegree of each node is 1, i.e., where the outdegree of each node is 1, which means that
exactly one edge begins at the node. exactly one edge starts at the node.
Thus, the graph consists of one or more A successor graph consists of one or more
components, and each component contains components, each of which contains
one cycle and some paths that lead to it. one cycle and some paths that lead to it.
Successor graphs are sometimes called Successor graphs are sometimes called
@ -535,9 +530,8 @@ Since each node in a successor graph has a
unique successor, we can define a function $f(x,k)$ unique successor, we can define a function $f(x,k)$
that returns the node that we will reach if that returns the node that we will reach if
we begin at node $x$ and walk $k$ steps forward. we begin at node $x$ and walk $k$ steps forward.
For example, in the above graph $f(4,6)=2$ For example, in the above graph $f(4,6)=2$,
because by walking 6 steps from node 4, because we will reach node 2 by walking 6 steps from node 4:
we will reach node 2:
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.9] \begin{tikzpicture}[scale=0.9]
@ -559,14 +553,14 @@ we will reach node 2:
\end{center} \end{center}
A straightforward way to calculate a value $f(x,k)$ A straightforward way to calculate a value $f(x,k)$
is to walk through the path step by step which takes $O(k)$ time. is to start at node $x$ and walk $k$ steps forward, which takes $O(k)$ time.
However, using preprocessing, we can calculate any However, using preprocessing, any value $f(x,k)$
value $f(x,k)$ in only $O(\log k)$ time. can be calculated in only $O(\log k)$ time.
The idea is to precalculate all values $f(x,k)$ where The idea is to precalculate all values $f(x,k)$ where
$k$ is a power of two and at most $u$ where $u$ is $k$ is a power of two and at most $u$, where $u$ is
the maximum number of steps we will ever walk. the maximum number of steps we will ever walk.
This can be done efficiently because This can be efficiently done, because
we can use the following recursion: we can use the following recursion:
\begin{equation*} \begin{equation*}
@ -576,8 +570,8 @@ we can use the following recursion:
\end{cases} \end{cases}
\end{equation*} \end{equation*}
Precalculating values $f(x,k)$ takes $O(n \log u)$ time Precalculating values $f(x,k)$ takes $O(n \log u)$ time,
because we calculate $O(\log u)$ values for each node. because $O(\log u)$ values are calculated for each node.
In the above graph, the first values are as follows: In the above graph, the first values are as follows:
\begin{center} \begin{center}
@ -593,7 +587,7 @@ $\cdots$ \\
\end{center} \end{center}
After this, any value $f(x,k)$ can be calculated After this, any value $f(x,k)$ can be calculated
by presenting the value $k$ as a sum of powers of two. by presenting the number of steps $k$ as a sum of powers of two.
For example, if we want to calculate the value $f(x,11)$, For example, if we want to calculate the value $f(x,11)$,
we first form the representation $11=8+2+1$. we first form the representation $11=8+2+1$.
Using this, Using this,
@ -602,7 +596,7 @@ For example, in the above graph
\[f(4,11)=f(f(f(4,8),2),1)=5.\] \[f(4,11)=f(f(f(4,8),2),1)=5.\]
Such a representation always consists of Such a representation always consists of
$O(\log k)$ parts so calculating a value $f(x,k)$ $O(\log k)$ parts, so calculating a value $f(x,k)$
takes $O(\log k)$ time. takes $O(\log k)$ time.
\section{Cycle detection} \section{Cycle detection}
@ -610,10 +604,13 @@ takes $O(\log k)$ time.
\index{cycle} \index{cycle}
\index{cycle detection} \index{cycle detection}
Interesting questions in a successor graph are Consider a successor graph that only contains
which node is the first node in the cycle a path that ends in a cycle.
if we begin our walk at node $x$, There are two interesting questions:
and what is the size of the cycle. if we begin our walk at the first node,
what is the first node in the cycle
and how many nodes does the cycle contain?
For example, in the graph For example, in the graph
\begin{center} \begin{center}
@ -633,14 +630,15 @@ For example, in the graph
\path[draw,thick,->] (6) -- (4); \path[draw,thick,->] (6) -- (4);
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
if we begin at node 1, the first node that belongs we begin our walk at node 1,
to the cycle is node 4, and the cycle consists the first node that belongs to the cycle is node 4, and the cycle consists
of three nodes (4, 5 and 6). of three nodes (4, 5 and 6).
An easy way to detect a cycle is to walk in the An easy way to detect the cycle is to walk in the
graph beginning from node $x$ and keep track of graph and keep track of
all visited nodes. Once we will visit a node all nodes that have been visited. Once a node is visited
for the second time, the first node in the cycle has been found. for the second time, we can conclude
that the node in question is the first node in the cycle.
This method works in $O(n)$ time and also uses This method works in $O(n)$ time and also uses
$O(n)$ memory. $O(n)$ memory.
@ -648,7 +646,7 @@ However, there are better algorithms for cycle detection.
The time complexity of those algorithms is still $O(n)$, The time complexity of those algorithms is still $O(n)$,
but they only use $O(1)$ memory. but they only use $O(1)$ memory.
This is an important improvement if $n$ is large. This is an important improvement if $n$ is large.
Next we will learn Floyd's algorithm that Next we will discuss Floyd's algorithm that
achieves these properties. achieves these properties.
\subsubsection{Floyd's algorithm} \subsubsection{Floyd's algorithm}
@ -659,11 +657,11 @@ achieves these properties.
in the graph using two pointers $a$ and $b$. in the graph using two pointers $a$ and $b$.
Both pointers begin at the starting node Both pointers begin at the starting node
of the graph. of the graph.
Then, on each turn, pointer $a$ walks Then, on each turn, the pointer $a$ walks
one step forward, while pointer $b$ one step forward and the pointer $b$
walks two steps forward. walks two steps forward.
The search continues like that until The process continues until
the pointers will meet each other: the pointers meet each other:
\begin{lstlisting} \begin{lstlisting}
a = f(x); a = f(x);
@ -674,13 +672,13 @@ while (a != b) {
} }
\end{lstlisting} \end{lstlisting}
At this point, pointer $a$ has walked $k$ steps, At this point, the pointer $a$ has walked $k$ steps
and pointer $b$ has walked $2k$ steps and the pointer $b$ has walked $2k$ steps,
where the length of the cycle divides $k$. so the length of the cycle divides $k$.
Thus, the first node that belongs to the cycle Thus, the first node that belongs to the cycle
can be found by moving pointer $a$ to the can be found by moving the pointer $a$ to the
starting node and advancing the pointers starting node and advancing the pointers
step by step until they will meet again: step by step until they meet again:
\begin{lstlisting} \begin{lstlisting}
a = x; a = x;