Chapter 4 first version
This commit is contained in:
parent
03c12213d9
commit
46f2aa6b71
267
luku04.tex
267
luku04.tex
|
@ -538,20 +538,18 @@ cout << (a^b) << "\n"; // 1001101110
|
||||||
|
|
||||||
\subsubsection{Pakka}
|
\subsubsection{Pakka}
|
||||||
|
|
||||||
\index{pakka@pakka}
|
\index{deque}
|
||||||
\index{deque@\texttt{deque}}
|
\index{deque@\texttt{deque}}
|
||||||
|
|
||||||
\key{Pakka} (\texttt{deque}) on dynaaminen taulukko,
|
A \key{deque} (\texttt{deque}) is a dynamic array
|
||||||
jonka kokoa pystyy muuttamaan tehokkaasti
|
whose size can be changed at both ends of the array.
|
||||||
sekä alku- että loppupäässä.
|
Like a vector, a deque contains functions
|
||||||
Pakka sisältää vektorin tavoin
|
\texttt{push\_back} and \texttt{pop\_back}, but
|
||||||
funktiot \texttt{push\_back}
|
it also contains additional functions
|
||||||
ja \texttt{pop\_back}, mutta siinä on lisäksi myös funktiot
|
\texttt{push\_front} and \texttt{pop\_front}
|
||||||
\texttt{push\_front} ja \texttt{pop\_front},
|
that are not available in a vector.
|
||||||
jotka käsittelevät taulukon alkua.
|
|
||||||
|
|
||||||
Seuraava koodi esittelee pakan käyttämistä:
|
|
||||||
|
|
||||||
|
A deque can be used as follows:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
deque<int> d;
|
deque<int> d;
|
||||||
d.push_back(5); // [5]
|
d.push_back(5); // [5]
|
||||||
|
@ -561,27 +559,26 @@ d.pop_back(); // [3,5]
|
||||||
d.pop_front(); // [5]
|
d.pop_front(); // [5]
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Pakan sisäinen toteutus on monimutkaisempi kuin
|
The internal implementation of a deque
|
||||||
vektorissa, minkä vuoksi se on
|
is more complex than the implementation of a vector.
|
||||||
vektoria raskaampi rakenne.
|
For this reason, a deque is slower than a vector.
|
||||||
Kuitenkin lisäyksen ja poiston
|
Still, the time complexity of adding and removing
|
||||||
aikavaativuus on keskimäärin $O(1)$ molemmissa päissä.
|
elements is $O(1)$ on average at both ends.
|
||||||
|
|
||||||
\subsubsection{Pino}
|
\subsubsection{Pino}
|
||||||
|
|
||||||
\index{pino@pino}
|
\index{stack}
|
||||||
\index{stack@\texttt{stack}}
|
\index{stack@\texttt{stack}}
|
||||||
|
|
||||||
\key{Pino} (\texttt{stack}) on tietorakenne,
|
A \key{stack} (\texttt{stack})
|
||||||
joka tarjoaa kaksi $O(1)$-aikaista
|
is a data structure that provides two
|
||||||
operaatiota:
|
$O(1)$ time operations:
|
||||||
alkion lisäys pinon päälle ja alkion
|
adding an element to the top,
|
||||||
poisto pinon päältä.
|
and removing an element from the top.
|
||||||
Pinossa ei ole mahdollista käsitellä muita
|
It is only possible to access the top
|
||||||
alkioita kuin pinon päällimmäistä alkiota.
|
element of a stack.
|
||||||
|
|
||||||
Seuraava koodi esittelee pinon käyttämistä:
|
|
||||||
|
|
||||||
|
The following code shows how a stack can be used:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
stack<int> s;
|
stack<int> s;
|
||||||
s.push(3);
|
s.push(3);
|
||||||
|
@ -591,19 +588,19 @@ cout << s.top(); // 5
|
||||||
s.pop();
|
s.pop();
|
||||||
cout << s.top(); // 2
|
cout << s.top(); // 2
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
\subsubsection{Jono}
|
\subsubsection{Queue}
|
||||||
|
|
||||||
\index{jono@jono}
|
\index{queue}
|
||||||
\index{queue@\texttt{queue}}
|
\index{queue@\texttt{queue}}
|
||||||
|
|
||||||
\key{Jono} (\texttt{queue}) on kuin pino,
|
A \key{queue} (\texttt{queue}) also
|
||||||
mutta alkion lisäys tapahtuu jonon loppuun
|
provides two $O(1)$ time operations:
|
||||||
ja alkion poisto tapahtuu jonon alusta.
|
adding a new element to the end,
|
||||||
Jonossa on mahdollista käsitellä vain
|
and removing the first element.
|
||||||
alussa ja lopussa olevaa alkiota.
|
It is only possible to access the first
|
||||||
|
and the last element of a queue.
|
||||||
Seuraava koodi esittelee jonon käyttämistä:
|
|
||||||
|
|
||||||
|
The following code shows how a queue can be used:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
queue<int> s;
|
queue<int> s;
|
||||||
s.push(3);
|
s.push(3);
|
||||||
|
@ -613,44 +610,36 @@ cout << s.front(); // 3
|
||||||
s.pop();
|
s.pop();
|
||||||
cout << s.front(); // 2
|
cout << s.front(); // 2
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
%
|
|
||||||
% Huomaa, että rakenteiden \texttt{stack} ja \texttt{queue}
|
|
||||||
% sijasta voi aina käyttää rakenteita
|
|
||||||
% \texttt{vector} ja \texttt{deque}, joilla voi
|
|
||||||
% tehdä kaiken saman ja enemmän.
|
|
||||||
% Kuitenkin \texttt{stack} ja \texttt{queue} ovat
|
|
||||||
% kevyempiä ja hieman tehokkaampia rakenteita,
|
|
||||||
% jos niiden operaatiot riittävät algoritmin toteuttamiseen.
|
|
||||||
|
|
||||||
\subsubsection{Prioriteettijono}
|
\subsubsection{Priority queue}
|
||||||
|
|
||||||
\index{prioriteettijono@prioriteettijono}
|
\index{priority queue}
|
||||||
\index{keko@keko}
|
\index{heap}
|
||||||
\index{priority\_queue@\texttt{priority\_queue}}
|
\index{priority\_queue@\texttt{priority\_queue}}
|
||||||
|
|
||||||
\key{Prioriteettijono} (\texttt{priority\_queue})
|
A \key{priority queue} (\texttt{priority\_queue})
|
||||||
pitää yllä joukkoa alkioista.
|
maintains a set of elements.
|
||||||
Sen operaatiot ovat alkion lisäys ja
|
The supported operations are insertion and,
|
||||||
jonon tyypistä riippuen joko
|
depending on the type of the queue,
|
||||||
pienimmän alkion haku ja poisto tai
|
retrieval and removal of
|
||||||
suurimman alkion haku ja poisto.
|
either the minimum element or the maximum element.
|
||||||
Lisäyksen ja poiston aikavaativuus on $O(\log n)$
|
The time complexity is $O(\log n)$
|
||||||
ja haun aikavaativuus on $O(1)$.
|
for insertion and removal and $O(1)$ for retrieval.
|
||||||
|
|
||||||
Vaikka prioriteettijonon operaatiot
|
While a set structure efficiently supports
|
||||||
pystyy toteuttamaan myös \texttt{set}-ra\-ken\-teel\-la,
|
all the operations of a priority queue,
|
||||||
prioriteettijonon etuna on,
|
the benefit in using a priority queue is
|
||||||
että sen kekoon perustuva sisäinen
|
that it has smaller constant factors.
|
||||||
toteutus on yksinkertaisempi
|
A priority queue is usually implemented using
|
||||||
kuin \texttt{set}-rakenteen tasapainoinen binääripuu,
|
a heap structure that is much simpler than a
|
||||||
minkä vuoksi rakenne on kevyempi ja
|
balanced binary tree needed for an ordered set.
|
||||||
operaatiot ovat tehokkaampia.
|
|
||||||
|
|
||||||
\begin{samepage}
|
\begin{samepage}
|
||||||
C++:n prioriteettijono toimii oletuksena niin,
|
As default, the elements in the C++
|
||||||
että alkiot ovat järjestyksessä suurimmasta pienimpään
|
priority queue are sorted in decreasing order,
|
||||||
ja jonosta pystyy hakemaan ja poistamaan suurimman alkion.
|
and it is possible to find and remove the
|
||||||
Seuraava koodi esittelee prioriteettijonon käyttämistä:
|
largest element in the queue.
|
||||||
|
The following code shows an example:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
priority_queue<int> q;
|
priority_queue<int> q;
|
||||||
|
@ -668,76 +657,77 @@ q.pop();
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
\end{samepage}
|
\end{samepage}
|
||||||
|
|
||||||
Seuraava määrittely luo käänteisen prioriteettijonon,
|
The following definition creates a priority queue
|
||||||
jossa jonosta pystyy hakemaan ja poistamaan pienimmän alkion:
|
that supports finding and removing the minimum element:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
priority_queue<int,vector<int>,greater<int>> q;
|
priority_queue<int,vector<int>,greater<int>> q;
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
\section{Vertailu järjestämiseen}
|
\section{Comparison to sorting}
|
||||||
|
|
||||||
Monen tehtävän voi ratkaista tehokkaasti joko
|
Often it's possible to solve a problem
|
||||||
käyttäen sopivia tietorakenteita
|
using either data structures or sorting.
|
||||||
tai taulukon järjestämistä.
|
Sometimes there are remarkable differences
|
||||||
Vaikka erilaiset ratkaisutavat olisivat kaikki
|
in the actual efficiency of these approaches,
|
||||||
periaatteessa tehokkaita, niissä voi olla
|
which may be hidden in their time complexities.
|
||||||
käytännössä merkittäviä eroja.
|
|
||||||
|
|
||||||
Tarkastellaan ongelmaa, jossa
|
Let us consider a problem where
|
||||||
annettuna on kaksi listaa $A$ ja $B$,
|
we are given two lists $A$ and $B$
|
||||||
joista kummassakin on $n$ kokonaislukua.
|
that both contain $n$ integers.
|
||||||
Tehtävänä on selvittää, moniko luku
|
Our task is to calculate the number of integers
|
||||||
esiintyy kummassakin listassa.
|
that belong to both of the lists.
|
||||||
Esimerkiksi jos listat ovat
|
For example, for the lists
|
||||||
\[A = [5,2,8,9,4] \hspace{10px} \textrm{ja} \hspace{10px} B = [3,2,9,5],\]
|
\[A = [5,2,8,9,4] \hspace{10px} \textrm{and} \hspace{10px} B = [3,2,9,5],\]
|
||||||
niin vastaus on 3, koska luvut 2, 5
|
the answer is 3 because the numbers 2, 5
|
||||||
ja 9 esiintyvät kummassakin listassa.
|
and 9 belong to both of the lists.
|
||||||
Suoraviivainen ratkaisu tehtävään on käydä läpi
|
|
||||||
kaikki lukuparit ajassa $O(n^2)$, mutta seuraavaksi
|
|
||||||
keskitymme tehokkaampiin ratkaisuihin.
|
|
||||||
|
|
||||||
\subsubsection{Ratkaisu 1}
|
A straightforward solution for the problem is
|
||||||
|
to go through all pairs of numbers in $O(n^2)$ time,
|
||||||
|
but next we will concentrate on
|
||||||
|
more efficient solutions.
|
||||||
|
|
||||||
Tallennetaan listan $A$ luvut joukkoon
|
\subsubsection{Solution 1}
|
||||||
ja käydään sitten läpi listan $B$ luvut ja
|
|
||||||
tarkistetaan jokaisesta, esiintyykö se myös listassa $A$.
|
|
||||||
Joukon ansiosta on tehokasta tarkastaa,
|
|
||||||
esiintyykö listan $B$ luku listassa $A$.
|
|
||||||
Kun joukko toteutetaan \texttt{set}-rakenteella,
|
|
||||||
algoritmin aikavaativuus on $O(n \log n)$.
|
|
||||||
|
|
||||||
\subsubsection{Ratkaisu 2}
|
We construct a set of the numbers in $A$,
|
||||||
|
and after this, iterate through the numbers
|
||||||
|
in $B$ and check for each number if it
|
||||||
|
also belongs to $A$.
|
||||||
|
This is efficient because the numbers in $A$
|
||||||
|
are in a set.
|
||||||
|
Using the \texttt{set} structure,
|
||||||
|
the time complexity of the algorithm is $O(n \log n)$.
|
||||||
|
|
||||||
Joukon ei tarvitse säilyttää lukuja
|
\subsubsection{Solution 2}
|
||||||
järjestyksessä, joten
|
|
||||||
\texttt{set}-ra\-ken\-teen sijasta voi
|
|
||||||
käyttää myös \texttt{unordered\_set}-ra\-ken\-net\-ta.
|
|
||||||
Tämä on helppo tapa parantaa algoritmin
|
|
||||||
tehokkuutta, koska
|
|
||||||
algoritmin toteutus säilyy samana ja vain tietorakenne vaihtuu.
|
|
||||||
Uuden algoritmin aikavaativuus on $O(n)$.
|
|
||||||
|
|
||||||
\subsubsection{Ratkaisu 3}
|
It is not needed to maintain an ordered set,
|
||||||
|
so instead of the \texttt{set} structure
|
||||||
|
we can also use the \texttt{unordered\_set} structure.
|
||||||
|
This is an easy way to make the algorithm
|
||||||
|
more efficient because we only have to change
|
||||||
|
the data structure that the algorithm uses.
|
||||||
|
The time complexity of the new algorithm is $O(n)$.
|
||||||
|
|
||||||
Tietorakenteiden sijasta voimme käyttää järjestämistä.
|
\subsubsection{Solution 3}
|
||||||
Järjestetään ensin listat $A$ ja $B$,
|
|
||||||
minkä jälkeen yhteiset luvut voi löytää
|
|
||||||
käymällä listat rinnakkain läpi.
|
|
||||||
Järjestämisen aikavaativuus on $O(n \log n)$ ja
|
|
||||||
läpikäynnin aikavaativuus on $O(n)$,
|
|
||||||
joten kokonaisaikavaativuus on $O(n \log n)$.
|
|
||||||
|
|
||||||
\subsubsection{Tehokkuusvertailu}
|
Instead of data structures, we can use sorting.
|
||||||
|
First, we sort both lists $A$ and $B$.
|
||||||
|
After this, we iterate through both the lists
|
||||||
|
at the same time and find the common elements.
|
||||||
|
The time complexity of sorting is $O(n \log n)$,
|
||||||
|
and the rest of the algorithm works in $O(n)$ time,
|
||||||
|
so the total time complexity is $O(n \log n)$.
|
||||||
|
|
||||||
Seuraavassa taulukossa on mittaustuloksia
|
\subsubsection{Efficiency comparison}
|
||||||
äskeisten algoritmien tehokkuudesta,
|
|
||||||
kun $n$ vaihtelee ja listojen luvut ovat
|
The following table shows how efficient
|
||||||
satunnaisia lukuja välillä $1 \ldots 10^9$:
|
the above algorithms are when $n$ varies and
|
||||||
|
the elements in the lists are random
|
||||||
|
integers between $1 \ldots 10^9$:
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tabular}{rrrr}
|
\begin{tabular}{rrrr}
|
||||||
$n$ & ratkaisu 1 & ratkaisu 2 & ratkaisu 3 \\
|
$n$ & solution 1 & solution 2 & solution 3 \\
|
||||||
\hline
|
\hline
|
||||||
$10^6$ & $1{,}5$ s & $0{,}3$ s & $0{,}2$ s \\
|
$10^6$ & $1{,}5$ s & $0{,}3$ s & $0{,}2$ s \\
|
||||||
$2 \cdot 10^6$ & $3{,}7$ s & $0{,}8$ s & $0{,}3$ s \\
|
$2 \cdot 10^6$ & $3{,}7$ s & $0{,}8$ s & $0{,}3$ s \\
|
||||||
|
@ -747,26 +737,23 @@ $5 \cdot 10^6$ & $10{,}0$ s & $2{,}3$ s & $0{,}9$ s \\
|
||||||
\end{tabular}
|
\end{tabular}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Ratkaisut 1 ja 2 ovat muuten samanlaisia,
|
Solutions 1 and 2 are equal except that
|
||||||
mutta ratkaisu 1 käyttää \texttt{set}-rakennetta,
|
solution 1 uses the \texttt{set} structure
|
||||||
kun taas ratkaisu 2 käyttää
|
and solution 2 uses the \texttt{unordered\_set} structure.
|
||||||
\texttt{unordered\_set}-rakennetta.
|
In this case, this choice has a big effect on
|
||||||
Tässä tapauksessa tällä valinnalla on
|
the running time becase solution 2
|
||||||
merkittävä vaikutus suoritusaikaan,
|
is 4–5 times faster than solution 1.
|
||||||
koska ratkaisu 2 on 4–5 kertaa
|
|
||||||
nopeampi kuin ratkaisu 1.
|
|
||||||
|
|
||||||
Tehokkain ratkaisu on kuitenkin järjestämistä
|
However, the most efficient solution is solution 3
|
||||||
käyttävä ratkaisu 3, joka on vielä puolet
|
that uses sorting.
|
||||||
nopeampi kuin ratkaisu 2.
|
It only uses half of the time compared to solution 2.
|
||||||
Kiinnostavaa on, että sekä ratkaisun 1 että
|
Interestingly, the time complexity of both
|
||||||
ratkaisun 3 aikavaativuus on $O(n \log n)$,
|
solution 1 and solution 3 is $O(n \log n)$,
|
||||||
mutta siitä huolimatta
|
but despite this, solution 3 is ten times faster.
|
||||||
ratkaisu 3 vie aikaa vain kymmenesosan.
|
The explanation for this is that
|
||||||
Tämän voi selittää sillä, että
|
sorting is a simple procedure and it is done
|
||||||
järjestäminen on kevyt
|
only once at the beginning of solution 3,
|
||||||
operaatio ja se täytyy tehdä vain kerran
|
and the rest of the algorithm works in linear time.
|
||||||
ratkaisussa 3 algoritmin alussa,
|
On the other hand,
|
||||||
minkä jälkeen algoritmin loppuosa on lineaarinen.
|
solution 3 maintains a complex balanced binary tree
|
||||||
Ratkaisu 1 taas pitää yllä monimutkaista
|
during the whole algorithm.
|
||||||
tasapainoista binääripuuta koko algoritmin ajan.
|
|
Loading…
Reference in New Issue