Chapter 30 first version

This commit is contained in:
Antti H S Laaksonen 2017-01-29 18:40:11 +02:00
parent 0a9b4770ef
commit 45279464a3
1 changed files with 146 additions and 161 deletions

View File

@ -1,33 +1,31 @@
\chapter{Sweep line}
\chapter{Sweep line algorithms}
\index{pyyhkxisyviiva@pyyhkäisyviiva}
\index{sweep line}
\key{Pyyhkäisyviiva} on tason halki kulkeva viiva,
jonka avulla voi ratkaista useita geometrisia tehtäviä.
Ideana on esittää tehtävä joukkona tapahtumia,
jotka vastaavat tason pisteitä.
Kun pyyhkäisyviiva törmää pisteeseen,
tapahtuma käsitellään ja tehtävän ratkaisu edistyy.
Many geometric problems can be solved using
\key{sweep line} algorithms.
The idea in such algorithms is to represent
the problem as a set of events that correspond
to points in the plane.
The events are processed in a sorted order
according to their x or y coordinate.
Tarkastellaan esimerkkinä tekniikan
käyttämisestä tehtävää,
jossa yrityksessä on töissä $n$ henkilöä
ja jokaisesta henkilöstä tiedetään,
milloin hän tuli töihin ja lähti töistä
tiettynä päivänä.
Tehtävänä on laskea,
mikä on suurin määrä henkilöitä,
jotka olivat samaan aikaan töissä.
As an example, let us consider a problem
where there is a company that has $n$ employees,
and we know for each employee arrival and
leaving times on a certain day.
Our task is to calculate the maximum number of
employees that were in the office at the same time.
Tehtävän voi ratkaista mallintamalla tilanteen
niin, että jokaista henkilöä vastaa kaksi tapahtumaa:
tuloaika töihin ja lähtöaika töistä.
Pyyhkäisyviiva käy läpi tapahtumat aikajärjestyksessä
ja pitää kirjaa, montako henkilöä oli töissä milloinkin.
Esimerkiksi tilannetta
The problem can be solved by modelling the situation
so that each employee is assigned two events that
corresponds to the arrival and leaving times.
After sorting the events, we can go trough them
and keep track of the number of people in the office.
For example, the table
\begin{center}
\begin{tabular}{ccc}
henkilö & tuloaika & lähtöaika \\
person & arrival time & leaving time \\
\hline
Uolevi & 10 & 15 \\
Maija & 6 & 12 \\
@ -35,7 +33,7 @@ Kaaleppi & 14 & 16 \\
Liisa & 5 & 13 \\
\end{tabular}
\end{center}
vastaavat seuraavat tapahtumat:
generates the following events:
\begin{center}
\begin{tikzpicture}[scale=0.6]
\draw (0,0) rectangle (17,-6.5);
@ -59,15 +57,16 @@ vastaavat seuraavat tapahtumat:
\node at (2,-5.5) {Liisa};
\end{tikzpicture}
\end{center}
Pyyhkäisyviiva käy läpi tapahtumat vasemmalta oikealle
ja pitää yllä laskuria.
Aina kun henkilö tulee töihin, laskurin arvo
kasvaa yhdellä, ja kun henkilö lähtee töistä,
laskurin arvo vähenee yhdellä.
Tehtävän ratkaisu on suurin laskuri arvo
pyyhkäisyviivan kulun aikana.
We go through the events from left to right
and maintain a counter.
Always when a person arrives, we increase
the value of the counter by one,
and when a person leaves,
we decrease the value by one.
The answer for the problem is the maximum
value of the counter during the algorithm.
Pyyhkäisyviiva kulkee seuraavasti tason halki:
In the example, the events are processed as follows:
\begin{center}
\begin{tikzpicture}[scale=0.6]
\path[draw,thick,->] (0.5,0.5) -- (16.5,0.5);
@ -119,26 +118,24 @@ Pyyhkäisyviiva kulkee seuraavasti tason halki:
\node at (13,-8) {$1$};
\end{tikzpicture}
\end{center}
Kuvan alareunan merkinnät $+$ ja $-$
tarkoittavat, että laskurin arvo kasvaa
ja vähenee yhdellä.
Niiden alapuolella on laskurin uusi arvo.
Laskurin suurin arvo 3 on voimassa
Uolevi tulohetken ja Maijan lähtöhetken välillä.
The symbols $+$ and $-$ indicate whether the
value of the counter increases of decreases,
and the value of the counter is shown below.
The maximum value of the counter is 3
between Uolevi's arrival and Maija's leaving.
Ratkaisun aikavaativuus on $O(n \log n)$,
koska tapahtumien järjestäminen vie aikaa $O(n \log n)$
ja pyyhkäisyviivan läpikäynti vie aikaa $O(n)$.
The running time of the solution is $O(n \log n)$,
because sorting the events takes $O(n \log n)$ time
and the rest of the algorithm takes $O(n)$ time.
\section{Janojen leikkauspisteet}
\section{Intersection points}
\index{leikkauspiste@leikkauspiste}
\index{intersection point}
Annettuna on $n$ janaa, joista jokainen
on vaaka- tai pystysuuntainen.
Tehtävänä on laskea tehokkaasti, monessako pisteessä
kaksi janaa leikkaavat toisensa.
Esimerkiksi tilanteessa
Given a set of $n$ line segments, each of them being either
horizontal or vertical, the problem is to efficiently
calculate the total number of intersection points.
For example, when the line segments are
\begin{center}
\begin{tikzpicture}[scale=0.5]
\path[draw,thick,-] (0,2) -- (5,2);
@ -148,7 +145,7 @@ Esimerkiksi tilanteessa
\path[draw,thick,-] (8,2) -- (8,5);
\end{tikzpicture}
\end{center}
leikkauspisteitä on kolme:
there are three intersection points:
\begin{center}
\begin{tikzpicture}[scale=0.5]
\path[draw,thick,-] (0,2) -- (5,2);
@ -164,21 +161,20 @@ leikkauspisteitä on kolme:
\end{tikzpicture}
\end{center}
It is easy to solve the problem in $O(n^2)$ time,
because we can go through all possible pairs of segments
and check if they intersect.
However, we can solve the problem more efficiently
in $O(n \log n)$ time using a sweep line algorithm.
Tehtävä on helppoa ratkaista ajassa $O(n^2)$,
koska riittää käydä läpi kaikki mahdolliset janaparit
ja tarkistaa, moniko leikkaa toisiaan.
Seuraavaksi ratkaisemme tehtävän
ajassa $O(n \log n)$ pyyhkäisyviivan avulla.
Ideana on luoda janoista kolmenlaisia tapahtumia:
The idea is to generate two types of events:
\begin{enumerate}[noitemsep]
\item[(1)] vaakajana alkaa
\item[(2)] vaakajana päättyy
\item[(3)] pystyjana
\item[(1)] horizontal segment begins
\item[(2)] horizontal segment ends
\item[(3)] vertical segment
\end{enumerate}
Esimerkkitilannetta vastaava pistejoukko on seuraava:
The following events correspond to the example:
\begin{center}
\begin{tikzpicture}[scale=0.6]
\path[draw,dashed] (0,2) -- (5,2);
@ -199,37 +195,35 @@ Esimerkkitilannetta vastaava pistejoukko on seuraava:
\end{tikzpicture}
\end{center}
Algoritmi käy läpi pisteet vasemmalta oikealle
ja pitää yllä tietorakennetta y-koordinaateista,
joissa on tällä hetkellä aktiivinen vaakajana.
Tapahtuman 1 kohdalla vaakajanan y-koordinaatti
lisätään joukkoon ja tapahtuman 2 kohdalla
vaakajanan y-koordinaatti poistetaan joukosta.
We go through the events from left to right
and use a data structure that maintains a set of
y coordinates where there is an active horizontal segment.
At event 1, we add the y coordinate of the segment
to the set, and at event 2, we remove the
y coordinate from the set.
Algoritmi laskee janojen leikkauspisteet
tapahtumien 3 kohdalla.
Kun pystyjana kulkee y-koordinaattien
$y_1 \ldots y_2$ välillä,
algoritmi laskee tietorakenteesta,
monessako vaakajanassa on y-koordinaatti
välillä $y_1 \ldots y_2$ ja kasvattaa
leikkauspisteiden määrää tällä arvolla.
Intersection points are calculated at event 3.
When there is a vertical segment between points
$y_1$ and $y_2$, we count the number of active
horizontal segments whose y coordinate is between
$y_1$ and $y_2$, and at this number to the total
number of intersection points.
Sopiva tietorakenne vaakajanojen y-koordinaattien
tallentamiseen on bi\-nää\-ri-indeksipuu tai segmenttipuu,
johon on tarvittaessa yhdistetty indeksien pakkaus.
Tällöin jokaisen pisteen käsittely
vie aikaa $O(\log n)$, joten algoritmin
kokonaisaikavaativuus on $O(n \log n)$.
An appropriate data structure for storing
y coordinates of horizontal segments is either
a binary-indexed tree or a segment tree,
possibly with index compression.
Using such structures, processing each event
takes $O(\log n)$ time, so the total running
time of the algorithm is $O(n \log n)$.
\section{Lähin pistepari}
\section{Nearest points}
\index{lzhin pistepari@lähin pistepari}
\index{nearest points}
Seuraava tehtävämme on etsiä $n$ pisteen
joukosta kaksi pistettä, jotka ovat
mahdollisimman lähellä toisiaan.
Esimerkiksi tilanteessa
Given a set of $n$ points, our next problem is
to find two points whose distance is minimum.
For example, if the points are
\begin{center}
\begin{tikzpicture}[scale=0.7]
\draw (0,0)--(12,0)--(12,4)--(0,4)--(0,0);
@ -251,7 +245,7 @@ Esimerkiksi tilanteessa
\end{tikzpicture}
\end{center}
\begin{samepage}
lähin pistepari on seuraava:
we should find the following points:
\begin{center}
\begin{tikzpicture}[scale=0.7]
\draw (0,0)--(12,0)--(12,4)--(0,4)--(0,0);
@ -274,32 +268,30 @@ lähin pistepari on seuraava:
\end{center}
\end{samepage}
Tämäkin tehtävä ratkeaa
$O(n \log n)$-ajassa pyyhkäisyviivan avulla.
Algoritmi käy pisteet läpi vasemmalta oikealle
ja pitää yllä arvoa $d$,
joka on pienin kahden
pisteen etäisyys.
Kunkin pisteen kohdalla algoritmi
etsii lähimmän toisen pisteen vasemmalta.
Jos etäisyys tähän pisteeseen on alle $d$,
tämä on uusi pienin kahden pisteen etäisyys
ja algoritmi päivittää $d$:n arvon.
This problem can be also solved in $O(n \log n)$ time
using a sweep line algorithm.
We go through the points from left to right
and maintain a value $d$: the minimum distance
between two points so far.
At each point, we find the nearest point to the left.
If the distance is less than $d$, it is the
new minimum distance and we update
the value of $d$.
Jos käsiteltävä piste on $(x,y)$
ja jokin vasemmalla oleva piste on
alle $d$:n etäisyydellä,
sen x-koordinaatin
tulee olla välillä $[x-d,x]$
ja y-koordinaatin tulee olla välillä $[y-d,y+d]$.
Algoritmin riittää siis tarkistaa
ainoastaan pisteet, jotka osuvat tälle välille,
mikä tehostaa hakua merkittävästi.
If the current point is $(x,y)$
and there is a point to the left
within a distance of less than $d$,
the x coordinate of such a point must
be between $[x-d,x]$ and the y coordinate
must be between $[y-d,y+d]$.
Thus, it suffices to only consider points
that are located in those ranges,
which makes the algorithm efficient.
Esimerkiksi seuraavassa kuvassa
katkoviiva-alue sisältää pisteet,
jotka voivat olla alle $d$:n etäisyydellä
tummennetusta pisteestä.
For example, in the following picture the
region marked with dashed lines contains
the points that can be within a distance of $d$
from the active point:
\\
\begin{center}
\begin{tikzpicture}[scale=0.7]
@ -332,34 +324,31 @@ tummennetusta pisteestä.
\end{tikzpicture}
\end{center}
Algoritmin tehokkuus perustuu siihen,
että $d$:n rajoittamalla alueella
on aina vain $O(1)$ pistettä.
Nämä pisteet pystyy käymään läpi
$O(\log n)$-aikaisesti
pitämällä algoritmin aikana yllä joukkoa pisteistä,
joiden x-koordinaatti on välillä $[x-d,x]$
ja jotka on järjestetty y-koordinaatin mukaan.
The efficiency of the algorithm is based on the fact
that the region limited by $d$ always contains
only $O(1)$ points.
We can go through those points in $O(\log n)$ time
by maintaining a set of points whose x coordinate
is between $[x-d,x]$ and that are sorted according
to the y coordinate.
Algoritmin aikavaativuus on $O(n \log n)$,
koska se käy läpi $n$ pistettä
ja etsii jokaiselle lähimmän
edeltävän pisteen ajassa $O(\log n)$.
The time complexity of the algorithm is $O(n \log n)$,
because it goes through $n$ points and
finds for each point the nearest point to the left
in $O(\log n)$ time.
\section{Konveksi peite}
\section{Convex hull}
\key{Konveksi peite}
on pienin konveksi monikulmio,
joka ympäröi kaikki pistejoukon pisteet.
Konveksius tarkoittaa,
että minkä tahansa kahden kärkipisteen välinen jana
kulkee monikulmion sisällä.
Hyvä mielikuva asiasta on,
että pistejoukko ympäröidään tiukasti
viritetyllä narulla.
The \key{convex hull} is the smallest convex polygon
that contains all points in a given set.
Convexity means that a line segment between
any two vertices of the polygon is completely
inside the polygon.
A good intuitive definition is that we surround
the points using a tight rope.
\begin{samepage}
Esimerkiksi pistejoukon
For example, for the points
\begin{center}
\begin{tikzpicture}[scale=0.7]
\draw (0,0) circle [radius=0.1];
@ -379,7 +368,7 @@ Esimerkiksi pistejoukon
\end{tikzpicture}
\end{center}
\end{samepage}
konveksi peite on seuraava:
the convex hull is as follows:
\begin{center}
\begin{tikzpicture}[scale=0.7]
\draw (0,0)--(4,-1)--(7,1)--(6,3)--(2,4)--(0,2)--(0,0);
@ -401,34 +390,30 @@ konveksi peite on seuraava:
\end{tikzpicture}
\end{center}
\index{Andrew'n algoritmi}
\index{Andrew's algorithm}
Tehokas ja helposti toteutettava menetelmä
konveksin peitteen muodostamiseen
on \key{Andrew'n algoritmi},
jonka aikavaativuus on $O(n \log n)$.
Algoritmi muodostaa konveksin peitteen kahdessa
osassa: ensin peitteen yläosan ja sitten peitteen alaosan.
Kummankin osan muodostaminen tapahtuu samalla tavalla,
minkä vuoksi voimme keskittyä yläosan muodostamiseen.
A good way to construct the convex hull
is \key{Andrew's algorithm}
that works in $O(n \log n)$ time.
The algorithm constructs the convex hull
in two steps:
first the upper hull and then the lower hull.
Both steps are similar, so we can focus on
constructing the upper hull.
Algoritmi järjestää ensin pisteet ensisijaisesti x-koordinaatin
ja toissijaisesti y-koordinaatin mukaan.
Tämän jälkeen se käy pisteet läpi järjestyksessä
ja lisää aina uuden pisteen osaksi peitettä.
Aina pisteen lisäämisen jälkeen algoritmi tarkastaa
ristitulon avulla,
muodostavatko kolme viimeistä pistettä peitteessä
vasemmalle kääntyvän osan.
Jos näin on,
algoritmi poistaa näistä keskimmäisen pisteen.
Tämän jälkeen algoritmi tarkastaa uudestaan
kolme viimeistä pistettä ja poistaa taas tarvittaessa
keskimmäisen pisteen.
Sama jatkuu, kunnes kolme viimeistä pistettä
eivät muodosta vasemmalle kääntyvää osaa.
We sort the points primarily according to
x coordinates and secondarily according to y coordinates.
After this, we go through the points and always
add the new point to the hull.
After adding a point we check using cross products
whether the tree last point in the hull turn left.
If this holds, we remove the middle point from the hull.
After this we keep checking again the three last points
and removing points, until the three last points
don't turn left.
Seuraava kuvasarja esittää Andrew'n algoritmin toimintaa:
The following pictures show how
Andrew's algorithm works:
\\
\begin{tabular}{ccccccc}
\\