147 lines
7.9 KiB
TeX
147 lines
7.9 KiB
TeX
\documentclass[a4paper, 11pt]{article}
|
|
|
|
\usepackage[utf8]{inputenc}
|
|
\usepackage[T1]{fontenc}
|
|
\usepackage[dutch]{babel}
|
|
\usepackage[margin=2.56cm]{geometry}
|
|
\usepackage[linesnumbered, algoruled, lined]{algorithm2e}
|
|
\usepackage{float}
|
|
|
|
\title{Project: Snijdende cirkels}
|
|
\author{Arthur Bols \& Ruben Van Laer}
|
|
\date{Mei 2019}
|
|
|
|
\newcommand{\CS}{C\nolinebreak\hspace{-.05em}\raisebox{.4ex}{\tiny\bf +}\nolinebreak\hspace{-.10em}\raisebox{.4ex}{\bf +}}
|
|
\def\CS{{C\nolinebreak[4]\hspace{-.05em}\raisebox{.4ex}{\bf \#}}}
|
|
|
|
\setlength\parindent{0em}
|
|
|
|
\begin{document}
|
|
\maketitle
|
|
|
|
\section{Inleiding}
|
|
In dit practicum bespreken we algoritmes die de onderlinge snijpunten van willekeurige cirkels bepalen. We trachten algoritmes met vooropgestelde complexiteit te ontwerpen en analyseren. Dit doen we door eerst en vooral een hoogniveau beschrijving van de algoritmes op te stellen (Sectie \ref{hoogniveau}) en deze dan te implementeren. Hierna testen we de respectievelijke implementaties op hun rekencomplexiteit (Sectie \ref{exp}) door de rekentijd te meten en te plotten (Sectie \ref{comp}).
|
|
|
|
\section{Hoogniveau beschrijving van de algoritmen}
|
|
\label{hoogniveau}
|
|
\subsection{Simpel algoritme $O(N^2)$}
|
|
|
|
Een eerste algoritme dat we zullen beschouwen is de meest eenvoudige manier om de onderlinge snijpunten tussen cirkels te bepalen. Het is simpelweg het nagaan van de snijpunten van elke cirkel met elke andere cirkel. In het geval van $N$ cirkels, wordt in algoritme \ref{algo1} de buitenste for-lus $N$ maal uitgevoerd. De binnenste for-lus wordt $N-1$ maal uitgevoerd. Dit geeft een complexiteit van $N*(N-1) = N^2 - N = O(N^2)$ voor het berekenen van de snijpunten.\\
|
|
|
|
\begin{algorithm}[H]
|
|
\{$cirkels$: lijst van cirkels met een middelpunt en een straal\}\\
|
|
$S \gets \emptyset$ \{$S$: lijst met alle snijpunten\}\\
|
|
\For{iedere $c_1$ in $cirkels$}{
|
|
Verwijder($cirkels$, $c_1$)\\
|
|
\For{iedere $c_2$ in ($cirkels$)}{
|
|
$snpt \gets$ Snijpunten($c_1$,$c_2$)\\
|
|
\If{$snpt \neq \emptyset$}{
|
|
Voegtoe($S$,$snpt$)
|
|
}
|
|
}
|
|
}
|
|
\caption{Simpel Algoritme}
|
|
\label{algo1}
|
|
\end{algorithm}
|
|
\subsection{Doorlooplijnalgoritme $O(N^2)$}
|
|
|
|
Het tweede algoritme dat we beschouwen, is een algoritme gebaseerd op het concept van een verticale doorlooplijn. We slaan het begin- en eindpunt van elke cirkel op en sorteren deze punten op basis van de x-coördinaat. Hierna gaan we van klein naar groot door al deze punten en markeren we cirkels bij het startpunt als actief. Bij het eindpunt worden ze weer inactief. Elke keer we een startpunt tegenkomen, bepalen we alle snijpunten van deze nieuwe cirkel met alle andere actieve cirkels. De complexiteit in het slechtste geval, zijnde wanneer alle cirkels tegelijkertijd actief zijn, zorgt voor het zoeken van snijpunten met alle andere cirkels zoals in algoritme \ref{algo1}, wat via dezelfde redenering een rekencomplexiteit van de grootte-orde $N^2$ oplevert.
|
|
|
|
\begin{algorithm}[H]
|
|
\{$cirkels$: lijst van cirkels met een middelpunt en een straal\}\\
|
|
$E \gets \emptyset$ \{$E$: gesorteerde rij met de horizontale begin- en eindpunten van elke cirkel (ook wel de 'Events' van de doorlooplijn)\}\\
|
|
\For{iedere $c$ in $cirkels$}{
|
|
$p_1 \gets$ Beginpunt($c$)\\
|
|
$p_2 \gets$ Eindpunt($c$)\\
|
|
Voegtoe($E$, $p_1$)\\
|
|
Voegtoe($E$, $p_2$)
|
|
}
|
|
$S \gets \emptyset$ \{$S$: lijst met alle snijpunten\}\\
|
|
$A \gets \emptyset$ \{$A$: lijst met cirkels die snijden met de doorlooplijn\}\\
|
|
\While{$E \neq \emptyset$}{
|
|
$e \gets$ Kleinste($E$)\\
|
|
Verwijder($E$, $e$)\\
|
|
\If{$e$ is een beginpunt}{
|
|
$c \gets$ Cirkel($e$)\\
|
|
\For{iedere $c_i$ in $A$}{
|
|
$snpt \gets$ Snijpunten($c$,$c_i$)\\
|
|
\If{$snpt \neq \emptyset$}{
|
|
Voegtoe($S$,$snpt$)
|
|
}
|
|
}
|
|
Voegtoe($A$, $c$)\\
|
|
}
|
|
\If{$e$ is een eindpunt}{
|
|
$c \gets$ Cirkel($e$)\\
|
|
Verwijder($A$, $c$)
|
|
}
|
|
}
|
|
\caption{Doorlooplijnalgoritme $O(N^2)$}
|
|
\label{algo2}
|
|
\end{algorithm}
|
|
|
|
\subsection{Doorlooplijnalgoritme $O((N+S)log_2(N))$}
|
|
|
|
Het derde en laatst beschouwde algoritme is een variant op algoritme \ref{algo2}, maar dan met efficiëntere gegevensstructuren. Het concept hierbij is hetzelfde, maar in plaats van het nagaan van de snijpunten met alle andere actieve cirkels, worden de actieve cirkels gesorteerd in een binaire boom-structuur op basis van de volgorde waarin ze de doorlooplijn snijden. Ervan uitgaande dat maximaal twee cirkels elkaar in hetzelfde punt snijden, werkt dit omdat vlak voor de snijding van twee cirkels, deze opeenvolgende cirkels zijn. De binaire boom-structuur garandeert een complexiteit van $log_2(N)$ voor het zoeken in de boom. Aangezien er $N$ cirkels zijn en er voor deze $N$ telkens de voorganger en opvolger gezocht dient te worden, vinden we een complexiteit van $2Nlog_2(N)$. Dit is $O(Nlog_2(N))$ en dus zeker ook $O((N+S)log_2(N))$.
|
|
|
|
\begin{algorithm}[H]
|
|
\{$cirkels$: lijst van cirkels met een middelpunt en een straal\}\\
|
|
$E \gets \emptyset$ \{$E$: gesorteerde rij met de horizontale begin- en eindpunten van elke cirkel (ook wel de 'Events' van de doorlooplijn)\}\\
|
|
\For{iedere $c$ in $cirkels$}{
|
|
$p_1 \gets$ Beginpunt($c$)\\
|
|
$p_2 \gets$ Eindpunt($c$)\\
|
|
Voegtoe($E$, $p_1$)\\
|
|
Voegtoe($E$, $p_2$)
|
|
}
|
|
$S \gets \emptyset$ \{$S$: lijst met alle snijpunten\}\\
|
|
$A \gets \emptyset$ \{$A$: (gesorteerde) binaire boom met cirkels die snijden met de doorlooplijn\}\\
|
|
\While{$E \neq \emptyset$}{
|
|
$e \gets$ Kleinste($E$)\\
|
|
$c \gets$ Cirkel($e$)\\
|
|
Verwijder($E$, $e$)\\
|
|
\If{$e$ is een beginpunt}{
|
|
|
|
Voegtoe($A$, $c$)\\
|
|
$c_v \gets$ Voorganger($c$)\\
|
|
$c_o \gets$ Opvolger($c$)\\
|
|
$snpt \gets$ Snijpunten($c$,$c_v$)\\
|
|
\If{$snpt \neq \emptyset$}{
|
|
Voegtoe($S$,$snpt$)
|
|
}
|
|
$snpt \gets$ Snijpunten($c$,$c_o$)\\
|
|
\If{$snpt \neq \emptyset$}{
|
|
Voegtoe($S$,$snpt$)
|
|
}
|
|
}
|
|
\If{$e$ is een eindpunt}{
|
|
Verwijder($A$, $c$)
|
|
}
|
|
}
|
|
\caption{Doorlooplijnalgoritme $O((N+S)log_2(N))$}
|
|
\end{algorithm}
|
|
\section{Beschrijving experimenten}\label{exp}
|
|
|
|
Om de complexiteit van de algoritmen na te gaan, voeren we experimenten op een toenemend aantal cirkels uit. We starten van 10 cirkels en verdubbelen steeds het aantal. Bij elk aantal voeren we 100 testen uit, meten we de tijd die het algoritme erover doet en nemen we het gemiddelde. Zo gaan we door tot ongeveer 10.000 cirkels voor het simpele algoritme en tot ongeveer 80.000 cirkels voor de andere algoritmes. Deze gemiddelde tijd kunnen we dan plotten in functie van het aantal cirkels en deze vergelijken met de grafiek van de complexiteit die we verwachten, met eventueel verschillende factoren. Ook kunnen we het beschouwen als een 'doubling ratio'-experiment en de verhouding tussen de opeenvolgende gemiddelde tijden berekenen. Deze 'doubling ratio' geeft ook een idee van de grootte-orde van de complexiteit. Zo geeft een verhouding van ongeveer 4 weer dat bij een verdubbeling de tijd met een factor 4 toeneemt. Dit wijst op een kwadratisch verband tussen het aantal cirkels en de tijd.
|
|
|
|
De concrete resultaten zijn te vinden in Sectie \ref{comp}.
|
|
|
|
\section{Correctheid van het algoritme}
|
|
|
|
Om de correctheid van de drie algoritmes na te gaan, bewijzen we eerst de correctheid van het simpele algoritme. Hierna voeren we een groot aantal testen uit waarbij zowel het simpele algoritme als een van de andere algoritme uitgevoerd worden. Na het uitvoeren vergelijken we de uitvoer met elkaar, waarin we nagaan of de resultaten op kleine afrondingsfouten na, hetzelfde zijn. Indien dit zo is voor het grote aantal testen, gaan we ervan uit dat het andere algoritme correct is.
|
|
|
|
\section{Bespreking resultaten en rekentijden}\label{comp}
|
|
|
|
\subsection{Simpel algoritme}
|
|
|
|
Grafiek + vermoedelijke complexiteit.
|
|
|
|
\subsection{Doorlooplijnalgoritme $O(N^2)$}
|
|
|
|
Grafiek + vermoedelijke complexiteit.
|
|
|
|
\subsection{Doorlooplijnalgoritme $O((N+S)log_2(N))$}
|
|
|
|
Grafiek + vermoedelijke complexiteit.
|
|
|
|
\end{document}
|