Chapter 28 first version

This commit is contained in:
Antti H S Laaksonen 2017-01-29 16:09:47 +02:00
parent 4994a68057
commit 1f0c8e66af
1 changed files with 142 additions and 147 deletions

View File

@ -538,19 +538,21 @@ of equal degree as $p(u)$.
For example, if $p(u)=t_2 u^2+t_1 u-t_0$, then For example, if $p(u)=t_2 u^2+t_1 u-t_0$, then
\[p'(u)=t_2(u+h)^2+t_1(u+h)-t_0=t_2 u^2 + (2ht_2+t_1)u+t_2h^2+t_1h-t_0.\] \[p'(u)=t_2(u+h)^2+t_1(u+h)-t_0=t_2 u^2 + (2ht_2+t_1)u+t_2h^2+t_1h-t_0.\]
\section{Dynaaminen toteutus} \section{Dynamic trees}
\index{dynaaminen segmenttipuu@dynaaminen segmenttipuu} \index{dynamic segment tree}
Tavallinen segmenttipuu on staattinen, A regular segment tree is static,
eli jokaiselle solmulle on paikka taulukossa which means that each node has a fixed position
ja puu vie kiinteän määrän muistia. in the array and storing the tree requires
Tämä toteutus kuitenkin tuhlaa muistia, a fixed amount of memory.
jos suurin osa puun solmuista on tyhjiä. However, if most nodes are empty, such an
\key{Dynaaminen segmenttipuu} varaa muistia vain implementation wastes memory.
niille solmuille, joita todella tarvitaan. In a \key{dynamic segment tree},
memory is reserved only for nodes that
are actually needed.
Solmut on kätevää tallentaa tietueina tähän tapaan: The nodes can be represented as structs as follows:
\begin{lstlisting} \begin{lstlisting}
struct node { struct node {
@ -560,42 +562,39 @@ struct node {
node(int x, int a, int b) : s(s), x(x), y(y) {} node(int x, int a, int b) : s(s), x(x), y(y) {}
}; };
\end{lstlisting} \end{lstlisting}
Tässä $s$ on solmussa oleva arvo, Here $s$ is the value of the node,
$[x,y]$ on solmua vastaava väli $[x,y]$ is the corresponding range,
ja $l$ ja $r$ osoittavat and $l$ and $r$ point to the left
solmun vasempaan ja oikeaan alipuuhun. and right subtree.
Tämän jälkeen solmuja voi käsitellä seuraavasti: After this, nodes can be manipulated as follows:
\begin{lstlisting} \begin{lstlisting}
// uuden solmun luonti // create new node
node *u = new node(0, 0, 15); node *u = new node(0, 0, 15);
// kentän muuttaminen // change value
u->s = 5; u->s = 5;
\end{lstlisting} \end{lstlisting}
\subsubsection{Harva segmenttipuu} \subsubsection{Sparse segment tree}
\index{harva segmenttipuu@harva segmenttipuu} \index{sparse segment tree}
Dynaaminen segmenttipuu on hyödyllinen, A dynamic segment tree is useful if
jos puun indeksialue $[0,N-1]$ on \textit{harva} the range $[0,N-1]$ covered by the tree is \emph{sparse},
eli $N$ on suuri mutta vain which means that $N$ is large but only a
pieni osa indekseistä on käytössä. small portion of the indices are used.
Siinä missä tavallinen segmenttipuu While a regular segment tree uses $O(n)$ memory,
vie muistia $O(N)$, a dynamic segment tree only uses $O(n \log N)$ memory,
dynaaminen segmenttipuu vie muistia where $n$ is the number of indices used.
vain $O(n \log N)$, missä $n$ on
käytössä olevien indeksien määrä.
\key{Harva segmenttipuu} aluksi tyhjä A \key{sparse segment tree} is initially empty
ja sen ainoa solmu on $[0,N-1]$. and its only node is $[0,N-1]$.
Kun puu muuttuu, siihen lisätään When the tree changes, new nodes are added dynamically
solmuja dynaamisesti sitä mukaa kuin niitä tarvitaan always when they are needed because of new indices.
uusien indeksien vuoksi. For example, if $N=16$, and the elements
Esimerkiksi jos $N=16$ ja indeksejä in indices 3 and 10 have been changes,
3 ja 10 on muutettu, the tree contains the following nodes:
puu sisältää seuraavat solmut:
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.9] \begin{tikzpicture}[scale=0.9]
\scriptsize \scriptsize
@ -621,43 +620,45 @@ puu sisältää seuraavat solmut:
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
Reitti puun juuresta lehteen sisältää Any path from the root to a leaf contains
$O(\log N)$ solmua, $O(\log N)$ nodes,
joten jokainen muutos puuhun lisää so each change adds at most $O(\log n)$
enintään $O(\log N)$ uutta solmua puuhun. new nodes to the tree.
Niinpä $n$ muutoksen jälkeen puussa Thus, after $n$ changes, the tree contains
on enintään $O(n \log N)$ solmua. at most $O(n \log N)$ nodes.
Huomaa, että jos kaikki tarvittavat indeksit Note that if all indices of the elements
ovat tiedossa are known at the beginning of the algorithm,
algoritmin alussa, dynaamisen segmenttipuun a dynamic segment tree is not needed,
sijasta voi käyttää tavallista segmenttipuuta but we can use a regular segment tree with
ja indeksien pakkausta (luku 9.4). index compression (Chapter 9.4).
Tämä ei ole kuitenkaan mahdollista, However, this is not possible if the indices
jos indeksit syntyvät vasta algoritmin aikana. are generated during the algorithm.
\subsubsection{Persistentti segmenttipuu} \subsubsection{Persistent segment tree}
\index{persistentti segmenttipuu@persistentti segmenttipuu} \index{persistent segment tree}
\index{muutoshistoria@muutoshistoria} \index{version history}
Dynaamisen toteutuksen avulla on myös Using a dynamic implementation,
mahdollista luoda \key{persistentti segmenttipuu}, it is also possible to create a
joka säilyttää puun muutoshistorian. \key{persistent segment tree} that stores
Tällöin muistissa on jokainen the \key{version history} of the tree.
segmenttipuun vaihe, joka on esiintynyt In such an implementation, we can
algoritmin suorituksen aikana. efficiently access
all versions of the tree that have been
existed during the algorithm.
Muutoshistorian hyötynä on, When the version history is available,
että kaikkia vanhoja puita voi käsitellä we can access all versions of the tree
segmenttipuun tapaan, like a regular segment tree, because their
koska niiden rakenne on edelleen olemassa. structure is stored.
Vanhoista puista voi myös johtaa uusia We can also derive new trees from the history
puita, joita voi muokata edelleen. and further manipulate them.
Tarkastellaan esimerkiksi seuraavaa muutossarjaa, Consider the following sequence of updates,
jossa punaiset solmut muuttuvat päivityksessä where red nodes change in an update
ja muut solmut säilyvät ennallaan: and other nodes remain the same:
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.8] \begin{tikzpicture}[scale=0.8]
@ -703,18 +704,18 @@ ja muut solmut säilyvät ennallaan:
\path[draw,thick,->] (3c) -- (6c); \path[draw,thick,->] (3c) -- (6c);
\path[draw,thick,->] (3c) -- (7c); \path[draw,thick,->] (3c) -- (7c);
\node at (3,-3) {vaihe 1}; \node at (3,-3) {step 1};
\node at (3+5,-3) {vaihe 2}; \node at (3+5,-3) {step 2};
\node at (3+10,-3) {vaihe 3}; \node at (3+10,-3) {step 3};
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
Jokaisen muutoksen jälkeen suurin osa puun After each update, most nodes in the tree
solmuista säilyy ennallaan, remain the same,
joten muistia säästävä tapa tallentaa muutoshistoria so an efficient way to store the version history
on käyttää mahdollisimman paljon hyväksi is to represent each tree in the history as a combination
puun vanhoja osia muutoksissa. of new nodes and subtrees of previous trees.
Tässä tapauksessa muutoshistorian voi In this case, the version history can be
tallentaa seuraavasti: stored as follows:
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.8] \begin{tikzpicture}[scale=0.8]
\path[use as bounding box] (0, 1) rectangle (16, -3.5); \path[use as bounding box] (0, 1) rectangle (16, -3.5);
@ -755,36 +756,35 @@ tallentaa seuraavasti:
\path[draw,thick,->] (3c) -- (7c); \path[draw,thick,->] (3c) -- (7c);
\node at (3,-3) {vaihe 1}; \node at (3,-3) {step 1};
\node at (3+5,-3) {vaihe 2}; \node at (3+5,-3) {step 2};
\node at (3+10,-3) {vaihe 3}; \node at (3+10,-3) {step 3};
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
Nyt muistissa on jokaisesta puun vaiheesta The structure of each version of the tree in the history can be
puun juuri, jonka avulla pystyy selvittämään reconstructed by following the pointers from the root.
koko puun rakenteen kyseisellä hetkellä. Each update only adds $O(\log N)$ new nodes to the tree
Jokainen muutos tuo vain $O(\log N)$ uutta solmua puuhun, when the indices are $[0,N-1]$,
kun puun indeksialue on $[0,N-1]$, so it is possible to store the full version history
joten koko muutoshistorian pitäminen muistissa on mahdollista. of the tree.
\section{Tietorakenteet} \section{Data structures}
Segmenttipuun solmussa voi olla Insted of a single value, a node in a segment tree
yksittäisen arvon can also contain a data structure that maintains information
sijasta myös jokin tietorakenne, about the corresponding range.
joka pitää yllä tietoa solmua vastaavasta välistä. In this case, the operations of the tree take
Tällöin segmenttipuun operaatiot vievät aikaa $O(f(n) \log n)$ time, where $f(n)$ is
$O(f(n) \log n)$, missä $f(n)$ on the time needed for retrieving or updating the
yksittäisen solmun tietorakenteen information in a single node.
käsittelyyn kuluva aika.
Tarkastellaan esimerkkinä segmenttipuuta, As an example, consider a segment tree that
jonka avulla voi laskea, montako kertaa supports queries of the form
luku $x$ esiintyy taulukon välillä $[a,b]$. ''how many times does element $x$ appear
Esimerkiksi seuraavassa taulukossa in range $[a,b]$?''
luku 1 esiintyy kolme kertaa For example, element 1 appears three times
merkityllä välillä: in the following subarray:
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.7] \begin{tikzpicture}[scale=0.7]
@ -802,16 +802,17 @@ merkityllä välillä:
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
Ideana on toteuttaa segmenttipuu, jonka The idea is to construct a segment tree
jokaisessa solmussa on tietorakenne, where each node has a data structure
josta voi kysyä, that can return the number of any element $x$
montako kertaa luku $x$ esiintyy solmun välillä. in the range.
Tällaisen segmenttipuun avulla Using such a segment tree,
vastaus kyselyyn syntyy laskemalla yhteen the answer for a query can be calculated
esiintymismäärät väleiltä, joista $[a,b]$ muodostuu. by combining the results from the nodes
that belong to the range.
Esimerkiksi yllä olevasta taulukosta syntyy For example, the following segment tree
seuraava segmenttipuu: corresponds to the above array:
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.7] \begin{tikzpicture}[scale=0.7]
@ -955,35 +956,32 @@ seuraava segmenttipuu:
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
Each node in the tree should contain
an appropriate data structure, for example a
\texttt{map} structure.
In this case, the time needed for accessing
a node is $O(\log n)$, so the total time complexity
of a query is $O(\log^2 n)$.
Sopiva tietorakenne segmenttipuun toteuttamiseen on Data structures in nodes increase the memory usage
hakemistorakenne, joka pitää kirjaa välillä esiintyvien of the tree.
lukujen määrästä. In this example, $O(n \log n)$ memory is needed,
Esimerkiksi \texttt{map}-ra\-ken\-net\-ta käyttäen because the tree consists of $O(\log n)$ levels,
yhden solmun käsittely vie aikaa $O(\log n)$, and the map structures contain a total
minkä seurauksena kyselyn aikavaativuus on $O(\log^2 n)$. of $O(n)$ values at each level.
Solmuissa olevat tietorakenteet kasvattavat \section{Two-dimensionality}
segmenttipuun muistinkäyttöä.
Tässä tapauksessa
segmenttipuu vie tilaa $O(n \log n)$,
koska siinä on $O(\log n)$ tasoa, joista
jokaisella hakemistorakenteet sisältävät $O(n)$ lukua.
\section{Kaksiulotteisuus} \index{two-dimensional segment tree}
\index{kaksiulotteinen segmenttipuu@kaksiulotteinen segmenttipuu} A \key{two-dimensional segment tree} supports
queries about rectangles in a two-dimensional array.
Such a segment tree can be implemented as
nested segmenet trees: a big tree corresponds to the
rows in the array, and each node contains a small tree
that corresponds to a column.
\key{Kaksiulotteinen segmenttipuu} mahdollistaa For example, in the array
kaksiulotteisen taulukon
suorakulmaisia alueita koskevat kyselyt.
Tällaisen segmenttipuun voi toteuttaa
sisäkkäisinä segmenttipuina:
suuri puu vastaa taulukon rivejä
ja sen kussakin solmussa on pieni puu,
joka vastaa taulukon sarakkeita.
Esimerkiksi taulukon
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.7] \begin{tikzpicture}[scale=0.7]
\draw (0,0) grid (4,4); \draw (0,0) grid (4,4);
@ -1009,7 +1007,8 @@ Esimerkiksi taulukon
\node[anchor=center] at (3.5, 3.5) {6}; \node[anchor=center] at (3.5, 3.5) {6};
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
alueiden summia voi laskea seuraavasta segmenttipuusta: sums of rectangles can be calculated
from the following segment tree:
\begin{center} \begin{center}
\begin{tikzpicture}[scale=0.4] \begin{tikzpicture}[scale=0.4]
\footnotesize \footnotesize
@ -1155,16 +1154,12 @@ alueiden summia voi laskea seuraavasta segmenttipuusta:
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
Kaksiulotteisen The operations in a two-dimensional segment tree
segmenttipuun operaatiot vievät aikaa take $O(\log^2 n)$ time, because the big tree
$O(\log^2 n)$, and each small tree contain $O(\log n)$ levels.
koska suuressa puussa ja kussakin The tree uses $O(n^2)$ memory, because each
pienessä puussa on $O(\log n)$ tasoa. small tree uses $O(n)$ memory.
Segmenttipuu vie muistia $O(n^2)$,
koska jokainen pieni puu
vie muistia $O(n)$.
Vastaavalla tavalla voi luoda myös segmenttipuita,
joissa on vielä enemmän ulottuvuuksia,
mutta tälle on harvoin tarvetta.
Using a similar idea, it is also possible to create
segment trees with more dimensions,
but this is rarely needed.