Skip to content
Snippets Groups Projects
Commit 2f36051e authored by Florian Unger's avatar Florian Unger
Browse files

kapitel 1.4 semi-finalized

parent 6e011a77
Branches restructuring
No related tags found
No related merge requests found
......@@ -16,8 +16,7 @@ Laufzeitfunktionen finden und ihre Komplexitätsklasse bestimmen.
Allerdings kann es auch vorkommen, dass eine (naive) rekursive Lösung deutlich langsamer läuft und mehr Speicher
benötigt.
\subsection{Beispiele}
\subsubsection{Die Fakultätsfunktion $n!$}
\subsection{Die Fakultätsfunktion $n!$}
Wir berechnen $\cdot!: \mathbb{N} \rightarrow \mathbb{N}$.
\begin{minipage}[t]{0.4\textwidth}
......@@ -87,7 +86,7 @@ sodass bei ihrem Einsatz Speicherverbrauch/Geschwindigkeit eigentlich kein aussc
Momentan (Stand 2020) führen beispielsweise Haskell und Scala solche Umwandlungen durch, während beispielsweise C,
Java, Python mit call stacks arbeiten.
\subsubsection{Die Fibonacci-Zahlen $\text{fib}(n)$}
\subsection{Die Fibonacci-Zahlen $\text{fib}(n)$}
Wir berechnen die Fibonacci-Zahlen durch die Funktion $\text{fib}: \mathbb{N} \rightarrow \mathbb{N}$, welche wie folgt definiert ist:
$\text{fib}(n) := \text{fib}(n-1) + \text{fib}(n-2)$.
......@@ -132,7 +131,7 @@ zudem $\mathcal{O}(2^n)$ (Breitensuche) oder auch ``nur'' $\mathcal{O}(n)$ (Tief
Auch funktionale Sprachen haben hier keine wirklich Hebel, die Laufzeit zu verbessern. Diese naive Implementierung ist
somit leider für größere $n$ unbrauchbar. Selbst eine moderne CPU berechnet $\texttt{fibrec}(1000)$ nichtmehr in unserer Lebenszeit.
\subsubsection{Die Türme von Hanoi}
\subsubsection{Die Türme von Hanoi*}
Ein weiteres klassisches Problem sind die Türme von Hanoi. Wir haben folgende Spielregeln:
\begin{itemize}
\item $3$ Stäbe $A, B$ und $C$.
......@@ -142,9 +141,9 @@ Ein weiteres klassisches Problem sind die Türme von Hanoi. Wir haben folgende S
\item Es darf nie eine größere auf einer kleineren Scheibe liegen.
\end{itemize}
TODO: Kommt das überhaupt dran?
TODO: Wird noch ausgeführt. Oder eine Übungsaufgabe.
\subsubsection{Binärsuche}
\subsection{Binärsuche}
Ein sehr praxisnahes Beispiel für Rekursion ist die Binärsuche auf sortierten Arrays. Wir suchen in einem sortierten
Array, welches Objekte enthält, die nach dem Schlüssel $k$ sortiert sind, das Objekt mit dem Schlüssel $k_0$.
Die Funktion $\texttt{key}(x)$ verrät uns hierbei den Schlüssel eines Objekts $x$. Wir nehmen dabei an, dass das
......@@ -156,10 +155,10 @@ gesuchte Objekt existiert.
\KwOut{Das Objekt mit dem Schlüssel $k_0$}
\caption{\texttt{binsearchrec}}
$m \leftarrow \floor{\frac{\texttt{len}(\mathcal{A})}{2}}$ \;
\uIf{$\texttt{key}(\mathcal{A}[m]) > k_0$}{
\uIf{$\emph{\texttt{key}}(\mathcal{A}[m]) > k_0$}{
return $\texttt{binsearchrec}(\mathcal{A}[0,\dots,m])$ \;
}
\ElseIf{$\texttt{key}(\mathcal{A}[m]) < k_0$}{
\ElseIf{$\emph{\texttt{key}}(\mathcal{A}[m]) < k_0$}{
return $\texttt{binsearchrec}(\mathcal{A}[m+1, \dots, \texttt{len}(\mathcal{A})])$ \;
}
\Else{
......@@ -171,10 +170,10 @@ Machen wir zuerst eine Aufwandsanalyse per Hand, bevor wir im nächsten Kapitel
Die Laufzeit von \texttt{binsearchrec} hat Unterschiede im best-case oder worst-case Szenario. Best-case wäre, wenn wir
schon beim ersten Aufruf zufällig unser gesuchtes Element finden (Aufwand $f \in \mathcal{O}(1)$).
Im pessimistischen Fall teilen wir unser Array so lange, bis nur noch ein Element übrig bleibt (nach Annahme ist es dann
das gesuchte). Würden wir auch die Suche nach nicht-enthaltenen Schlüsseln erlauben, wären wir auch automatisch immer im
worst-case. Betrachten wir hier als $T(n) = T_{\text{worst}}^{\texttt{binsearchrec}}$. Ein Durchlauf von
$\texttt{binsearchrec}$ hat dabei Aufwand $f \in \mathcal{O}(1)$.
Im pessimistischen Fall teilen wir unser Array so lange, bis nur noch ein Element, das Gesuchte, übrig bleibt.
Würden wir auch die Suche nach nicht-enthaltenen Schlüsseln erlauben, wären wir auch immer im
worst-case. Betrachten wir hier also $T(n) = T_{\text{worst}}^{\texttt{binsearchrec}}$. Ein Durchlauf von
$\texttt{binsearchrec}$ hat dabei Aufwand $f \in \mathcal{O}(1)$ sowie den Aufwand rekursiver Aufrufe.
\begin{align*}
T(n) &= f(n) + T(\floor{\frac{n}{2}}) \\
......@@ -184,7 +183,7 @@ $\texttt{binsearchrec}$ hat dabei Aufwand $f \in \mathcal{O}(1)$.
&\leq \log_2(n) f(n) \in \mathcal{O}(\ln(n))
\end{align*}
Der asymptotische Aufwand liegt in $T^{\texttt{binsearchrec}} \in \mathcal{O}(\log{n})$, siehe Hauptsatz der
Der asymptotische Aufwand liegt damit in $T_{\text{worst}}^{\texttt{binsearchrec}} \in \mathcal{O}(\log{n})$, siehe Hauptsatz der
Laufzeitfunktionen. Damit ist überhaupt erst die Motivation für Sortieralgorithmen gegeben: Suchen in sortierten Arrays
geht verdammt schnell.
......@@ -235,5 +234,9 @@ Greift keiner der drei Fälle, so muss die Laufzeit anders bestimmt werden. Insb
$\texttt{fibrec}$ lassen sich nicht mit dem Hauptsatz der Laufzeitfunktionen behandeln, da ihre Teilprobleme kein
Bruchteil $\frac{n}{b}$ des Hauptproblems sind.
Wir untersuchen nun als Beispiel die asymptotische Laufzeit von
Wir untersuchen nun als Beispiel die asymptotische Laufzeit von $\texttt{binsearchrec}$ noch einmal. Wir haben pro
Rekursionsschritt ein ($a=1)$ halb so großes Unterproblem ($b=2$), damit also $\log_b a = 0$. Die konstante Funktion
$f$ liegt für ein $ε \in \mathbb{R}^+$ gerade nichtmehr in $\mathcal{O}(n^{log_b(a) - ε})$, aber definitv in
$\mathcal{O}(n^{\log_b a}) = \mathcal{O}(n^0)$, also haben wir den zweiten Fall und unser Aufwand liegt in $Θ(n^{\log_b
a} \log n) = Θ(\log n)$.
\section{Quicksort}
Quicksort ist einer der meist verwendeten Sortieralgorithmen. Die Kernidee ist wieder das divide-et-impera-Prinzip.
Der Kern des Algorithmus ist die Zerlegung des Arrays, die sogenannte Partition.
%\begin{center}
%\includegraphics[bb=0cm 0bp 1059bp 324bp,scale=0.2]{bilder/quick}
%\par\end{center}
Quicksort ist einer der meist verwendeten Sortieralgorithmen. Die Idee ist wieder das divide-et-impera-Prinzipr,
der Kern des Algorithmus ist die Zerlegung des Arrays, die sogenannte Partition.
\subsection{Partition}
Für eine Partition eines Arrays $\mathcal{A}$ wählen wir zuerst ein Pivotelement $p$. Nun sortieren wir das Array so,
......@@ -76,7 +72,7 @@ Sei der Einfachheit halber $n = 2^{k'}$ für ein $k' \in \mathbb{N}$.
&= \ldots \\
&= nT_{\text{qs}}^{\text{best}}(1) + \sum_{k=0}^{\log_2(n)} 2^k T_{\text{p}}^{\text{best}}\left(\frac{n}{2^k}\right) \\
\end{align*}
Wir sehen nun in einer Nebenrechnung, dass $n \mapsto 2^k T_{\text{p}(\frac{n}{2^k})} \in \Theta(n)$ für alle
Wir sehen nun in einer Nebenrechnung, dass $n \mapsto 2^k T_{\text{p}}(\frac{n}{2^k}) \in \Theta(n)$ für alle
$k \in \mathbb{N}$.
Sei also $k$ fixiert. Dann gilt $c_1 \floor{\frac{n}{2^k}} \leq T_{\text{p}}(\floor{\frac{n}{2^k}}) \leq c_2
\ceil{\frac{n}{2^k}}$ für alle $n \geq n_0 2^k$, wobei $n_0$ durch $T_{\text{p}} \in \Theta(n)$ gegeben ist. Wir
......
......@@ -2,6 +2,8 @@
\usepackage[ngerman]{babel}
%encoding
\usepackage[utf8]{inputenc}
\usepackage{alphabeta}
%math symbols
\usepackage{amsmath}
......@@ -56,6 +58,6 @@
\tableofcontents
\input{100_Grundlagen}
%\input{200_Sortieralgorithmen}
\input{200_Sortieralgorithmen}
%\input{300_Datenstrukturen}
\end{document}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment