Skip to content

Commit

Permalink
hamilton paths, knight tour, de bruikn sequences
Browse files Browse the repository at this point in the history
  • Loading branch information
emanuele-em committed Nov 14, 2023
1 parent cc08cd7 commit 0221520
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 4 deletions.
8 changes: 4 additions & 4 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@
- [Offline algorithms](offline_algorithms.md)
- [Paths and circuits](path_and_circuit.md)
- [Eulerian paths](eulerian_paths.md)
<!-- - [Hamiltonian paths](README.md) -->
<!-- - [De Bruijn sequences](README.md) -->
<!-- - [Knight’s tours](README.md) -->
<!-- - [Flows and cuts](README.md) -->
- [Hamiltonian paths](hamiltonian_path.md)
- [De Bruijn sequences](de_bruijn_sequences.md)
- [Knight’s tours](knight_s_tour.md)
<!-- - [Flows and cuts](README.md) -->
<!-- - [Ford–Fulkerson algorithm](README.md) -->
<!-- - [Disjoint paths](README.md) -->
<!-- - [Maximum matchings](README.md) -->
Expand Down
54 changes: 54 additions & 0 deletions src/de_bruijn_sequences.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# De Bruijn sequences

A **De Bruijn sequence**
is a string that contains
every string of length $n$
exactly once as a substring, for a fixed
alphabet of $k$ characters.
The length of such a string is
$k^n+n-1$ characters.
For example, when $n=3$ and $k=2$,
an example of a De Bruijn sequence is

$$
0001011100
$$

The substrings of this string are all
combinations of three bits:
000, 001, 010, 011, 100, 101, 110 and 111.

It turns out that each De Bruijn sequence
corresponds to an Eulerian path in a graph.
The idea is to construct a graph where
each node contains a string of $n-1$ characters
and each edge adds one character to the string.
The following graph corresponds to the above scenario:

<script type="text/tikz">
\begin{tikzpicture}[scale=0.8]
\node[draw, circle] (00) at (-3,0) {00};
\node[draw, circle] (11) at (3,0) {11};
\node[draw, circle] (01) at (0,2) {01};
\node[draw, circle] (10) at (0,-2) {10};

\path[draw,thick,->] (00) edge [bend left=20] node[font=\small,label=1] {} (01);
\path[draw,thick,->] (01) edge [bend left=20] node[font=\small,label=1] {} (11);
\path[draw,thick,->] (11) edge [bend left=20] node[font=\small,label=below:0] {} (10);
\path[draw,thick,->] (10) edge [bend left=20] node[font=\small,label=below:0] {} (00);

\path[draw,thick,->] (01) edge [bend left=30] node[font=\small,label=right:0] {} (10);
\path[draw,thick,->] (10) edge [bend left=30] node[font=\small,label=left:1] {} (01);

\path[draw,thick,-] (00) edge [loop left] node[font=\small,label=below:0] {} (00);
\path[draw,thick,-] (11) edge [loop right] node[font=\small,label=below:1] {} (11);
\end{tikzpicture}
</script>

An Eulerian path in this graph corresponds to a string
that contains all strings of length $n$.
The string contains the characters of the starting node
and all characters of the edges.
The starting node has $n-1$ characters
and there are $k^n$ characters in the edges,
so the length of the string is $k^n+n-1$.
125 changes: 125 additions & 0 deletions src/hamiltonian_path.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Hamiltonian paths

A **Hamiltonian path**
is a path
that visits each node of the graph exactly once.
For example, the graph

<script type="text/tikz">
\begin{tikzpicture}[scale=0.9]
\node[draw, circle] (1) at (1,5) {1};
\node[draw, circle] (2) at (3,5) {2};
\node[draw, circle] (3) at (5,4) {3};
\node[draw, circle] (4) at (1,3) {4};
\node[draw, circle] (5) at (3,3) {5};

\path[draw,thick,-] (1) -- (2);
\path[draw,thick,-] (2) -- (3);
\path[draw,thick,-] (1) -- (4);
\path[draw,thick,-] (3) -- (5);
\path[draw,thick,-] (2) -- (5);
\path[draw,thick,-] (4) -- (5);
\end{tikzpicture}
</script>

contains a Hamiltonian path from node 1 to node 3:

<script type="text/tikz">
\begin{tikzpicture}[scale=0.9]
\node[draw, circle] (1) at (1,5) {1};
\node[draw, circle] (2) at (3,5) {2};
\node[draw, circle] (3) at (5,4) {3};
\node[draw, circle] (4) at (1,3) {4};
\node[draw, circle] (5) at (3,3) {5};

\path[draw,thick,-] (1) -- (2);
\path[draw,thick,-] (2) -- (3);
\path[draw,thick,-] (1) -- (4);
\path[draw,thick,-] (3) -- (5);
\path[draw,thick,-] (2) -- (5);
\path[draw,thick,-] (4) -- (5);

\path[draw=red,thick,->,line width=2pt] (1) -- node[font=\small,label={[red]left:1.}] {} (4);
\path[draw=red,thick,->,line width=2pt] (4) -- node[font=\small,label={[red]south:2.}] {} (5);
\path[draw=red,thick,->,line width=2pt] (5) -- node[font=\small,label={[red]left:3.}] {} (2);
\path[draw=red,thick,->,line width=2pt] (2) -- node[font=\small,label={[red]north:4.}] {} (3);
\end{tikzpicture}
</script>

If a Hamiltonian path begins and ends at the same node,
it is called a **Hamiltonian circuit**.
The graph above also has an Hamiltonian circuit
that begins and ends at node 1:

<script type="text/tikz">
\begin{tikzpicture}[scale=0.9]
\node[draw, circle] (1) at (1,5) {1};
\node[draw, circle] (2) at (3,5) {2};
\node[draw, circle] (3) at (5,4) {3};
\node[draw, circle] (4) at (1,3) {4};
\node[draw, circle] (5) at (3,3) {5};

\path[draw,thick,-] (1) -- (2);
\path[draw,thick,-] (2) -- (3);
\path[draw,thick,-] (1) -- (4);
\path[draw,thick,-] (3) -- (5);
\path[draw,thick,-] (2) -- (5);
\path[draw,thick,-] (4) -- (5);

\path[draw=red,thick,->,line width=2pt] (1) -- node[font=\small,label={[red]north:1.}] {} (2);
\path[draw=red,thick,->,line width=2pt] (2) -- node[font=\small,label={[red]north:2.}] {} (3);
\path[draw=red,thick,->,line width=2pt] (3) -- node[font=\small,label={[red]south:3.}] {} (5);
\path[draw=red,thick,->,line width=2pt] (5) -- node[font=\small,label={[red]south:4.}] {} (4);
\path[draw=red,thick,->,line width=2pt] (4) -- node[font=\small,label={[red]left:5.}] {} (1);
\end{tikzpicture}
</script>

## Existence

No efficient method is known for testing if a graph
contains a Hamiltonian path, and the problem is NP-hard.
Still, in some special cases, we can be certain
that a graph contains a Hamiltonian path.

A simple observation is that if the graph is complete,
i.e., there is an edge between all pairs of nodes,
it also contains a Hamiltonian path.
Also stronger results have been achieved:

- **Dirac's theorem**:
If the degree of each node is at least $n/2$,
the graph contains a Hamiltonian path.
- **Ore's theorem**:
If the sum of degrees of each non-adjacent pair of nodes
is at least $n$,
the graph contains a Hamiltonian path.

A common property in these theorems and other results is
that they guarantee the existence of a Hamiltonian path
if the graph has _a large number_ of edges.
This makes sense, because the more edges the graph contains,
the more possibilities there is to construct a Hamiltonian path.

## Construction

Since there is no efficient way to check if a Hamiltonian
path exists, it is clear that there is also no method
to efficiently construct the path, because otherwise
we could just try to construct the path and see
whether it exists.

A simple way to search for a Hamiltonian path is
to use a backtracking algorithm that goes through all
possible ways to construct the path.
The time complexity of such an algorithm is at least $O(n!)$,
because there are $n!$ different ways to choose the order of $n$ nodes.

A more efficient solution is based on dynamic programming
(see Chapter 10.5).
The idea is to calculate values
of a function `possible(S,x)`,
where $S$ is a subset of nodes and $x$
is one of the nodes.
The function indicates whether there is a Hamiltonian path
that visits the nodes of $S$ and ends at node $x$.
It is possible to implement this solution in $O(2^n n^2)$ time.
69 changes: 69 additions & 0 deletions src/knight_s_tour.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Knight’s tours

A **knight's tour** is a sequence of moves
of a knight on an $n \times n$ chessboard
following the rules of chess such that the knight
visits each square exactly once.
A knight's tour is called a _closed_ tour
if the knight finally returns to the starting square and
otherwise it is called an _open_ tour.

For example, here is an open knight's tour on a $5 \times 5$ board:

<script type="text/tikz">
\begin{tikzpicture}[scale=0.7]
\draw (0,0) grid (5,5);
\node at (0.5,4.5) {1};
\node at (1.5,4.5) {4};
\node at (2.5,4.5) {11};
\node at (3.5,4.5) {16};
\node at (4.5,4.5) {25};
\node at (0.5,3.5) {12};
\node at (1.5,3.5) {17};
\node at (2.5,3.5) {2};
\node at (3.5,3.5) {5};
\node at (4.5,3.5) {10};
\node at (0.5,2.5) {3};
\node at (1.5,2.5) {20};
\node at (2.5,2.5) {7};
\node at (3.5,2.5) {24};
\node at (4.5,2.5) {15};
\node at (0.5,1.5) {18};
\node at (1.5,1.5) {13};
\node at (2.5,1.5) {22};
\node at (3.5,1.5) {9};
\node at (4.5,1.5) {6};
\node at (0.5,0.5) {21};
\node at (1.5,0.5) {8};
\node at (2.5,0.5) {19};
\node at (3.5,0.5) {14};
\node at (4.5,0.5) {23};
\end{tikzpicture}
</script>

A knight's tour corresponds to a Hamiltonian path in a graph
whose nodes represent the squares of the board,
and two nodes are connected with an edge if a knight
can move between the squares according to the rules of chess.

A natural way to construct a knight's tour is to use backtracking.
The search can be made more efficient by using
_heuristics_ that attempt to guide the knight so that
a complete tour will be found quickly.

## Warnsdorf's rule

**Warnsdorf's rule** is a simple and effective heuristic
for finding a knight's tour[^1].
Using the rule, it is possible to efficiently construct a tour
even on a large board.
The idea is to always move the knight so that it ends up
in a square where the number of possible moves is as
_small_ as possible.

For example, in the following situation, there are five
possible squares to which the knight can move (squares $a \ldots e$):

___

[^1] This heuristic was proposed in Warnsdorf's book [69] in 1823. There are also polynomial algorithms for finding knight's tours [52], but they are more complicated.

0 comments on commit 0221520

Please sign in to comment.