Corrections

This commit is contained in:
Antti H S Laaksonen 2017-02-17 22:45:59 +02:00
parent c447a6f33b
commit 4e58b65f46
1 changed files with 39 additions and 36 deletions

View File

@ -67,14 +67,14 @@ An acyclic graph always has a topological sort.
However, if the graph contains a cycle, However, if the graph contains a cycle,
it is not possible to form 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. before the other nodes in the cycle.
It turns out that depth-first search can be used It turns out that depth-first search can be used
to both check if a directed graph contains a cycle to both check if a directed graph contains a cycle
and, if it does not contain a cycle, to construct a topological sort. 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 of the graph
and always begin a depth-first search at the current node and always begin a depth-first search at the current node
if it has not been processed yet. if it has not been processed yet.
During the searches, the nodes have three possible states: During the searches, the nodes have three possible states:
@ -89,7 +89,7 @@ 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,
its state becomes 1. its state becomes 1.
Finally, after all successors of the node have Finally, after all successors of the node have
been processed, the state of the node becomes 2. been processed, its state becomes 2.
If the graph contains a cycle, we will find out 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
@ -97,8 +97,8 @@ 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 does not contain a cycle, we can construct If the graph does not contain a cycle, we can construct
a topological sort by maintaining a list of nodes and a topological sort by
adding each node to the list when the state of the node becomes 2. adding each node to a 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}
@ -287,17 +287,19 @@ from node 4 to node 6 in the following graph:
\path[draw,thick,->,>=latex] (3) -- (6); \path[draw,thick,->,>=latex] (3) -- (6);
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
There are a total of three such paths: 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$
\item $4 \rightarrow 5 \rightarrow 3 \rightarrow 6$ \item $4 \rightarrow 5 \rightarrow 3 \rightarrow 6$
\end{itemize} \end{itemize}
The idea is to go through the nodes in a topological sort,
and calculate for each node the total number of paths
that arrive at the node from different directions.
A topological sort for the above graph is as follows:
To count the paths,
we go through the nodes in a topological sort,
and calculate for each node $x$ the number of paths
from node 4 to node $x$.
A topological sort for the above graph is as follows:
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.9] \begin{tikzpicture}[scale=0.9]
\node[draw, circle] (1) at (3,0) {$1$}; \node[draw, circle] (1) at (3,0) {$1$};
@ -316,8 +318,8 @@ A topological sort for the above graph is as follows:
\path[draw,thick,->,>=latex] (3) -- (6); \path[draw,thick,->,>=latex] (3) -- (6);
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
Hence, 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]
\node[draw, circle] (1) at (1,5) {$1$}; \node[draw, circle] (1) at (1,5) {$1$};
@ -344,20 +346,21 @@ Hence, the numbers of paths are as follows:
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
For example, there are two paths from node 4 to node 2, For example, since there are two paths
because we can arrive at node 2 from node 1 or node 5, from node 4 to node 2 and
there is one path from node 4 to node 1 there is one path from node 4 to node 5,
and there is one path from node 4 to node 5. we can conclude that there are three
paths from node 4 to node 3.
\subsubsection{Extending Dijkstra's algorithm} \subsubsection{Extending Dijkstra's algorithm}
\index{Dijkstra's algorithm} \index{Dijkstra's algorithm}
A by-product of Dijkstra's algorithm is a directed, acyclic A by-product of Dijkstra's algorithm is a directed, acyclic
graph that shows for each node in the original graph graph that indicates 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 to this graph. Dynamic programming can be applied to that graph.
For example, in the graph For example, in the graph
\begin{center} \begin{center}
\begin{tikzpicture} \begin{tikzpicture}
@ -376,7 +379,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}
shortest paths from node 1 may use the following edges: the shortest paths from node 1 may use the following edges:
\begin{center} \begin{center}
\begin{tikzpicture} \begin{tikzpicture}
\node[draw, circle] (1) at (0,0) {$1$}; \node[draw, circle] (1) at (0,0) {$1$};
@ -424,14 +427,14 @@ using dynamic programming:
Actually, any dynamic programming problem Actually, any dynamic programming problem
can be represented as a directed, acyclic graph. can be represented as a directed, acyclic graph.
In such a graph, each node is a dynamic programming state, In such a graph, each node corresponds to a dynamic programming state
and the edges indicate how the states depend on each other. and the edges indicate how the states depend on each other.
As an example, consider the problem As an example, consider the problem
where our task is to form a sum of money $x$ of forming a sum of money $x$
using coins 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 problem, 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 the coins can be chosen. 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$,
@ -477,8 +480,8 @@ equals the total number of solutions.
For the rest of the chapter, For the rest of the chapter,
we will concentrate on \key{successor graphs} we will concentrate on \key{successor graphs}
where the outdegree of each node is 1, which means that where the outdegree of each node is 1, i.e.,
exactly one edge starts at the node. exactly one edge starts at each node.
A successor graph consists of one or more A successor graph consists of one or more
components, each of which contains components, each of which contains
one cycle and some paths that lead to it. one cycle and some paths that lead to it.
@ -552,9 +555,9 @@ because we will reach node 2 by walking 6 steps from node 4:
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
A straightforward way to calculate a value $f(x,k)$ A straightforward way to calculate a value of $f(x,k)$
is to start at node $x$ and walk $k$ steps forward, which takes $O(k)$ time. is to start at node $x$ and walk $k$ steps forward, which takes $O(k)$ time.
However, using preprocessing, any value $f(x,k)$ However, using preprocessing, any value of $f(x,k)$
can be calculated 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
@ -586,17 +589,17 @@ $\cdots$ \\
\end{tabular} \end{tabular}
\end{center} \end{center}
After this, any value $f(x,k)$ can be calculated After this, any value of $f(x,k)$ can be calculated
by presenting the number of steps $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 of $f(x,11)$,
we first form the representation $11=8+2+1$. we first form the representation $11=8+2+1$.
Using this, Using that,
\[f(x,11)=f(f(f(x,8),2),1).\] \[f(x,11)=f(f(f(x,8),2),1).\]
For example, in the above graph For example, in the previous 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 of $f(x,k)$
takes $O(\log k)$ time. takes $O(\log k)$ time.
\section{Cycle detection} \section{Cycle detection}
@ -607,7 +610,7 @@ takes $O(\log k)$ time.
Consider a successor graph that only contains Consider a successor graph that only contains
a path that ends in a cycle. a path that ends in a cycle.
There are two interesting questions: There are two interesting questions:
if we begin our walk at the first node, if we begin our walk at the starting node,
what is the first node in the cycle what is the first node in the cycle
and how many nodes does the cycle contain? and how many nodes does the cycle contain?
@ -638,12 +641,12 @@ An easy way to detect the cycle is to walk in the
graph and keep track of graph and keep track of
all nodes that have been visited. Once a node is visited all nodes that have been visited. Once a node is visited
for the second time, we can conclude for the second time, we can conclude
that the node in question is the first node in the cycle. that the node 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.
However, there are better algorithms for cycle detection. However, there are better algorithms for cycle detection.
The time complexity of those algorithms is still $O(n)$, The time complexity of such 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 discuss Floyd's algorithm that Next we will discuss Floyd's algorithm that
@ -655,8 +658,8 @@ achieves these properties.
\key{Floyd's algorithm} walks forward \key{Floyd's algorithm} walks forward
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 a node $x$ that
of the graph. is the starting node of the graph.
Then, on each turn, the pointer $a$ walks Then, on each turn, the pointer $a$ walks
one step forward and the pointer $b$ one step forward and the pointer $b$
walks two steps forward. walks two steps forward.
@ -676,8 +679,8 @@ At this point, the pointer $a$ has walked $k$ steps
and the pointer $b$ has walked $2k$ steps, and the pointer $b$ has walked $2k$ steps,
so 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 the pointer $a$ to the can be found by moving the pointer $a$ to node $x$
starting node and advancing the pointers and advancing the pointers
step by step until they meet again: step by step until they meet again:
\begin{lstlisting} \begin{lstlisting}