Commit 16673a76 authored by Sander van Veen's avatar Sander van Veen

Added 'Algorithm & Complexity' to the repository.

This commit includes assignment 2 and the syllabus of the
subject Algorithm & Complexity of the University of Amsterdam.
parents
\documentclass[10pt,a4paper]{article}
\usepackage[latin1]{inputenc}
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amssymb}
\usepackage{enumerate}
\usepackage{listings}
\usepackage{url}
\usepackage{float}
\title{Algoritme en complexiteit: opgaves deel 2}
\author{Sander van Veen \& Richard Torenvliet \\ Tadde\"us Kroes \& Jayke Meijer}
% dot graphs
\usepackage{dot2texi}
\usepackage{tikz}
\usetikzlibrary{shapes,arrows}
\begin{document}
\maketitle
\tableofcontents
\pagebreak
\section{Langste pad in een boom}
We kiezen een blad, dus een knoop met slechts 1 connectie naar een andere
knoop, en stellen dit als wortel. We krijgen nu dus een boom die helemaal aan 1
kant van de wortel `hangt'. Van deze boom bepalen we het langste pad, dus de
diepte, door een Breadth-First-Search uit te voeren. De knoop die we hierin als
laatste tegen komen, is de diepste knoop, en het pad hierheen is nu te bepalen
door omhoog te gaan naar de wortel. Noteer dit pad en de lengte van dit pad. Nu
kiezen we het volgende blad, en voeren hetzelfde principe uit. Als we alle
bladeren hebben gehad, kijken we welk pad het langste is. Dit is het langste
pad in de boom. \\
\\
Het algoritme is in de orde $\mathcal{O}(n^2)$. Het zoeken van het langste pad
in de boom is in de orde $\mathcal{O}(n)$. Dit omdat elke knoop langs gegaan
moet worden. Dit moeten we uitvoeren op maximaal $n-1$ knopen, in het geval dat
alle knopen met elkaar zijn verbonden via 1 centraal punt. Daarom voeren we
worst-case $(n-1) * n$ operaties uit, dus $n^2 - n$, en dat is in de orde van
$\mathcal{O}(n^2)$.
\section{Ski's en kinderen}
Greedy algoritme: zoek de combinatie ($l_i$, $h_j$), waarbij $|l_i - h_j|$
minimaal is. Haal $l_i$ en $h_j$ nu uit de verzamelingen, en doe dit opnieuw.\\
\\
Dit algoritme is in de orde van $n^3$, en wel omdat je $n$ paar ski's en $n$
kinderen hebt. Per keer zoek je per kind door de verzameling ski's heen en
noteer je de waarde voor $|l_i - h_j|$, dus je doet $n$ maal $n$ zoekopdrachten
per keer. Vervolgens, nadat je deze $n^2$ zoekopdrachten hebt uitgevoerd, begin
je opnieuw met de verzamelingen van $n-1$ ski's en $n-1$ kinderen. Dit is
wederom in de orde van $n^2$. Dit blijf je herhalen tot er geen kinderen en
ski's meer over zijn. Omdat er $n$ kinderen waren, is dit $n$ maal. We voeren
dus $n$ maal een $n^2$ operatie uit, dus de orde van dit algoritme is
$\mathcal{O}(n^3)$.
\pagebreak
\section{Minimum Spanning Tree}
\begin{enumerate}[(a)]
\item Gegeven graaf $G$ met een cycle en een kant $e$ met maximaal gewicht:
\\
\begin{dot2tex}[dot,options=-tmath --autosize --cache]
graph G {
rankdir=LR;
node [shape="circle"]; v_0 v_1 v_2 v_3 v_4 v_5 v_6 v_7;
v_0 -> v_1;
v_1 -> v_2;
v_1 -> v_3;
v_2 -> v_4;
v_3 -> v_5 [label="e"];
v_4 -> v_6;
v_5 -> v_6;
v_6 -> v_7;
}
\end{dot2tex}
\\
\\
Het bewijs dat er is een Minimum Spanning Tree is voor graaf G, waarin
kant $e$ niet zit, volgt uit het bewijs van het ongerijmde: \\
\\
Stel dat $e$ in een MST zit, dan kunnen er twee subtrees worden gemaakt
door $e$ uit de tree te halen:
\\
\begin{dot2tex}[dot,options=-tmath --autosize --cache]
graph G {
rankdir=LR;
node [shape="circle"]; v_0 v_1 v_2 v_3 v_4 v_5 v_6 v_7;
v_0 -> v_1;
v_1 -> v_2;
v_1 -> v_3;
v_2 -> v_4 [label="f"];
v_4 -> v_6;
v_5 -> v_6;
v_6 -> v_7;
}
\end{dot2tex}
\\
De knopen die verbonden werden met $e$ ($v_3$ en $v_5$) zitten nu elk in
andere subtree. De twee subtrees worden verbonden met elkaar (kant $f$),
doordat $e$ onderdeel was van een cycle. Omdat er een kant $f$ bestaat
die een kleiner gewicht heeft dan $e$ (want $e$ was het maximum), is er
een MST in graaf G die de kant $e$ niet bevat. De MST van kant $f$ heeft
immers een kleiner totaal gewicht dan de MST met kant $e$.
\item Het ``Reverse-delete algorithm''\footnote{Reverse-delete algorithm:
\url{http://en.wikipedia.org/wiki/Reverse-Delete_algorithm}} beschrijft
het bovenstaande principe. Het algoritme beschreven in pseudocode:
\begin{lstlisting}
function ReverseDelete(edges[] E):
sort E in decreasing order
i := 0
for( i := 0; i < size(E); i := i + 1 ):
if temp.v1 is connected to temp.v2:
delete E[i]
return E
\end{lstlisting}
Op wikipedia\footnote{Pseudocode Reverse-delete algorithm:
\url{http://en.wikipedia.org/wiki/Reverse-Delete_algorithm#Pseudocode}}
is de bron van de bovenstaande pseudocode. Echter wordt daar een
delete operatie uitgevoerd voor elke kant en als het niet mogelijk is om
de kant te verwijderen, dan wordt de kant teruggeplaatst. Ondanks dat
deze verwijder operatie in $\mathcal{O}(1)$ zit, is de pseudocode
aangepast zodat de verwijder operatie pas uitgevoerd wordt, als de
knopen van een kant nog wel verbonden zijn.
\item De \emph{running time complexity} van het Reverse-delete algoritme is
in $\mathcal{O}(E \; log E \; (log \; log \; E)^3)$, waar $E$ staat voor
het aantal kanten in de graaf. Dit volgt uit:
\begin{enumerate}[1.]
\item Het sorteren van de kanten op aflopend gewicht is van de orde
$\mathcal{O}(E \; log \; E)$.
\item Het aflopen van de kanten is van de orde $\mathcal{O}(E)$.
\item Kijken of twee knopen zijn verbonden is in
$\mathcal{O}(logV \; (log \; log \; V)^3)$.\footnote{
Near-optimal algorithm to check if vertices are connected:
\url{http://portal.acm.org/citation.cfm?doid=335305.335345}}
Hier is $V$ het aantal knopen in de graaf.
\end{enumerate}
Hieruit volgt dat de running time complexity van het Reverse algoritme
neerkomt op $\mathcal{O}(E \times logV \; (log \; log \; V)^3)$, want
voor elke kant moet gecontroleerd worden of de twee verbonden punten
van de kant nog via een ander pad verbonden zijn.
\end{enumerate}
\section{Eenheidsintervallen}
\begin{enumerate}[(a)]
\item Om aan te tonen dat het greedy algoritme gegeven in de opdracht
niet de optimale indeling geeft, wordt bewezen door het volgende
tegenvoorbeeld: \\
\\
Gegeven de verzameling rationele getallen $A = \{0, \frac{2}{10},
\frac{3}{10}, \frac{11}{10}, \frac{12}{10}, \frac{14}{10} \}$.
Het greedy algoritme zou de eenheidsintervallen $\{ [-1,0],
[\frac{2}{10},\frac{12}{10}], [\frac{14}{10},\frac{24}{10}] \}$
geven. Dat zijn drie intervallen. Het is mogelijk om met twee
intervallen de zelfde getallen te omvatten: $\{ [0,1],
[\frac{11}{10},\frac{21}{10}] \}$. Hierdoor is aangetoond dat er
een optimalere methode is om het probleem op te lossen. De
definitie van een greedy algoritme is dat het de meest optimale
methode moet zijn, daar voldoet de gegeven algoritme niet aan.
\item Pseudocode:
\begin{lstlisting}
quicksort list in ascending order
min := i := first element from the list
V := {}
do:
i := next element from the list
if( i > min + 1 ):
add [min, min+1] to V
min := i := next element from the list
while( end of the list is not reached )
return V
\end{lstlisting}
\\
Bewijs dat het werkt: \\
Als we een willekeurig interval uit V verwijderen, kunnen we geen
combinatie van intervalgrenzen in V bedenken zodat alle elementen uit de
reeks in een interval in V zijn in te delen. Dit werkt alleen met twee
of meer elementen in de lijst.
\item Het quicksort algoritme zit in $\mathcal{O}(n \log{n})$. De
iteratie wordt $n$ maal uitgevoerd, dit valt weg tegen de
tijdscomplexiteit van de quicksort. Het totale algoritme zit dus in
$\mathcal{O}(n \log{n})$.
\end{enumerate}
\section{E\'en na kortste pad}
Zoek alle paden van A naar B door alles uit te proberen, met behulp van
Dijkstra's algoritme\footnote{Reverse-delete algorithm:
\url{http://en.wikipedia.org/wiki/Dijkstra's_algorithm}}, onthoudt alle
paden en sorteer deze op lengte van klein naar groot. Retourneer het op \'e\'en
na korste pad, dat is de tweede in de rij.
\section{Dijkstra's algoritme}
De complexiteit van Dijkstra's algoritme is in de orde van $n^2$. Dit betekent
dat het vinden van het korste pad in de graaf van orde $n^2$ is. Dit komt omdat
je in het ergste geval (de \emph{worst-case}), tussen elke knoop een kant hebt.
In dat geval moet je tussen vanaf elke knoop de afstand tot die knoop optellen
bij de afstand tot al zijn buren. Dat betekent dat je $n$ maal $n$ keer moet
gaan optellen, dus $\mathcal{O}(n^2)$. \\
\\
In het programma voeren we dit $n$ maal uit, omdat we in de graaf van elk punt
de afstand naar alle andere punten berekenen, in plaats van van 1 punt naar
alle andere punten. Dit levert ons dus een functie in de orde van
$\mathcal{O}(n * n^2 = n^3)$ op. \\
\\
De complexiteit van het na\"{i}ve algoritme is voor het berekenen van een enkel
punt naar alle andere punten in de orde van $\mathcal{O}(n!)$, omdat je van
elke knoop die je tegen komt, in de worst case naar alle andere knopen toe kunt
gaan. Dit voeren we ook weer $n$ maal uit, omdat we van elk punt naar elk punt
het korste pad zoeken. Dan krijg je dus een functie in de orde van
$\mathcal{O}(n * n!)$. \\
\\
Dit betekent dat als $n$ groter wordt, het verschil steeds meer toe neemt. Dit
zien we terug in de test resultaten. We hebben het programma de ratio laten
berekenen, dus hoeveel sneller Dijkstra's algoritme is ten opzichte van het
na\"{i}ve algoritme. De resultaten hiervan staan in tabel \ref{tabel1}.
\begin{table}[H]
\begin{tabular}{ c | c }
$n$ & ratio\\
\hline
4 & 1.3\\
5 & 2.0\\
6 & 4.1\\
7 & 9.7\\
8 & 25\\
9 & 78\\
10 & $2.5 * 10^2$\\
11 & $6.0 * 10^2$\\
12 & $3.0 * 10^3$\\
13 & $2.0 * 10^5$
\end{tabular}
\caption{De tabel met daarin de ratio tegenover het aantal punten in de graaf}
\label{tabel1}
\end{table}
Bij deze tabel moet wel de kanttekening worden gemaakt dat bij grotere waarden,
we over minder grafen hebben getest, omdat het anders te veel tijd in beslag
nam. Daardoor waren de testresultaten erg verschillend, en is de weergegeven
ratio een benadering. \\
\\
Hieruit blijkt dus dat zelfs de ratio extreem snel toe neemt. Daaruit blijkt
dat Dijkstra's algoritme veel effici\"{e}nter is.
\end{document}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment