Chapter 4 first version

This commit is contained in:
Antti H S Laaksonen 2016-12-31 17:35:06 +02:00
parent 03c12213d9
commit 46f2aa6b71
1 changed files with 127 additions and 140 deletions

View File

@ -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 45 times faster than solution 1.
koska ratkaisu 2 on 45 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.