Chapter 3 first version
This commit is contained in:
parent
9fd1b20e8e
commit
5cf7cf710b
229
luku03.tex
229
luku03.tex
|
@ -768,118 +768,121 @@ sort(v.begin(), v.end(), cmp);
|
||||||
|
|
||||||
\index{binary search}
|
\index{binary search}
|
||||||
|
|
||||||
Tavallinen tapa etsiä alkiota taulukosta
|
A general method for searching for an element
|
||||||
on käyttää \texttt{for}-silmukkaa, joka käy läpi
|
in an array is to use a \texttt{for} loop
|
||||||
taulukon sisällön alusta loppuun.
|
that iterates through all elements in the array.
|
||||||
Esimerkiksi seuraava koodi etsii alkiota
|
For example, the following code searches for
|
||||||
$x$ taulukosta \texttt{t}.
|
an element $x$ in array \texttt{t}:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
for (int i = 1; i <= n; i++) {
|
for (int i = 1; i <= n; i++) {
|
||||||
if (t[i] == x) // alkio x löytyi kohdasta i
|
if (t[i] == x) // x found at index i
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Tämän menetelmän aikavaativuus on $O(n)$,
|
The time complexity of this approach is $O(n)$
|
||||||
koska pahimmassa tapauksessa koko taulukko täytyy
|
because in the worst case, we have to check
|
||||||
käydä läpi.
|
all elements in the array.
|
||||||
Jos taulukon sisältö voi olla mikä tahansa,
|
If the array can contain any elements,
|
||||||
tämä on kuitenkin tehokkain mahdollinen menetelmä,
|
this is also the best possible approach because
|
||||||
koska saatavilla ei ole lisätietoa siitä,
|
there is no additional information available where
|
||||||
mistä päin taulukkoa alkiota $x$ kannattaa etsiä.
|
in the array we should search for the element $x$.
|
||||||
|
|
||||||
Tilanne on toinen silloin, kun taulukko on
|
However, if the array is \emph{sorted},
|
||||||
järjestyksessä.
|
the situation is different.
|
||||||
Tässä tapauksessa haku on mahdollista toteuttaa
|
In this case it is possible to perform the
|
||||||
paljon nopeammin, koska taulukon järjestys
|
search much faster, because the order of the
|
||||||
ohjaa etsimään alkiota oikeasta suunnasta.
|
elements in the array guides us.
|
||||||
Seuraavaksi käsiteltävä \key{binäärihaku}
|
The following \key{binary search} algorithm
|
||||||
löytää alkion järjestetystä taulukosta
|
efficiently searches for an element in a sorted array
|
||||||
tehokkaasti ajassa $O(\log n)$.
|
in $O(\log n)$ time.
|
||||||
|
|
||||||
\subsubsection{Toteutus 1}
|
\subsubsection{Method 1}
|
||||||
|
|
||||||
Perinteinen tapa toteuttaa binäärihaku muistuttaa sanan etsimistä
|
The traditional way to implement binary search
|
||||||
sanakirjasta. Haku puolittaa joka askeleella hakualueen taulukossa,
|
resembles looking for a word in a dictionary.
|
||||||
kunnes lopulta etsittävä alkio löytyy tai osoittautuu,
|
At each step, the search halves the active region in the array,
|
||||||
että sitä ei ole taulukossa.
|
until the desired element is found, or it turns out
|
||||||
|
that there is no such element.
|
||||||
|
|
||||||
Haku tarkistaa ensin taulukon keskimmäisen alkion.
|
First, the search checks the middle element in the array.
|
||||||
Jos keskimmäinen alkio on etsittävä alkio, haku päättyy.
|
If the middle element is the desired element,
|
||||||
Muuten haku jatkuu taulukon vasempaan tai oikeaan osaan sen mukaan,
|
the search terminates.
|
||||||
onko keskimmäinen alkio suurempi vain pienempi kuin etsittävä alkio.
|
Otherwise, the search recursively continues
|
||||||
|
to the left half or to the right half of the array,
|
||||||
|
depending on the value of the middle element.
|
||||||
|
|
||||||
Yllä olevan idean voi toteuttaa seuraavasti:
|
The above idea can be implemented as follows:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
int a = 1, b = n;
|
int a = 1, b = n;
|
||||||
while (a <= b) {
|
while (a <= b) {
|
||||||
int k = (a+b)/2;
|
int k = (a+b)/2;
|
||||||
if (t[k] == x) // alkio x löytyi kohdasta k
|
if (t[k] == x) // x found at index k
|
||||||
if (t[k] > x) b = k-1;
|
if (t[k] > x) b = k-1;
|
||||||
else a = k+1;
|
else a = k+1;
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Algoritmi pitää yllä väliä $a \ldots b$, joka on
|
The algorithm maintains a range $a \ldots b$
|
||||||
jäljellä oleva hakualue taulukossa.
|
that corresponds to the active region in the array.
|
||||||
Aluksi väli on $1 \ldots n$ eli koko taulukko.
|
Initially, the range is $1 \ldots n$, the whole array.
|
||||||
Välin koko puolittuu algoritmin joka vaiheessa,
|
The algorithm halves the size of the range at each step,
|
||||||
joten aikavaativuus on $O(\log n)$.
|
so the time complexity is $O(\log n)$.
|
||||||
|
|
||||||
\subsubsection{Toteutus 2}
|
\subsubsection{Method 2}
|
||||||
|
|
||||||
Vaihtoehtoinen tapa toteuttaa binäärihaku
|
An alternative method for implementing binary search
|
||||||
perustuu taulukon tehostettuun läpikäyntiin.
|
is based on a more efficient way to iterate through
|
||||||
Ideana on käydä taulukkoa läpi hyppien
|
the elements in the array.
|
||||||
ja hidastaa vauhtia, kun etsittävä alkio lähestyy.
|
The idea is to make jumps and slow the speed
|
||||||
|
when we get closer to the desired element.
|
||||||
|
|
||||||
Haku käy taulukkoa läpi vasemmalta oikealle aloittaen
|
The search goes through the array from the left to
|
||||||
hypyn pituudesta $n/2$.
|
the right, and the initial jump length is $n/2$.
|
||||||
Joka vaiheessa hypyn pituus puolittuu:
|
At each step, the jump length will be halved:
|
||||||
ensin $n/4$, sitten $n/8$, sitten $n/16$ jne.,
|
first $n/4$, then $n/8$, $n/16$, etc., until
|
||||||
kunnes lopulta hypyn pituus on 1.
|
finally the length is 1.
|
||||||
Hyppyjen jälkeen joko haettava alkio on löytynyt
|
After the jumps, either the desired element has
|
||||||
tai selviää, että sitä ei ole taulukossa.
|
been found or we know that it doesn't exist in the array.
|
||||||
|
|
||||||
Seuraava koodi toteuttaa äskeisen idean:
|
The following code implements the above idea:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
int k = 1;
|
int k = 1;
|
||||||
for (int b = n/2; b >= 1; b /= 2) {
|
for (int b = n/2; b >= 1; b /= 2) {
|
||||||
while (k+b <= n && t[k+b] <= x) k += b;
|
while (k+b <= n && t[k+b] <= x) k += b;
|
||||||
}
|
}
|
||||||
if (t[k] == x) // alkio x löytyi kohdasta k
|
if (t[k] == x) // x was found at index k
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Muuttuja $k$ on läpikäynnin kohta taulukossa
|
Variable $k$ is the position in the array,
|
||||||
ja muuttuja $b$ on hypyn pituus.
|
and variable $b$ is the jump length.
|
||||||
Jos alkio $x$ esiintyy taulukossa,
|
If the array contains the element $x$,
|
||||||
sen kohta on muuttujassa $k$ algoritmin päätteeksi.
|
the index of the element will be in variable $k$
|
||||||
Algoritmin aikavaativuus on $O(\log n)$,
|
after the search.
|
||||||
koska \texttt{while}-silmukassa oleva koodi suoritetaan
|
The time complexity of the algorithm is $O(\log n)$,
|
||||||
aina enintään kahdesti.
|
because the code in the \texttt{while} loop
|
||||||
|
is performed at most twice for each jump length.
|
||||||
|
|
||||||
\subsubsection{Muutoskohdan etsiminen}
|
\subsubsection{Finding the smallest solution}
|
||||||
|
|
||||||
Käytännössä binäärihakua tarvitsee toteuttaa
|
In practice, it is seldom needed to implement
|
||||||
harvoin alkion etsimiseen taulukosta,
|
binary search for array search,
|
||||||
koska sen sijasta voi käyttää standardikirjastoa.
|
because we can use the standard library instead.
|
||||||
Esimerkiksi C++:n funktiot \texttt{lower\_bound}
|
For example, the C++ functions \texttt{lower\_bound}
|
||||||
ja \texttt{upper\_bound} toteuttavat binäärihaun
|
and \texttt{upper\_bound} implement binary search,
|
||||||
ja tietorakenne \texttt{set} ylläpitää joukkoa,
|
and the data structure \texttt{set} maintains a
|
||||||
jonka operaatiot ovat $O(\log n)$-aikaisia.
|
set of elements with $O(\log n)$ time operations.
|
||||||
|
|
||||||
Sitäkin tärkeämpi binäärihaun käyttökohde on
|
However, an important use for binary search is
|
||||||
funktion \key{muutoskohdan} etsiminen.
|
to find a position where the value of a function changes.
|
||||||
Oletetaan, että haluamme löytää pienimmän arvon $k$,
|
Suppose that we wish to find the smallest value $k$
|
||||||
joka on kelvollinen ratkaisu ongelmaan.
|
that is a valid solution for a problem.
|
||||||
Käytössämme on funktio $\texttt{ok}(x)$,
|
We are given a function $\texttt{ok}(x)$
|
||||||
joka palauttaa \texttt{true}, jos $x$ on kelvollinen
|
that returns \texttt{true} if $x$ is a valid solution
|
||||||
ratkaisu, ja muuten \texttt{false}.
|
and \texttt{false} otherwise.
|
||||||
Lisäksi tiedämme, että $\texttt{ok}(x)$ on \texttt{false}
|
In addition, we know that $\texttt{ok}(x)$ is \texttt{false}
|
||||||
aina kun $x<k$ ja \texttt{true} aina kun $x \geq k$.
|
when $x<k$ and \texttt{true} when $x \ge k$.
|
||||||
% Toisin sanoen haluamme löytää funktion \texttt{ok} \emph{muutoskohdan},
|
The situation looks as follows:
|
||||||
% jossa arvosta \texttt{false} tulee arvo \texttt{true}.
|
|
||||||
Tilanne näyttää seuraavalta:
|
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tabular}{r|rrrrrrrr}
|
\begin{tabular}{r|rrrrrrrr}
|
||||||
|
@ -891,8 +894,7 @@ $\texttt{ok}(x)$ & \texttt{false} & \texttt{false}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
\noindent
|
\noindent
|
||||||
Nyt muutoskohta on mahdollista etsiä käyttämällä
|
The value $k$ can be found using binary search:
|
||||||
binäärihakua:
|
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
int x = -1;
|
int x = -1;
|
||||||
|
@ -902,43 +904,41 @@ for (int b = z; b >= 1; b /= 2) {
|
||||||
int k = x+1;
|
int k = x+1;
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Haku etsii suurimman $x$:n arvon,
|
The search finds the largest value of $x$ for which
|
||||||
jolla $\texttt{ok}(x)$ on \texttt{false}.
|
$\texttt{ok}(x)$ is \texttt{false}.
|
||||||
Niinpä tästä seuraava arvo $k=x+1$
|
Thus, the next value $k=x+1$
|
||||||
on pienin arvo, jolla $\texttt{ok}(k)$ on \texttt{true}.
|
is the smallest possible value for which
|
||||||
Hypyn aloituspituus $z$ tulee olla
|
$\texttt{ok}(k)$ is \texttt{true}.
|
||||||
sopiva suuri luku, esimerkiksi sellainen,
|
The initial jump length $z$ has to be
|
||||||
jolla $\texttt{ok}(z)$ on varmasti \texttt{true}.
|
large enough, for example some value
|
||||||
|
for which we know beforehand that $\texttt{ok}(z)$ is \texttt{true}.
|
||||||
|
|
||||||
Algoritmi kutsuu $O(\log z)$ kertaa funktiota
|
The algorithm calls the function \texttt{ok}
|
||||||
\texttt{ok}, joten kokonaisaikavaativuus
|
$O(\log z)$ times, so the total time complexity
|
||||||
riippuu siitä, kauanko funktion \texttt{ok}
|
depends on the function \texttt{ok}.
|
||||||
suoritus kestää.
|
For example, if the function works in $O(n)$ time,
|
||||||
Esimerkiksi jos ratkaisun tarkastus
|
the total time complexity becomes $O(n \log z)$.
|
||||||
vie aikaa $O(n)$, niin kokonaisaikavaativuus
|
|
||||||
on $O(n \log z)$.
|
|
||||||
|
|
||||||
\subsubsection{Huippuarvon etsiminen}
|
\subsubsection{Finding the maximum value}
|
||||||
|
|
||||||
Binäärihaulla voi myös etsiä
|
Binary search can also be used for finding
|
||||||
suurimman arvon funktiolle,
|
the maximum value for a function that is
|
||||||
joka on ensin kasvava ja sitten laskeva.
|
first increasing and then decreasing.
|
||||||
Toisin sanoen tehtävänä on etsiä arvo
|
Our task is to find a value $k$ such that
|
||||||
$k$ niin, että
|
|
||||||
|
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item
|
\item
|
||||||
$f(x)<f(x+1)$, kun $x<k$, ja
|
$f(x)<f(x+1)$ when $x<k$, and
|
||||||
\item
|
\item
|
||||||
$f(x)>f(x+1)$, kun $x >= k$.
|
$f(x)>f(x+1)$ when $x >= k$.
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
Ideana on etsiä binäärihaulla
|
The idea is to use binary search
|
||||||
viimeinen kohta $x$,
|
for finding the largest value of $x$
|
||||||
jossa pätee $f(x)<f(x+1)$.
|
for which $f(x)<f(x+1)$.
|
||||||
Tällöin $k=x+1$,
|
This implies that $k=x+1$
|
||||||
koska pätee $f(x+1)>f(x+2)$.
|
because $f(x+1)>f(x+2)$.
|
||||||
Seuraava koodi toteuttaa haun:
|
The following code implements the search:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
int x = -1;
|
int x = -1;
|
||||||
|
@ -948,11 +948,8 @@ for (int b = z; b >= 1; b /= 2) {
|
||||||
int k = x+1;
|
int k = x+1;
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Huomaa, että toisin kuin tavallisessa binäärihaussa,
|
Note that unlike in the regular binary search,
|
||||||
tässä ei ole sallittua,
|
here it is not allowed that successive values
|
||||||
että peräkkäiset arvot olisivat yhtä suuria.
|
of the function are equal.
|
||||||
Silloin ei olisi mahdollista tietää,
|
In this case it would not be possible to know
|
||||||
mihin suuntaan hakua tulee jatkaa.
|
how to continue the search.
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue