2016-12-28 23:54:51 +01:00
|
|
|
\chapter{Complete search}
|
|
|
|
|
2017-01-31 20:06:46 +01:00
|
|
|
\key{Complete search}
|
2017-01-01 18:38:49 +01:00
|
|
|
is a general method that can be used
|
2017-02-13 22:16:30 +01:00
|
|
|
to solve almost any algorithm problem.
|
2017-01-01 18:38:49 +01:00
|
|
|
The idea is to generate all possible
|
2017-01-31 20:06:46 +01:00
|
|
|
solutions to the problem using brute force,
|
|
|
|
and then select the best solution or count the
|
2017-01-01 18:38:49 +01:00
|
|
|
number of solutions, depending on the problem.
|
|
|
|
|
|
|
|
Complete search is a good technique
|
2017-01-31 20:06:46 +01:00
|
|
|
if there is enough time to go through all the solutions,
|
2017-01-01 18:38:49 +01:00
|
|
|
because the search is usually easy to implement
|
|
|
|
and it always gives the correct answer.
|
|
|
|
If complete search is too slow,
|
2017-01-31 20:06:46 +01:00
|
|
|
other techniques, such as greedy algorithms or
|
|
|
|
dynamic programming, may be needed.
|
2017-01-01 18:38:49 +01:00
|
|
|
|
|
|
|
\section{Generating subsets}
|
|
|
|
|
|
|
|
\index{subset}
|
|
|
|
|
2017-01-31 20:06:46 +01:00
|
|
|
We first consider the problem of generating
|
|
|
|
all subsets of a set of $n$ elements.
|
2017-04-17 16:59:27 +02:00
|
|
|
For example, the subsets of $\{0,1,2\}$ are
|
|
|
|
$\emptyset$, $\{0\}$, $\{1\}$, $\{2\}$, $\{0,1\}$,
|
|
|
|
$\{0,2\}$, $\{1,2\}$ and $\{0,1,2\}$.
|
2017-05-18 19:03:10 +02:00
|
|
|
There are two common methods to generate subsets:
|
|
|
|
we can either perform a recursive search
|
|
|
|
or exploit the bit representation of integers.
|
2017-01-01 18:38:49 +01:00
|
|
|
|
|
|
|
\subsubsection{Method 1}
|
|
|
|
|
|
|
|
An elegant way to go through all subsets
|
|
|
|
of a set is to use recursion.
|
2017-05-18 19:03:10 +02:00
|
|
|
The following function \texttt{search}
|
2017-01-01 18:38:49 +01:00
|
|
|
generates the subsets of the set
|
2017-04-17 16:59:27 +02:00
|
|
|
$\{0,1,\ldots,n-1\}$.
|
|
|
|
The function maintains a vector \texttt{subset}
|
2017-01-31 20:06:46 +01:00
|
|
|
that will contain the elements of each subset.
|
|
|
|
The search begins when the function is called
|
2017-04-17 16:59:27 +02:00
|
|
|
with parameter 0.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{lstlisting}
|
2017-05-18 19:03:10 +02:00
|
|
|
void search(int k) {
|
2017-04-17 16:59:27 +02:00
|
|
|
if (k == n) {
|
|
|
|
// process subset
|
2016-12-28 23:54:51 +01:00
|
|
|
} else {
|
2017-05-18 19:03:10 +02:00
|
|
|
search(k+1);
|
2017-04-17 16:59:27 +02:00
|
|
|
subset.push_back(k);
|
2017-05-18 19:03:10 +02:00
|
|
|
search(k+1);
|
2017-04-17 16:59:27 +02:00
|
|
|
subset.pop_back();
|
2016-12-28 23:54:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
\end{lstlisting}
|
|
|
|
|
2017-05-18 19:03:10 +02:00
|
|
|
When the function \texttt{search}
|
|
|
|
is called with parameter $k$,
|
2017-05-25 22:23:54 +02:00
|
|
|
it decides whether to include the
|
2017-05-18 19:03:10 +02:00
|
|
|
element $k$ in the subset or not,
|
|
|
|
and in both cases,
|
|
|
|
then calls itself with parameter $k+1$
|
|
|
|
However, if $k=n$, the function notices that
|
|
|
|
all elements have been processed
|
|
|
|
and a subset has been generated.
|
|
|
|
|
|
|
|
The following tree illustrates the function calls when $n=3$.
|
2017-01-31 20:06:46 +01:00
|
|
|
We can always choose either the left branch
|
|
|
|
($k$ is not included in the subset) or the right branch
|
|
|
|
($k$ is included in the subset).
|
2017-01-01 18:38:49 +01:00
|
|
|
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}[scale=.45]
|
|
|
|
\begin{scope}
|
|
|
|
\small
|
2017-05-18 19:03:10 +02:00
|
|
|
\node at (0,0) {$\texttt{search}(0)$};
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-05-18 19:03:10 +02:00
|
|
|
\node at (-8,-4) {$\texttt{search}(1)$};
|
|
|
|
\node at (8,-4) {$\texttt{search}(1)$};
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\path[draw,thick,->] (0,0-0.5) -- (-8,-4+0.5);
|
|
|
|
\path[draw,thick,->] (0,0-0.5) -- (8,-4+0.5);
|
|
|
|
|
2017-05-18 19:03:10 +02:00
|
|
|
\node at (-12,-8) {$\texttt{search}(2)$};
|
|
|
|
\node at (-4,-8) {$\texttt{search}(2)$};
|
|
|
|
\node at (4,-8) {$\texttt{search}(2)$};
|
|
|
|
\node at (12,-8) {$\texttt{search}(2)$};
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\path[draw,thick,->] (-8,-4-0.5) -- (-12,-8+0.5);
|
|
|
|
\path[draw,thick,->] (-8,-4-0.5) -- (-4,-8+0.5);
|
|
|
|
\path[draw,thick,->] (8,-4-0.5) -- (4,-8+0.5);
|
|
|
|
\path[draw,thick,->] (8,-4-0.5) -- (12,-8+0.5);
|
|
|
|
|
2017-05-18 19:03:10 +02:00
|
|
|
\node at (-14,-12) {$\texttt{search}(3)$};
|
|
|
|
\node at (-10,-12) {$\texttt{search}(3)$};
|
|
|
|
\node at (-6,-12) {$\texttt{search}(3)$};
|
|
|
|
\node at (-2,-12) {$\texttt{search}(3)$};
|
|
|
|
\node at (2,-12) {$\texttt{search}(3)$};
|
|
|
|
\node at (6,-12) {$\texttt{search}(3)$};
|
|
|
|
\node at (10,-12) {$\texttt{search}(3)$};
|
|
|
|
\node at (14,-12) {$\texttt{search}(3)$};
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\node at (-14,-13.5) {$\emptyset$};
|
2017-04-17 16:59:27 +02:00
|
|
|
\node at (-10,-13.5) {$\{2\}$};
|
|
|
|
\node at (-6,-13.5) {$\{1\}$};
|
|
|
|
\node at (-2,-13.5) {$\{1,2\}$};
|
|
|
|
\node at (2,-13.5) {$\{0\}$};
|
|
|
|
\node at (6,-13.5) {$\{0,2\}$};
|
|
|
|
\node at (10,-13.5) {$\{0,1\}$};
|
|
|
|
\node at (14,-13.5) {$\{0,1,2\}$};
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
|
|
|
|
\path[draw,thick,->] (-12,-8-0.5) -- (-14,-12+0.5);
|
|
|
|
\path[draw,thick,->] (-12,-8-0.5) -- (-10,-12+0.5);
|
|
|
|
\path[draw,thick,->] (-4,-8-0.5) -- (-6,-12+0.5);
|
|
|
|
\path[draw,thick,->] (-4,-8-0.5) -- (-2,-12+0.5);
|
|
|
|
\path[draw,thick,->] (4,-8-0.5) -- (2,-12+0.5);
|
|
|
|
\path[draw,thick,->] (4,-8-0.5) -- (6,-12+0.5);
|
|
|
|
\path[draw,thick,->] (12,-8-0.5) -- (10,-12+0.5);
|
|
|
|
\path[draw,thick,->] (12,-8-0.5) -- (14,-12+0.5);
|
|
|
|
\end{scope}
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
|
|
|
|
2017-01-01 18:38:49 +01:00
|
|
|
\subsubsection{Method 2}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-05-18 19:03:10 +02:00
|
|
|
Another way to generate subsets is based on
|
2017-01-01 18:38:49 +01:00
|
|
|
the bit representation of integers.
|
|
|
|
Each subset of a set of $n$ elements
|
|
|
|
can be represented as a sequence of $n$ bits,
|
|
|
|
which corresponds to an integer between $0 \ldots 2^n-1$.
|
2017-01-31 20:06:46 +01:00
|
|
|
The ones in the bit sequence indicate
|
|
|
|
which elements are included in the subset.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-05-18 19:03:10 +02:00
|
|
|
The usual convention is that
|
|
|
|
the last bit corresponds to element 0,
|
|
|
|
the second last bit corresponds to element 1,
|
|
|
|
and so on.
|
2017-01-01 18:38:49 +01:00
|
|
|
For example, the bit representation of 25
|
2017-05-25 22:23:54 +02:00
|
|
|
is 11001, which corresponds to the subset $\{0,3,4\}$.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-05-18 19:03:10 +02:00
|
|
|
The following code goes through the subsets
|
2017-01-01 18:38:49 +01:00
|
|
|
of a set of $n$ elements
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
for (int b = 0; b < (1<<n); b++) {
|
2017-04-17 16:59:27 +02:00
|
|
|
// process subset
|
2016-12-28 23:54:51 +01:00
|
|
|
}
|
|
|
|
\end{lstlisting}
|
|
|
|
|
2017-02-13 22:16:30 +01:00
|
|
|
The following code shows how we can find
|
|
|
|
the elements of a subset that corresponds to a bit sequence.
|
2017-01-31 20:06:46 +01:00
|
|
|
When processing each subset,
|
|
|
|
the code builds a vector that contains the
|
|
|
|
elements in the subset.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
for (int b = 0; b < (1<<n); b++) {
|
2017-04-17 16:59:27 +02:00
|
|
|
vector<int> subset;
|
2016-12-28 23:54:51 +01:00
|
|
|
for (int i = 0; i < n; i++) {
|
2017-04-17 16:59:27 +02:00
|
|
|
if (b&(1<<i)) subset.push_back(i);
|
2016-12-28 23:54:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
\end{lstlisting}
|
|
|
|
|
2017-01-01 18:38:49 +01:00
|
|
|
\section{Generating permutations}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-01 18:38:49 +01:00
|
|
|
\index{permutation}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-05-18 19:03:10 +02:00
|
|
|
Next we consider the problem of generating
|
2017-01-31 20:06:46 +01:00
|
|
|
all permutations of a set of $n$ elements.
|
2017-04-17 16:59:27 +02:00
|
|
|
For example, the permutations of $\{0,1,2\}$ are
|
|
|
|
$(0,1,2)$, $(0,2,1)$, $(1,0,2)$, $(1,2,0)$,
|
|
|
|
$(2,0,1)$ and $(2,1,0)$.
|
2017-01-31 20:06:46 +01:00
|
|
|
Again, there are two approaches:
|
2017-02-13 22:16:30 +01:00
|
|
|
we can either use recursion or go through the
|
2017-01-31 20:06:46 +01:00
|
|
|
permutations iteratively.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-01 18:38:49 +01:00
|
|
|
\subsubsection{Method 1}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-01 18:38:49 +01:00
|
|
|
Like subsets, permutations can be generated
|
|
|
|
using recursion.
|
2017-05-18 19:03:10 +02:00
|
|
|
The following function \texttt{search} goes
|
2017-04-17 16:59:27 +02:00
|
|
|
through the permutations of the set $\{0,1,\ldots,n-1\}$.
|
2017-05-18 19:03:10 +02:00
|
|
|
The function builds a vector \texttt{permutation}
|
2017-05-25 22:23:54 +02:00
|
|
|
that contains the permutation,
|
2017-01-31 20:06:46 +01:00
|
|
|
and the search begins when the function is
|
|
|
|
called without parameters.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{lstlisting}
|
2017-05-18 19:03:10 +02:00
|
|
|
void search() {
|
|
|
|
if (permutation.size() == n) {
|
2017-04-17 16:59:27 +02:00
|
|
|
// process permutation
|
2016-12-28 23:54:51 +01:00
|
|
|
} else {
|
2017-04-17 16:59:27 +02:00
|
|
|
for (int i = 0; i < n; i++) {
|
|
|
|
if (chosen[i]) continue;
|
|
|
|
chosen[i] = true;
|
2017-05-18 19:03:10 +02:00
|
|
|
permutation.push_back(i);
|
|
|
|
search();
|
2017-04-17 16:59:27 +02:00
|
|
|
chosen[i] = false;
|
2017-05-18 19:03:10 +02:00
|
|
|
permutation.pop_back();
|
2016-12-28 23:54:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
\end{lstlisting}
|
|
|
|
|
2017-01-01 18:38:49 +01:00
|
|
|
Each function call adds a new element to
|
2017-05-18 19:03:10 +02:00
|
|
|
\texttt{permutation}.
|
2017-04-17 16:59:27 +02:00
|
|
|
The array \texttt{chosen} indicates which
|
|
|
|
elements are already included in the permutation.
|
2017-05-18 19:03:10 +02:00
|
|
|
If the size of \texttt{permutation} equals the size of the set,
|
2017-01-01 18:38:49 +01:00
|
|
|
a permutation has been generated.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-01 18:38:49 +01:00
|
|
|
\subsubsection{Method 2}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\index{next\_permutation@\texttt{next\_permutation}}
|
|
|
|
|
2017-01-31 20:06:46 +01:00
|
|
|
Another method for generating permutations
|
|
|
|
is to begin with the permutation
|
2017-05-18 19:03:10 +02:00
|
|
|
$\{0,1,\ldots,n-1\}$ and repeatedly
|
2017-01-31 20:06:46 +01:00
|
|
|
use a function that constructs the next permutation
|
|
|
|
in increasing order.
|
2017-01-01 18:38:49 +01:00
|
|
|
The C++ standard library contains the function
|
2017-01-31 20:06:46 +01:00
|
|
|
\texttt{next\_permutation} that can be used for this:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{lstlisting}
|
2017-05-18 19:03:10 +02:00
|
|
|
vector<int> permutation;
|
2017-04-17 16:59:27 +02:00
|
|
|
for (int i = 0; i < n; i++) {
|
2017-05-18 19:03:10 +02:00
|
|
|
permutation.push_back(i);
|
2016-12-28 23:54:51 +01:00
|
|
|
}
|
|
|
|
do {
|
2017-04-17 16:59:27 +02:00
|
|
|
// process permutation
|
2017-05-18 19:03:10 +02:00
|
|
|
} while (next_permutation(permutation.begin(),permutation.end()));
|
2016-12-28 23:54:51 +01:00
|
|
|
\end{lstlisting}
|
|
|
|
|
2017-01-01 19:47:18 +01:00
|
|
|
\section{Backtracking}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-01 19:47:18 +01:00
|
|
|
\index{backtracking}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-01 19:47:18 +01:00
|
|
|
A \key{backtracking} algorithm
|
2017-01-31 20:06:46 +01:00
|
|
|
begins with an empty solution
|
2017-01-01 19:47:18 +01:00
|
|
|
and extends the solution step by step.
|
2017-01-31 20:06:46 +01:00
|
|
|
The search recursively
|
|
|
|
goes through all different ways how
|
|
|
|
a solution can be constructed.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-01 19:47:18 +01:00
|
|
|
\index{queen problem}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-05-18 19:03:10 +02:00
|
|
|
As an example, consider the problem of
|
|
|
|
calculating the number
|
2017-12-10 11:06:32 +01:00
|
|
|
of ways $n$ queens can be placed on
|
2017-01-01 19:47:18 +01:00
|
|
|
an $n \times n$ chessboard so that
|
|
|
|
no two queens attack each other.
|
|
|
|
For example, when $n=4$,
|
2017-05-18 19:03:10 +02:00
|
|
|
there are two possible solutions:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}[scale=.65]
|
|
|
|
\begin{scope}
|
|
|
|
\draw (0, 0) grid (4, 4);
|
2017-02-22 20:15:48 +01:00
|
|
|
\node at (1.5,3.5) {\symqueen};
|
|
|
|
\node at (3.5,2.5) {\symqueen};
|
|
|
|
\node at (0.5,1.5) {\symqueen};
|
|
|
|
\node at (2.5,0.5) {\symqueen};
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\draw (6, 0) grid (10, 4);
|
2017-02-22 20:15:48 +01:00
|
|
|
\node at (6+2.5,3.5) {\symqueen};
|
|
|
|
\node at (6+0.5,2.5) {\symqueen};
|
|
|
|
\node at (6+3.5,1.5) {\symqueen};
|
|
|
|
\node at (6+1.5,0.5) {\symqueen};
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\end{scope}
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
|
|
|
|
2017-01-01 19:47:18 +01:00
|
|
|
The problem can be solved using backtracking
|
|
|
|
by placing queens to the board row by row.
|
2017-01-31 20:06:46 +01:00
|
|
|
More precisely, exactly one queen will
|
2017-12-10 11:06:32 +01:00
|
|
|
be placed on each row so that no queen attacks
|
2017-01-01 19:47:18 +01:00
|
|
|
any of the queens placed before.
|
2017-01-31 20:06:46 +01:00
|
|
|
A solution has been found when all
|
2017-12-10 11:06:32 +01:00
|
|
|
$n$ queens have been placed on the board.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-02-13 22:16:30 +01:00
|
|
|
For example, when $n=4$,
|
|
|
|
some partial solutions generated by
|
|
|
|
the backtracking algorithm are as follows:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}[scale=.55]
|
|
|
|
\begin{scope}
|
|
|
|
\draw (0, 0) grid (4, 4);
|
|
|
|
|
|
|
|
\draw (-9, -6) grid (-5, -2);
|
|
|
|
\draw (-3, -6) grid (1, -2);
|
|
|
|
\draw (3, -6) grid (7, -2);
|
|
|
|
\draw (9, -6) grid (13, -2);
|
|
|
|
|
2017-02-22 20:15:48 +01:00
|
|
|
\node at (-9+0.5,-3+0.5) {\symqueen};
|
|
|
|
\node at (-3+1+0.5,-3+0.5) {\symqueen};
|
|
|
|
\node at (3+2+0.5,-3+0.5) {\symqueen};
|
|
|
|
\node at (9+3+0.5,-3+0.5) {\symqueen};
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\draw (2,0) -- (-7,-2);
|
|
|
|
\draw (2,0) -- (-1,-2);
|
|
|
|
\draw (2,0) -- (5,-2);
|
|
|
|
\draw (2,0) -- (11,-2);
|
|
|
|
|
|
|
|
\draw (-11, -12) grid (-7, -8);
|
|
|
|
\draw (-6, -12) grid (-2, -8);
|
|
|
|
\draw (-1, -12) grid (3, -8);
|
|
|
|
\draw (4, -12) grid (8, -8);
|
|
|
|
\draw[white] (11, -12) grid (15, -8);
|
2017-02-22 20:15:48 +01:00
|
|
|
\node at (-11+1+0.5,-9+0.5) {\symqueen};
|
|
|
|
\node at (-6+1+0.5,-9+0.5) {\symqueen};
|
|
|
|
\node at (-1+1+0.5,-9+0.5) {\symqueen};
|
|
|
|
\node at (4+1+0.5,-9+0.5) {\symqueen};
|
|
|
|
\node at (-11+0+0.5,-10+0.5) {\symqueen};
|
|
|
|
\node at (-6+1+0.5,-10+0.5) {\symqueen};
|
|
|
|
\node at (-1+2+0.5,-10+0.5) {\symqueen};
|
|
|
|
\node at (4+3+0.5,-10+0.5) {\symqueen};
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\draw (-1,-6) -- (-9,-8);
|
|
|
|
\draw (-1,-6) -- (-4,-8);
|
|
|
|
\draw (-1,-6) -- (1,-8);
|
|
|
|
\draw (-1,-6) -- (6,-8);
|
|
|
|
|
2017-05-18 19:03:10 +02:00
|
|
|
\node at (-9,-13) {illegal};
|
|
|
|
\node at (-4,-13) {illegal};
|
|
|
|
\node at (1,-13) {illegal};
|
|
|
|
\node at (6,-13) {valid};
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\end{scope}
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
|
|
|
|
2017-05-18 19:03:10 +02:00
|
|
|
At the bottom level, the three first configurations
|
|
|
|
are illegal, because the queens attack each other.
|
|
|
|
However, the fourth configuration is valid
|
2017-01-31 20:06:46 +01:00
|
|
|
and it can be extended to a complete solution by
|
2017-01-01 19:47:18 +01:00
|
|
|
placing two more queens to the board.
|
2017-05-18 19:03:10 +02:00
|
|
|
There is only one way to place the two remaining queens.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{samepage}
|
2017-05-18 19:03:10 +02:00
|
|
|
The algorithm can be implemented as follows:
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{lstlisting}
|
2017-01-01 19:47:18 +01:00
|
|
|
void search(int y) {
|
2016-12-28 23:54:51 +01:00
|
|
|
if (y == n) {
|
2017-04-17 16:59:27 +02:00
|
|
|
count++;
|
2016-12-28 23:54:51 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (int x = 0; x < n; x++) {
|
2017-05-25 22:23:54 +02:00
|
|
|
if (column[x] || diag1[x+y] || diag2[x-y+n-1]) continue;
|
|
|
|
column[x] = diag1[x+y] = diag2[x-y+n-1] = 1;
|
2017-01-01 19:47:18 +01:00
|
|
|
search(y+1);
|
2017-05-25 22:23:54 +02:00
|
|
|
column[x] = diag1[x+y] = diag2[x-y+n-1] = 0;
|
2016-12-28 23:54:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
\end{lstlisting}
|
|
|
|
\end{samepage}
|
2017-01-01 19:47:18 +01:00
|
|
|
The search begins by calling \texttt{search(0)}.
|
2017-12-10 11:06:32 +01:00
|
|
|
The size of the board is $n \times n$,
|
2017-01-01 19:47:18 +01:00
|
|
|
and the code calculates the number of solutions
|
2017-04-17 16:59:27 +02:00
|
|
|
to \texttt{count}.
|
2017-01-01 19:47:18 +01:00
|
|
|
|
|
|
|
The code assumes that the rows and columns
|
2017-05-18 19:03:10 +02:00
|
|
|
of the board are numbered from 0 to $n-1$.
|
|
|
|
When the function \texttt{search} is
|
|
|
|
called with parameter $y$,
|
2017-12-10 11:06:32 +01:00
|
|
|
it places a queen on row $y$
|
2017-05-18 19:03:10 +02:00
|
|
|
and then calls itself with parameter $y+1$.
|
2017-05-25 22:23:54 +02:00
|
|
|
Then, if $y=n$, a solution has been found
|
2017-05-18 19:03:10 +02:00
|
|
|
and the variable \texttt{count} is increased by one.
|
2017-01-01 19:47:18 +01:00
|
|
|
|
2017-05-25 22:23:54 +02:00
|
|
|
The array \texttt{column} keeps track of columns
|
|
|
|
that contain a queen,
|
|
|
|
and the arrays \texttt{diag1} and \texttt{diag2}
|
|
|
|
keep track of diagonals.
|
2017-01-01 19:47:18 +01:00
|
|
|
It is not allowed to add another queen to a
|
2017-01-31 20:06:46 +01:00
|
|
|
column or diagonal that already contains a queen.
|
2017-05-18 19:03:10 +02:00
|
|
|
For example, the columns and diagonals of
|
2017-01-01 19:47:18 +01:00
|
|
|
the $4 \times 4$ board are numbered as follows:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}[scale=.65]
|
|
|
|
\begin{scope}
|
|
|
|
\draw (0-6, 0) grid (4-6, 4);
|
|
|
|
\node at (-6+0.5,3.5) {$0$};
|
|
|
|
\node at (-6+1.5,3.5) {$1$};
|
|
|
|
\node at (-6+2.5,3.5) {$2$};
|
|
|
|
\node at (-6+3.5,3.5) {$3$};
|
|
|
|
\node at (-6+0.5,2.5) {$0$};
|
|
|
|
\node at (-6+1.5,2.5) {$1$};
|
|
|
|
\node at (-6+2.5,2.5) {$2$};
|
|
|
|
\node at (-6+3.5,2.5) {$3$};
|
|
|
|
\node at (-6+0.5,1.5) {$0$};
|
|
|
|
\node at (-6+1.5,1.5) {$1$};
|
|
|
|
\node at (-6+2.5,1.5) {$2$};
|
|
|
|
\node at (-6+3.5,1.5) {$3$};
|
|
|
|
\node at (-6+0.5,0.5) {$0$};
|
|
|
|
\node at (-6+1.5,0.5) {$1$};
|
|
|
|
\node at (-6+2.5,0.5) {$2$};
|
|
|
|
\node at (-6+3.5,0.5) {$3$};
|
|
|
|
|
|
|
|
\draw (0, 0) grid (4, 4);
|
|
|
|
\node at (0.5,3.5) {$0$};
|
|
|
|
\node at (1.5,3.5) {$1$};
|
|
|
|
\node at (2.5,3.5) {$2$};
|
|
|
|
\node at (3.5,3.5) {$3$};
|
|
|
|
\node at (0.5,2.5) {$1$};
|
|
|
|
\node at (1.5,2.5) {$2$};
|
|
|
|
\node at (2.5,2.5) {$3$};
|
|
|
|
\node at (3.5,2.5) {$4$};
|
|
|
|
\node at (0.5,1.5) {$2$};
|
|
|
|
\node at (1.5,1.5) {$3$};
|
|
|
|
\node at (2.5,1.5) {$4$};
|
|
|
|
\node at (3.5,1.5) {$5$};
|
|
|
|
\node at (0.5,0.5) {$3$};
|
|
|
|
\node at (1.5,0.5) {$4$};
|
|
|
|
\node at (2.5,0.5) {$5$};
|
|
|
|
\node at (3.5,0.5) {$6$};
|
|
|
|
|
|
|
|
\draw (6, 0) grid (10, 4);
|
|
|
|
\node at (6.5,3.5) {$3$};
|
|
|
|
\node at (7.5,3.5) {$4$};
|
|
|
|
\node at (8.5,3.5) {$5$};
|
|
|
|
\node at (9.5,3.5) {$6$};
|
|
|
|
\node at (6.5,2.5) {$2$};
|
|
|
|
\node at (7.5,2.5) {$3$};
|
|
|
|
\node at (8.5,2.5) {$4$};
|
|
|
|
\node at (9.5,2.5) {$5$};
|
|
|
|
\node at (6.5,1.5) {$1$};
|
|
|
|
\node at (7.5,1.5) {$2$};
|
|
|
|
\node at (8.5,1.5) {$3$};
|
|
|
|
\node at (9.5,1.5) {$4$};
|
|
|
|
\node at (6.5,0.5) {$0$};
|
|
|
|
\node at (7.5,0.5) {$1$};
|
|
|
|
\node at (8.5,0.5) {$2$};
|
|
|
|
\node at (9.5,0.5) {$3$};
|
|
|
|
|
2017-05-25 22:23:54 +02:00
|
|
|
\node at (-4,-1) {\texttt{column}};
|
|
|
|
\node at (2,-1) {\texttt{diag1}};
|
|
|
|
\node at (8,-1) {\texttt{diag2}};
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\end{scope}
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
|
|
|
|
2017-02-25 15:51:29 +01:00
|
|
|
Let $q(n)$ denote the number of ways
|
2017-12-10 11:06:32 +01:00
|
|
|
to place $n$ queens on an $n \times n$ chessboard.
|
2017-01-31 20:06:46 +01:00
|
|
|
The above backtracking
|
2017-02-27 20:29:32 +01:00
|
|
|
algorithm tells us that, for example, $q(8)=92$.
|
2017-01-31 20:06:46 +01:00
|
|
|
When $n$ increases, the search quickly becomes slow,
|
2017-12-10 11:06:32 +01:00
|
|
|
because the number of solutions increases
|
2017-01-01 19:47:18 +01:00
|
|
|
exponentially.
|
2017-02-25 15:51:29 +01:00
|
|
|
For example, calculating $q(16)=14772512$
|
|
|
|
using the above algorithm already takes about a minute
|
|
|
|
on a modern computer\footnote{There is no known way to efficiently
|
|
|
|
calculate larger values of $q(n)$. The current record is
|
|
|
|
$q(27)=234907967154122528$, calculated in 2016 \cite{q27}.}.
|
2017-01-01 19:47:18 +01:00
|
|
|
|
|
|
|
\section{Pruning the search}
|
|
|
|
|
2017-01-31 20:06:46 +01:00
|
|
|
We can often optimize backtracking
|
2017-01-01 19:47:18 +01:00
|
|
|
by pruning the search tree.
|
|
|
|
The idea is to add ''intelligence'' to the algorithm
|
2017-02-13 22:16:30 +01:00
|
|
|
so that it will notice as soon as possible
|
2017-01-31 20:06:46 +01:00
|
|
|
if a partial solution cannot be extended
|
|
|
|
to a complete solution.
|
|
|
|
Such optimizations can have a tremendous
|
2017-01-01 19:47:18 +01:00
|
|
|
effect on the efficiency of the search.
|
|
|
|
|
2017-02-27 20:29:32 +01:00
|
|
|
Let us consider the problem
|
2017-01-31 20:06:46 +01:00
|
|
|
of calculating the number of paths
|
2017-01-01 19:47:18 +01:00
|
|
|
in an $n \times n$ grid from the upper-left corner
|
2017-05-18 19:03:10 +02:00
|
|
|
to the lower-right corner such that the
|
|
|
|
path visits each square exactly once.
|
2017-01-31 20:06:46 +01:00
|
|
|
For example, in a $7 \times 7$ grid,
|
|
|
|
there are 111712 such paths.
|
2017-01-01 19:47:18 +01:00
|
|
|
One of the paths is as follows:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}[scale=.55]
|
|
|
|
\begin{scope}
|
|
|
|
\draw (0, 0) grid (7, 7);
|
|
|
|
\draw[thick,->] (0.5,6.5) -- (0.5,4.5) -- (2.5,4.5) --
|
|
|
|
(2.5,3.5) -- (0.5,3.5) -- (0.5,0.5) --
|
|
|
|
(3.5,0.5) -- (3.5,1.5) -- (1.5,1.5) --
|
|
|
|
(1.5,2.5) -- (4.5,2.5) -- (4.5,0.5) --
|
|
|
|
(5.5,0.5) -- (5.5,3.5) -- (3.5,3.5) --
|
|
|
|
(3.5,5.5) -- (1.5,5.5) -- (1.5,6.5) --
|
|
|
|
(4.5,6.5) -- (4.5,4.5) -- (5.5,4.5) --
|
|
|
|
(5.5,6.5) -- (6.5,6.5) -- (6.5,0.5);
|
|
|
|
\end{scope}
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
|
|
|
|
2017-05-18 19:03:10 +02:00
|
|
|
We focus on the $7 \times 7$ case,
|
2017-02-13 22:16:30 +01:00
|
|
|
because its level of difficulty is appropriate to our needs.
|
2017-01-01 19:47:18 +01:00
|
|
|
We begin with a straightforward backtracking algorithm,
|
|
|
|
and then optimize it step by step using observations
|
2017-05-18 19:03:10 +02:00
|
|
|
of how the search can be pruned.
|
2017-01-01 19:47:18 +01:00
|
|
|
After each optimization, we measure the running time
|
|
|
|
of the algorithm and the number of recursive calls,
|
2017-05-18 19:03:10 +02:00
|
|
|
so that we clearly see the effect of each
|
2017-01-01 19:47:18 +01:00
|
|
|
optimization on the efficiency of the search.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-01 19:47:18 +01:00
|
|
|
\subsubsection{Basic algorithm}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-31 20:06:46 +01:00
|
|
|
The first version of the algorithm does not contain
|
2017-01-01 19:47:18 +01:00
|
|
|
any optimizations. We simply use backtracking to generate
|
|
|
|
all possible paths from the upper-left corner to
|
2017-01-31 20:06:46 +01:00
|
|
|
the lower-right corner and count the number of such paths.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{itemize}
|
|
|
|
\item
|
2017-01-01 19:47:18 +01:00
|
|
|
running time: 483 seconds
|
2016-12-28 23:54:51 +01:00
|
|
|
\item
|
2017-04-18 19:15:43 +02:00
|
|
|
number of recursive calls: 76 billion
|
2016-12-28 23:54:51 +01:00
|
|
|
\end{itemize}
|
|
|
|
|
2017-01-01 19:47:18 +01:00
|
|
|
\subsubsection{Optimization 1}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-02-13 22:16:30 +01:00
|
|
|
In any solution, we first move one step
|
2017-01-31 20:06:46 +01:00
|
|
|
down or right.
|
2017-01-01 19:47:18 +01:00
|
|
|
There are always two paths that
|
|
|
|
are symmetric
|
|
|
|
about the diagonal of the grid
|
|
|
|
after the first step.
|
|
|
|
For example, the following paths are symmetric:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
\begin{tabular}{ccc}
|
|
|
|
\begin{tikzpicture}[scale=.55]
|
|
|
|
\begin{scope}
|
|
|
|
\draw (0, 0) grid (7, 7);
|
|
|
|
\draw[thick,->] (0.5,6.5) -- (0.5,4.5) -- (2.5,4.5) --
|
|
|
|
(2.5,3.5) -- (0.5,3.5) -- (0.5,0.5) --
|
|
|
|
(3.5,0.5) -- (3.5,1.5) -- (1.5,1.5) --
|
|
|
|
(1.5,2.5) -- (4.5,2.5) -- (4.5,0.5) --
|
|
|
|
(5.5,0.5) -- (5.5,3.5) -- (3.5,3.5) --
|
|
|
|
(3.5,5.5) -- (1.5,5.5) -- (1.5,6.5) --
|
|
|
|
(4.5,6.5) -- (4.5,4.5) -- (5.5,4.5) --
|
|
|
|
(5.5,6.5) -- (6.5,6.5) -- (6.5,0.5);
|
|
|
|
\end{scope}
|
|
|
|
\end{tikzpicture}
|
|
|
|
& \hspace{20px}
|
|
|
|
&
|
|
|
|
\begin{tikzpicture}[scale=.55]
|
|
|
|
\begin{scope}[yscale=1,xscale=-1,rotate=-90]
|
|
|
|
\draw (0, 0) grid (7, 7);
|
|
|
|
\draw[thick,->] (0.5,6.5) -- (0.5,4.5) -- (2.5,4.5) --
|
|
|
|
(2.5,3.5) -- (0.5,3.5) -- (0.5,0.5) --
|
|
|
|
(3.5,0.5) -- (3.5,1.5) -- (1.5,1.5) --
|
|
|
|
(1.5,2.5) -- (4.5,2.5) -- (4.5,0.5) --
|
|
|
|
(5.5,0.5) -- (5.5,3.5) -- (3.5,3.5) --
|
|
|
|
(3.5,5.5) -- (1.5,5.5) -- (1.5,6.5) --
|
|
|
|
(4.5,6.5) -- (4.5,4.5) -- (5.5,4.5) --
|
|
|
|
(5.5,6.5) -- (6.5,6.5) -- (6.5,0.5);
|
|
|
|
\end{scope}
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{tabular}
|
|
|
|
\end{center}
|
|
|
|
|
2017-01-31 20:06:46 +01:00
|
|
|
Hence, we can decide that we always first
|
2017-05-18 19:03:10 +02:00
|
|
|
move one step down (or right),
|
|
|
|
and finally multiply the number of solutions by two.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{itemize}
|
|
|
|
\item
|
2017-01-01 19:47:18 +01:00
|
|
|
running time: 244 seconds
|
2016-12-28 23:54:51 +01:00
|
|
|
\item
|
2017-04-18 19:15:43 +02:00
|
|
|
number of recursive calls: 38 billion
|
2016-12-28 23:54:51 +01:00
|
|
|
\end{itemize}
|
|
|
|
|
2017-01-01 19:47:18 +01:00
|
|
|
\subsubsection{Optimization 2}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-01 19:47:18 +01:00
|
|
|
If the path reaches the lower-right square
|
|
|
|
before it has visited all other squares of the grid,
|
|
|
|
it is clear that
|
|
|
|
it will not be possible to complete the solution.
|
2017-01-31 20:06:46 +01:00
|
|
|
An example of this is the following path:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}[scale=.55]
|
|
|
|
\begin{scope}
|
|
|
|
\draw (0, 0) grid (7, 7);
|
|
|
|
\draw[thick,->] (0.5,6.5) -- (0.5,4.5) -- (2.5,4.5) --
|
|
|
|
(2.5,3.5) -- (0.5,3.5) -- (0.5,0.5) --
|
|
|
|
(3.5,0.5) -- (3.5,1.5) -- (1.5,1.5) --
|
|
|
|
(1.5,2.5) -- (4.5,2.5) -- (4.5,0.5) --
|
|
|
|
(6.5,0.5);
|
|
|
|
\end{scope}
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
2017-01-31 20:06:46 +01:00
|
|
|
Using this observation, we can terminate the search
|
2017-01-01 19:47:18 +01:00
|
|
|
immediately if we reach the lower-right square too early.
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{itemize}
|
|
|
|
\item
|
2017-01-01 19:47:18 +01:00
|
|
|
running time: 119 seconds
|
2016-12-28 23:54:51 +01:00
|
|
|
\item
|
2017-04-18 19:15:43 +02:00
|
|
|
number of recursive calls: 20 billion
|
2016-12-28 23:54:51 +01:00
|
|
|
\end{itemize}
|
|
|
|
|
2017-01-01 19:47:18 +01:00
|
|
|
\subsubsection{Optimization 3}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-05-18 19:03:10 +02:00
|
|
|
If the path touches a wall
|
|
|
|
and can turn either left or right,
|
|
|
|
the grid splits into two parts
|
|
|
|
that contain unvisited squares.
|
|
|
|
For example, in the following situation,
|
|
|
|
the path can turn either left or right:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}[scale=.55]
|
|
|
|
\begin{scope}
|
|
|
|
\draw (0, 0) grid (7, 7);
|
|
|
|
\draw[thick,->] (0.5,6.5) -- (0.5,4.5) -- (2.5,4.5) --
|
|
|
|
(2.5,3.5) -- (0.5,3.5) -- (0.5,0.5) --
|
|
|
|
(3.5,0.5) -- (3.5,1.5) -- (1.5,1.5) --
|
|
|
|
(1.5,2.5) -- (4.5,2.5) -- (4.5,0.5) --
|
|
|
|
(5.5,0.5) -- (5.5,6.5);
|
|
|
|
\end{scope}
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
2017-05-18 19:03:10 +02:00
|
|
|
In this case, we cannot visit all squares anymore,
|
2017-01-31 20:06:46 +01:00
|
|
|
so we can terminate the search.
|
2017-05-18 19:03:10 +02:00
|
|
|
This optimization is very useful:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{itemize}
|
|
|
|
\item
|
2017-01-01 19:47:18 +01:00
|
|
|
running time: 1.8 seconds
|
2016-12-28 23:54:51 +01:00
|
|
|
\item
|
2017-04-18 19:15:43 +02:00
|
|
|
number of recursive calls: 221 million
|
2016-12-28 23:54:51 +01:00
|
|
|
\end{itemize}
|
|
|
|
|
2017-01-01 19:47:18 +01:00
|
|
|
\subsubsection{Optimization 4}
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-05-18 19:03:10 +02:00
|
|
|
The idea of Optimization 3
|
2017-01-01 19:47:18 +01:00
|
|
|
can be generalized:
|
2017-05-18 19:03:10 +02:00
|
|
|
if the path cannot continue forward
|
|
|
|
but can turn either left or right,
|
2017-01-01 19:47:18 +01:00
|
|
|
the grid splits into two parts
|
2017-05-18 19:03:10 +02:00
|
|
|
that both contain unvisited squares.
|
|
|
|
For example, consider the following path:
|
|
|
|
|
2016-12-28 23:54:51 +01:00
|
|
|
\begin{center}
|
|
|
|
\begin{tikzpicture}[scale=.55]
|
|
|
|
\begin{scope}
|
|
|
|
\draw (0, 0) grid (7, 7);
|
|
|
|
\draw[thick,->] (0.5,6.5) -- (0.5,4.5) -- (2.5,4.5) --
|
|
|
|
(2.5,3.5) -- (0.5,3.5) -- (0.5,0.5) --
|
|
|
|
(3.5,0.5) -- (3.5,1.5) -- (1.5,1.5) --
|
|
|
|
(1.5,2.5) -- (4.5,2.5) -- (4.5,0.5) --
|
|
|
|
(5.5,0.5) -- (5.5,4.5) -- (3.5,4.5);
|
|
|
|
\end{scope}
|
|
|
|
\end{tikzpicture}
|
|
|
|
\end{center}
|
2017-05-18 19:03:10 +02:00
|
|
|
It is clear that we cannot visit all squares anymore,
|
|
|
|
so we can terminate the search.
|
|
|
|
After this optimization, the search is
|
2017-01-31 20:06:46 +01:00
|
|
|
very efficient:
|
2016-12-28 23:54:51 +01:00
|
|
|
|
|
|
|
\begin{itemize}
|
|
|
|
\item
|
2017-01-01 19:47:18 +01:00
|
|
|
running time: 0.6 seconds
|
2016-12-28 23:54:51 +01:00
|
|
|
\item
|
2017-04-18 19:15:43 +02:00
|
|
|
number of recursive calls: 69 million
|
2016-12-28 23:54:51 +01:00
|
|
|
\end{itemize}
|
|
|
|
|
|
|
|
~\\
|
2017-02-27 20:29:32 +01:00
|
|
|
Now is a good moment to stop optimizing
|
2017-01-31 20:06:46 +01:00
|
|
|
the algorithm and see what we have achieved.
|
2017-01-01 19:47:18 +01:00
|
|
|
The running time of the original algorithm
|
|
|
|
was 483 seconds,
|
|
|
|
and now after the optimizations,
|
|
|
|
the running time is only 0.6 seconds.
|
|
|
|
Thus, the algorithm became nearly 1000 times
|
2017-05-18 19:03:10 +02:00
|
|
|
faster after the optimizations.
|
2017-01-01 19:47:18 +01:00
|
|
|
|
2017-01-31 20:06:46 +01:00
|
|
|
This is a usual phenomenon in backtracking,
|
2017-01-01 19:47:18 +01:00
|
|
|
because the search tree is usually large
|
2017-01-31 20:06:46 +01:00
|
|
|
and even simple observations can effectively
|
|
|
|
prune the search.
|
2017-01-01 19:47:18 +01:00
|
|
|
Especially useful are optimizations that
|
2017-01-31 20:06:46 +01:00
|
|
|
occur during the first steps of the algorithm,
|
|
|
|
i.e., at the top of the search tree.
|
2016-12-28 23:54:51 +01:00
|
|
|
|
2017-01-01 20:08:51 +01:00
|
|
|
\section{Meet in the middle}
|
|
|
|
|
|
|
|
\index{meet in the middle}
|
|
|
|
|
|
|
|
\key{Meet in the middle} is a technique
|
|
|
|
where the search space is divided into
|
2017-02-13 22:16:30 +01:00
|
|
|
two parts of about equal size.
|
2017-01-01 20:08:51 +01:00
|
|
|
A separate search is performed
|
2017-02-13 22:16:30 +01:00
|
|
|
for both of the parts,
|
2017-01-01 20:08:51 +01:00
|
|
|
and finally the results of the searches are combined.
|
|
|
|
|
2017-01-31 20:06:46 +01:00
|
|
|
The technique can be used
|
2017-01-01 20:08:51 +01:00
|
|
|
if there is an efficient way to combine the
|
|
|
|
results of the searches.
|
2017-01-31 20:06:46 +01:00
|
|
|
In such a situation, the two searches may require less
|
2017-01-01 20:08:51 +01:00
|
|
|
time than one large search.
|
|
|
|
Typically, we can turn a factor of $2^n$
|
|
|
|
into a factor of $2^{n/2}$ using the meet in the
|
|
|
|
middle technique.
|
|
|
|
|
|
|
|
As an example, consider a problem where
|
|
|
|
we are given a list of $n$ numbers and
|
2017-05-18 19:03:10 +02:00
|
|
|
a number $x$,
|
|
|
|
and we want to find out if it is possible
|
2017-01-01 20:08:51 +01:00
|
|
|
to choose some numbers from the list so that
|
2017-01-31 20:06:46 +01:00
|
|
|
their sum is $x$.
|
2017-01-01 20:08:51 +01:00
|
|
|
For example, given the list $[2,4,5,9]$ and $x=15$,
|
|
|
|
we can choose the numbers $[2,4,9]$ to get $2+4+9=15$.
|
2017-05-25 22:23:54 +02:00
|
|
|
However, if $x=10$ for the same list,
|
2017-01-01 20:08:51 +01:00
|
|
|
it is not possible to form the sum.
|
|
|
|
|
2017-05-25 22:23:54 +02:00
|
|
|
A simple algorithm to the problem is to
|
2017-01-01 20:14:04 +01:00
|
|
|
go through all subsets of the elements and
|
2017-01-01 20:08:51 +01:00
|
|
|
check if the sum of any of the subsets is $x$.
|
2017-05-18 19:03:10 +02:00
|
|
|
The running time of such an algorithm is $O(2^n)$,
|
2017-01-31 20:06:46 +01:00
|
|
|
because there are $2^n$ subsets.
|
2017-01-01 20:08:51 +01:00
|
|
|
However, using the meet in the middle technique,
|
2017-05-18 19:03:10 +02:00
|
|
|
we can achieve a more efficient $O(2^{n/2})$ time algorithm\footnote{This
|
|
|
|
idea was introduced in 1974 by E. Horowitz and S. Sahni \cite{hor74}.}.
|
2017-01-01 20:08:51 +01:00
|
|
|
Note that $O(2^n)$ and $O(2^{n/2})$ are different
|
|
|
|
complexities because $2^{n/2}$ equals $\sqrt{2^n}$.
|
|
|
|
|
2017-01-31 20:06:46 +01:00
|
|
|
The idea is to divide the list into
|
|
|
|
two lists $A$ and $B$ such that both
|
|
|
|
lists contain about half of the numbers.
|
2017-01-01 20:08:51 +01:00
|
|
|
The first search generates all subsets
|
2017-05-18 19:03:10 +02:00
|
|
|
of $A$ and stores their sums to a list $S_A$.
|
2017-01-01 20:08:51 +01:00
|
|
|
Correspondingly, the second search creates
|
2017-01-31 20:06:46 +01:00
|
|
|
a list $S_B$ from $B$.
|
2017-01-01 20:08:51 +01:00
|
|
|
After this, it suffices to check if it is possible
|
2017-02-13 22:16:30 +01:00
|
|
|
to choose one element from $S_A$ and another
|
2017-02-27 20:29:32 +01:00
|
|
|
element from $S_B$ such that their sum is $x$.
|
2017-01-01 20:08:51 +01:00
|
|
|
This is possible exactly when there is a way to
|
2017-05-18 19:03:10 +02:00
|
|
|
form the sum $x$ using the numbers of the original list.
|
2017-01-01 20:08:51 +01:00
|
|
|
|
2017-01-31 20:06:46 +01:00
|
|
|
For example, suppose that the list is $[2,4,5,9]$ and $x=15$.
|
2017-01-01 20:08:51 +01:00
|
|
|
First, we divide the list into $A=[2,4]$ and $B=[5,9]$.
|
2017-01-31 20:06:46 +01:00
|
|
|
After this, we create lists
|
2017-01-01 20:08:51 +01:00
|
|
|
$S_A=[0,2,4,6]$ and $S_B=[0,5,9,14]$.
|
2017-02-13 22:16:30 +01:00
|
|
|
In this case, the sum $x=15$ is possible to form,
|
2017-05-18 19:03:10 +02:00
|
|
|
because $S_A$ contains the sum $6$,
|
|
|
|
$S_B$ contains the sum $9$, and $6+9=15$.
|
|
|
|
This corresponds to the solution $[2,4,9]$.
|
2017-01-01 20:08:51 +01:00
|
|
|
|
2017-12-10 10:25:35 +01:00
|
|
|
We can implement the algorithm so that
|
|
|
|
its time complexity is $O(2^{n/2})$.
|
|
|
|
First, we generate \emph{sorted} lists $S_A$ and $S_B$,
|
|
|
|
which can be done in $O(2^{n/2})$ time using a merge-like technique.
|
|
|
|
After this, since the lists are sorted,
|
|
|
|
we can check in $O(2^{n/2})$ time if
|
|
|
|
the sum $x$ can be created from $S_A$ and $S_B$.
|