Subsets and permutations
This commit is contained in:
parent
9bdb67701a
commit
9de7221c10
243
luku05.tex
243
luku05.tex
|
@ -1,102 +1,102 @@
|
||||||
\chapter{Complete search}
|
\chapter{Complete search}
|
||||||
|
|
||||||
\key{Täydellinen haku}
|
\key{Compelete search}
|
||||||
on yleispätevä tapa ratkaista
|
is a general method that can be used
|
||||||
lähes mikä tahansa ohjelmointitehtävä.
|
for solving almost any algorithm problem.
|
||||||
Ideana on käydä läpi raa'alla voimalla kaikki
|
The idea is to generate all possible
|
||||||
mahdolliset tehtävän ratkaisut ja tehtävästä riippuen
|
solutions for the problem using brute force,
|
||||||
valita paras ratkaisu
|
and select the best solution or count the
|
||||||
tai laskea ratkaisuiden yhteismäärä.
|
number of solutions, depending on the problem.
|
||||||
|
|
||||||
Täydellinen haku on hyvä menetelmä, jos kaikki
|
|
||||||
ratkaisut ehtii käydä läpi,
|
|
||||||
koska haku on yleensä suoraviivainen toteuttaa
|
|
||||||
ja se antaa varmasti oikean vastauksen.
|
|
||||||
Jos täydellinen haku on liian hidas,
|
|
||||||
seuraavien lukujen ahneet algoritmit tai
|
|
||||||
dynaaminen ohjelmointi voivat soveltua
|
|
||||||
tehtävään.
|
|
||||||
|
|
||||||
\section{Osajoukkojen läpikäynti}
|
Complete search is a good technique
|
||||||
|
if it is feasible to go through all the solutions,
|
||||||
|
because the search is usually easy to implement
|
||||||
|
and it always gives the correct answer.
|
||||||
|
If complete search is too slow,
|
||||||
|
greedy algorithms or dynamic programming,
|
||||||
|
presented in the next chapters,
|
||||||
|
may be used.
|
||||||
|
|
||||||
\index{osajoukko@osajoukko}
|
\section{Generating subsets}
|
||||||
|
|
||||||
Aloitamme tapauksesta, jossa tehtävän
|
\index{subset}
|
||||||
mahdollisia ratkaisuja ovat
|
|
||||||
$n$-alkioisen joukon osajoukot.
|
|
||||||
Tällöin täydellisen haun tulee
|
|
||||||
käydä läpi kaikki joukon osa\-joukot,
|
|
||||||
joita on yhteensä $2^n$ kappaletta.
|
|
||||||
Käymme läpi kaksi menetelmää
|
|
||||||
tällaisen haun toteuttamiseen.
|
|
||||||
|
|
||||||
\subsubsection{Menetelmä 1}
|
We first consider the case where
|
||||||
|
the possible solutions for the problem
|
||||||
|
are the subsets of a set of $n$ elements.
|
||||||
|
In this case, a complete search algorithm
|
||||||
|
has to generate
|
||||||
|
all $2^n$ subsets of the set.
|
||||||
|
|
||||||
Kätevä tapa käydä läpi
|
\subsubsection{Method 1}
|
||||||
kaikki joukon osajoukot on
|
|
||||||
käyttää rekursiota.
|
An elegant way to go through all subsets
|
||||||
Seuraava funktio \texttt{haku} muodostaa
|
of a set is to use recursion.
|
||||||
joukon $\{1,2,\ldots,n\}$ osajoukot.
|
The following function \texttt{gen}
|
||||||
Funktio pitää yllä vektoria \texttt{v},
|
generates the subsets of the set
|
||||||
johon se kokoaa osajoukossa olevat luvut.
|
$\{1,2,\ldots,n\}$.
|
||||||
Osajoukkojen muodostaminen alkaa
|
The function maintains a vector \texttt{v}
|
||||||
tekemällä funktiokutsu \texttt{haku(1)}.
|
that will contain the elements in the subset.
|
||||||
|
The generation of the subsets
|
||||||
|
begins when the function
|
||||||
|
is called with parameter $1$.
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
void haku(int k) {
|
void gen(int k) {
|
||||||
if (k == n+1) {
|
if (k == n+1) {
|
||||||
// käsittele osajoukko v
|
// process subset v
|
||||||
} else {
|
} else {
|
||||||
haku(k+1);
|
gen(k+1);
|
||||||
v.push_back(k);
|
v.push_back(k);
|
||||||
haku(k+1);
|
gen(k+1);
|
||||||
v.pop_back();
|
v.pop_back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Funktion parametri $k$ on luku,
|
The parameter $k$ is the number that is the next
|
||||||
joka on ehdolla lisättäväksi osajoukkoon seuraavaksi.
|
candidate to be included in the subset.
|
||||||
Joka kutsulla funktio haarautuu kahteen tapaukseen:
|
The function branches to two cases:
|
||||||
joko luku $k$ lisätään tai ei lisätä osajoukkoon.
|
either $k$ is included or it is not included in the subset.
|
||||||
Aina kun $k=n+1$, kaikki luvut on käyty läpi
|
Finally, when $k=n+1$, a decision has been made for
|
||||||
ja yksi osajoukko on muodostettu.
|
all the numbers and one subset has been generated.
|
||||||
|
|
||||||
|
For example, when $n=3$, the function calls
|
||||||
|
create a tree illustrated below.
|
||||||
|
At each call, the left branch doesn't include
|
||||||
|
the number and the right branch includes the number
|
||||||
|
in the subset.
|
||||||
|
|
||||||
Esimerkiksi kun $n=3$, funktiokutsut
|
|
||||||
muodostavat seuraavan kuvan mukaisen puun.
|
|
||||||
Joka kutsussa
|
|
||||||
vasen haara jättää luvun pois osajoukosta
|
|
||||||
ja oikea haara lisää sen osajoukkoon.
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=.45]
|
\begin{tikzpicture}[scale=.45]
|
||||||
\begin{scope}
|
\begin{scope}
|
||||||
\small
|
\small
|
||||||
\node at (0,0) {$\texttt{haku}(1)$};
|
\node at (0,0) {$\texttt{gen}(1)$};
|
||||||
|
|
||||||
\node at (-8,-4) {$\texttt{haku}(2)$};
|
\node at (-8,-4) {$\texttt{gen}(2)$};
|
||||||
\node at (8,-4) {$\texttt{haku}(2)$};
|
\node at (8,-4) {$\texttt{gen}(2)$};
|
||||||
|
|
||||||
\path[draw,thick,->] (0,0-0.5) -- (-8,-4+0.5);
|
\path[draw,thick,->] (0,0-0.5) -- (-8,-4+0.5);
|
||||||
\path[draw,thick,->] (0,0-0.5) -- (8,-4+0.5);
|
\path[draw,thick,->] (0,0-0.5) -- (8,-4+0.5);
|
||||||
|
|
||||||
\node at (-12,-8) {$\texttt{haku}(3)$};
|
\node at (-12,-8) {$\texttt{gen}(3)$};
|
||||||
\node at (-4,-8) {$\texttt{haku}(3)$};
|
\node at (-4,-8) {$\texttt{gen}(3)$};
|
||||||
\node at (4,-8) {$\texttt{haku}(3)$};
|
\node at (4,-8) {$\texttt{gen}(3)$};
|
||||||
\node at (12,-8) {$\texttt{haku}(3)$};
|
\node at (12,-8) {$\texttt{gen}(3)$};
|
||||||
|
|
||||||
\path[draw,thick,->] (-8,-4-0.5) -- (-12,-8+0.5);
|
\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) -- (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);
|
\path[draw,thick,->] (8,-4-0.5) -- (12,-8+0.5);
|
||||||
|
|
||||||
\node at (-14,-12) {$\texttt{haku}(4)$};
|
\node at (-14,-12) {$\texttt{gen}(4)$};
|
||||||
\node at (-10,-12) {$\texttt{haku}(4)$};
|
\node at (-10,-12) {$\texttt{gen}(4)$};
|
||||||
\node at (-6,-12) {$\texttt{haku}(4)$};
|
\node at (-6,-12) {$\texttt{gen}(4)$};
|
||||||
\node at (-2,-12) {$\texttt{haku}(4)$};
|
\node at (-2,-12) {$\texttt{gen}(4)$};
|
||||||
\node at (2,-12) {$\texttt{haku}(4)$};
|
\node at (2,-12) {$\texttt{gen}(4)$};
|
||||||
\node at (6,-12) {$\texttt{haku}(4)$};
|
\node at (6,-12) {$\texttt{gen}(4)$};
|
||||||
\node at (10,-12) {$\texttt{haku}(4)$};
|
\node at (10,-12) {$\texttt{gen}(4)$};
|
||||||
\node at (14,-12) {$\texttt{haku}(4)$};
|
\node at (14,-12) {$\texttt{gen}(4)$};
|
||||||
|
|
||||||
\node at (-14,-13.5) {$\emptyset$};
|
\node at (-14,-13.5) {$\emptyset$};
|
||||||
\node at (-10,-13.5) {$\{3\}$};
|
\node at (-10,-13.5) {$\{3\}$};
|
||||||
|
@ -120,36 +120,36 @@ ja oikea haara lisää sen osajoukkoon.
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
\subsubsection{Menetelmä 2}
|
\subsubsection{Method 2}
|
||||||
|
|
||||||
Toinen tapa käydä osajoukot läpi on hyödyntää kokonaislukujen
|
Another way to generate the subsets is to exploit
|
||||||
bittiesitystä. Jokainen $n$ alkion osajoukko
|
the bit representation of integers.
|
||||||
voidaan esittää $n$ bitin jonona,
|
Each subset of a set of $n$ elements
|
||||||
joka taas vastaa lukua väliltä $0 \ldots 2^n-1$.
|
can be represented as a sequence of $n$ bits,
|
||||||
Bittiesityksen ykkösbitit ilmaisevat,
|
which corresponds to an integer between $0 \ldots 2^n-1$.
|
||||||
mitkä joukon alkiot on valittu osajoukkoon.
|
The ones in the bit representation indicate
|
||||||
|
which elements of the set are included in the subset.
|
||||||
|
|
||||||
Tavallinen käytäntö on tulkita kokonaisluvun
|
The usual interpretation is that element $k$
|
||||||
bittiesitys osajoukkona niin,
|
is included in the subset if $k$th bit from the
|
||||||
että alkio $k$ kuuluu osajoukkoon,
|
end of the bit sequence is one.
|
||||||
jos lopusta lukien $k$. bitti on 1.
|
For example, the bit representation of 25
|
||||||
Esimerkiksi luvun 25 bittiesitys on 11001,
|
is 11001 that corresponds to the subset $\{1,4,5\}$.
|
||||||
mikä vastaa osajoukkoa $\{1,4,5\}$.
|
|
||||||
|
|
||||||
Seuraava koodi käy läpi $n$ alkion joukon
|
The following iterates through all subsets
|
||||||
osajoukkojen bittiesitykset:
|
of a set of $n$ elements
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
for (int b = 0; b < (1<<n); b++) {
|
for (int b = 0; b < (1<<n); b++) {
|
||||||
// käsittele osajoukko b
|
// process subset b
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Seuraava koodi muodostaa jokaisen osajoukon
|
The following code converts each bit
|
||||||
kohdalla vektorin \texttt{v},
|
representation to a vector \texttt{v}
|
||||||
joka sisältää osajoukossa olevat luvut.
|
that contains the elements in the subset.
|
||||||
Ne saadaan selville tutkimalla, mitkä bitit ovat
|
This can be done by checking which bits
|
||||||
ykkösiä osajoukon bittiesityksessä.
|
are one in the bit representation.
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
for (int b = 0; b < (1<<n); b++) {
|
for (int b = 0; b < (1<<n); b++) {
|
||||||
|
@ -160,33 +160,30 @@ for (int b = 0; b < (1<<n); b++) {
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
\section{Permutaatioiden läpikäynti}
|
\section{Generating permutations}
|
||||||
|
|
||||||
\index{permutaatio@permutaatio}
|
\index{permutation}
|
||||||
|
|
||||||
Toinen usein esiintyvä tilanne on,
|
Another common situation is that the solutions
|
||||||
että tehtävän ratkaisut ovat $n$-alkioisen
|
for the problem are permutations of a
|
||||||
joukon permutaatioita,
|
set of $n$ elements.
|
||||||
jolloin täydellisen haun tulee
|
In this case, a complete search algorithm has to
|
||||||
käydä läpi $n!$ mahdollista permutaatiota.
|
generate $n!$ possible permutations.
|
||||||
Myös tässä tapauksessa on kaksi luontevaa
|
|
||||||
menetelmää täydellisen haun toteuttamiseen.
|
|
||||||
|
|
||||||
\subsubsection{Menetelmä 1}
|
\subsubsection{Method 1}
|
||||||
|
|
||||||
Osajoukkojen tavoin permutaatioita voi muodostaa
|
Like subsets, permutations can be generated
|
||||||
rekursiivisesti.
|
using recursion.
|
||||||
Seuraava funktio \texttt{haku} käy läpi
|
The following function \texttt{gen} iterates
|
||||||
joukon $\{1,2,\ldots,n\}$ permutaatiot.
|
through the permutations of the set $\{1,2,\ldots,n\}$.
|
||||||
Funktio muodostaa kunkin permutaation
|
The function uses the vector \texttt{v}
|
||||||
vuorollaan vektoriin \texttt{v}.
|
for storing the permutations, and the generation
|
||||||
Permutaatioiden muodostus alkaa kutsumalla
|
begins by calling the function without parameters.
|
||||||
funktiota ilman parametreja.
|
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
void haku() {
|
void haku() {
|
||||||
if (v.size() == n) {
|
if (v.size() == n) {
|
||||||
// käsittele permutaatio v
|
// process permutation v
|
||||||
} else {
|
} else {
|
||||||
for (int i = 1; i <= n; i++) {
|
for (int i = 1; i <= n; i++) {
|
||||||
if (p[i]) continue;
|
if (p[i]) continue;
|
||||||
|
@ -200,26 +197,26 @@ void haku() {
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Funktion jokainen kutsu lisää uuden
|
Each function call adds a new element to
|
||||||
luvun permutaatioon vektoriin \texttt{v}.
|
the permutation in the vector \texttt{v}.
|
||||||
Taulukko \texttt{p} kertoo, mitkä luvut on jo
|
The array \texttt{p} indicates which
|
||||||
valittu permutaatioon.
|
elements are already included in the permutation.
|
||||||
Jos $\texttt{p}[k]=0$, luku $k$ ei ole mukana,
|
If $\texttt{p}[k]=0$, element $k$ is not included,
|
||||||
ja jos $\texttt{p}[k]=1$, luku $k$ on mukana.
|
and if $\texttt{p}[k]=1$, element $k$ is included.
|
||||||
Jos vektorin \texttt{v} koko on sama kuin
|
If the size of the vector equals the size of the set,
|
||||||
joukon koko $n$, permutaatio on tullut valmiiksi.
|
a permutation has been generated.
|
||||||
|
|
||||||
\subsubsection{Menetelmä 2}
|
\subsubsection{Method 2}
|
||||||
|
|
||||||
\index{next\_permutation@\texttt{next\_permutation}}
|
\index{next\_permutation@\texttt{next\_permutation}}
|
||||||
|
|
||||||
Toinen ratkaisu on aloittaa permutaatiosta
|
Another method is to begin from permutation
|
||||||
$\{1,2,\ldots,n\}$ ja muodostaa joka askeleella
|
$\{1,2,\ldots,n\}$ and at each step generate the
|
||||||
järjestyksessä seuraava permutaatio.
|
next permutation in increasing order.
|
||||||
C++:n standardikirjastossa on funktio
|
The C++ standard library contains the function
|
||||||
\texttt{next\_permutation}, joka tekee tämän muunnoksen.
|
\texttt{next\_permutation} that can be used for this.
|
||||||
Seuraava koodi käy läpi joukon $\{1,2,\ldots,n\}$
|
The following code generates the permutations
|
||||||
permutaatiot funktion avulla:
|
of the set $\{1,2,\ldots,n\}$ using the function:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
vector<int> v;
|
vector<int> v;
|
||||||
|
@ -227,7 +224,7 @@ for (int i = 1; i <= n; i++) {
|
||||||
v.push_back(i);
|
v.push_back(i);
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
// käsittele permutaatio v
|
// process permutation v
|
||||||
} while (next_permutation(v.begin(),v.end()));
|
} while (next_permutation(v.begin(),v.end()));
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue