diff --git a/202_quicksort.tex b/202_quicksort.tex index f961c8bc22c1bdfffb3c6846bad210a1fd451ac6..a13658294f396577cc828b6381f181040fbd3c8c 100644 --- a/202_quicksort.tex +++ b/202_quicksort.tex @@ -17,7 +17,7 @@ Vorerst nehmen wir als Pivotelement einfach das letzte Arrayelement. $p \leftarrow \mathcal{A}[n-1]$ \; $i \leftarrow -1$ \; \For{$j \leftarrow 0$ \KwTo $n-2$}{ - \If{$\mathcal{A}[j] \leq p$}{ + \If{$\mathcal{A}[j] ≤ p$}{ $\texttt{swap}(\mathcal{A}[i+1], \mathcal{A}[j])$ \; $i \leftarrow i + 1$ \; } @@ -49,14 +49,14 @@ vertauscht([5]). Dabei wird eine Sortierung innerhalb einer Partition zwar viell Korrektheit des Algorithmus nicht relevant. Man kann also feststellen: Das Array ist stets in vier (möglicherweise leere) Teilbereiche unterteilt. Der -Speicherort von $p$ (Anfangs $n-1$), die noch nicht verglichenen Elemente $j < \text{ index } \leq n-2$, die kleiner als -$p$ eingestuften Elemente $0 \leq \text{ index } \leq i$ und die als größer als $p$ eingestuften Elemente $i < \text{ index } -\leq j$. +Speicherort von $p$ (Anfangs $n-1$), die noch nicht verglichenen Elemente $j < \text{ index } ≤ n-2$, die kleiner als +$p$ eingestuften Elemente $0 ≤ \text{ index } ≤ i$ und die als größer als $p$ eingestuften Elemente $i < \text{ index } +≤ j$. Da ein Schleifendurchlauf die Schleifeninvarianten (also der Programmzustand exakt vor dem Inkrementieren von $j$) \begin{itemize} - \item Elemente mit Index $x$, wobei $x \leq i$, sind kleiner oder gleich $p$, - \item Elemente mit Index $x$, wobei $i < x$ und $x \leq j$, sind größer $p$, + \item Elemente mit Index $x$, wobei $x ≤ i$, sind kleiner oder gleich $p$, + \item Elemente mit Index $x$, wobei $i < x$ und $x ≤ j$, sind größer $p$, \item Elemente mit Index $x$, wobei $j < x$, sind noch nicht überprüft, \end{itemize} erhält, aber die Anzahl der nicht überprüften Elemente pro Schleifendurchlauf um eins schrumpft, terminiert der @@ -102,47 +102,20 @@ Algorithmus $\texttt{quicksort}$ terminiert als spätestens nach $n$ rekursiven \subsubsection{Laufzeit:} Die allgemeine Rekusionsgleichung für $T_{\text{qs}} = T_{\texttt{quicksort}}$ lautet \[ - T_{\text{qs}}(n) = T_{\text{qs}}(m) + T_{\text{qs}}(n-m) + T_p(n) + f(n), \text{ wobei } f \in \mathcal{O}(1). + T_{\text{qs}}(n) = T_{\text{qs}}(m) + T_{\text{qs}}(n-m-1) + T_p(n) + f(n), \text{ wobei } f \in \mathcal{O}(1). \] Dabei verschwindet $f$ völlig unter $T_p \in \mathcal{O}(n)$. -Anders als bei $\texttt{partition}$ gibt es hier aber Unterschiede im best/worst case. +Der Parameter $m \in \{0,\dots,n-1\}$ beschreibt dabei die Position des Pivotelements. Die Laufzeit des Algorithmus +hängt nun stark von $m$ ab: \paragraph{Best case} -Im besten Fall treffen wir mit dem Pivotelement $p$ genau den Median von $\mathcal{A}$. Dann haben wir durch $m = -\frac{n}{2}$ pro Rekursionsschritt eine balancierte Aufteilung des Rekursionsbaums. Dann haben wir nach dem Hauptsatz -der Laufzeitfunktionen also $a=2, b=2$, $f \in \mathcal{O}(n)$ und damit eine best-case-Laufzeit von -$\mathcal{O}(n \log(n))$. Wir rechnen aber noch einmal per Hand nach: Einerseits um zu sehen wie das geschieht, -andererseits, da der worst-case nicht mit dem Satz berechnet werden kann. -Sei der Einfachheit halber $n = 2^{k'}$ für ein $k' \in \mathbb{N}$. -\begin{align*} - T_{\texttt{qs}}^{\text{best}}(n) -&= T_{\text{qs}}^{\text{best}} \left(\frac{n}{2} \right) + T_{\text{qs}}^{\text{best}} \left(\frac{n}{2} \right) + T_{\text{p}}(n) \\ - &= 2T_{\text{qs}}^{\text{best}} \left(\frac{n}{2} \right) + T_{\text{p}}(n) \\ - &= 4T_{\text{qs}}^{\text{best}}\left(\frac{n}{4} \right) + 2T_{\text{p}} \left(\frac{n}{2} \right) + T_{\text{p}}(n) \\ - &= 8T_{\text{qs}}^{\text{best}}\left(\frac{n}{8} \right) + 4T_{\text{p}} \left(\frac{n}{4} \right) + 2T_{\text{p}}\left(\frac{n}{2} \right) + T_{\text{p}}(n) \\ - &= \ldots \\ - &= nT_{\text{qs}}^{\text{best}}(1) + \sum_{k=0}^{\log_2(n)} 2^k T_{\text{p}}\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 -$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 -multiplizieren die Ungleichung mit $2^k$, erhalten damit $2^k c_1 \floor*{\frac{n}{2^k}} \leq 2^k -T_{\text{p}}(\floor*{\frac{n}{2^k}}) \leq 2^k c_2 \ceil*{\frac{n}{2^k}}$, was wieder für alle $n \geq n_0 2^k$ gültig ist. -Da $2^k \floor*{\frac{n}{2^k}} \leq n$ und $2^k \ceil*{\frac{n}{2^k}} \geq n$ gilt, können wir die Ungleichung zu -$c_1 n \leq 2^k T_{\text{p}}(\floor*{\frac{n}{2^k}}) \leq c_2 n$ vereinfachen. Damit gilt mit $c_1, c_2$ und $n_0' = 2^k -n_0$: +Im besten Fall treffen wir mit dem Pivotelement $p$ genau den Median von $\mathcal{A}$. Dann haben wir durch $m \simeq +\frac{n}{2}$ pro Rekursionsschritt eine balancierte Aufteilung des Rekursionsbaums, die Rekursionsgleichung wird zu \[ - n \mapsto 2^k T_{\text{p}}\left(\floor*{\frac{n}{2^k}}\right) \in \Theta(n). + T_{\text{qs}}(n) = 2T_{\text{qs}}\left( \frac{n}{2} \right) + T_p(n). \] - -Damit haben wir -\begin{align*} - T_{\text{qs}}^{\text{best}}(n) - &= nT_{\text{qs}}^{\text{best}}(1) + \sum_{k=0}^{\log_2(n)} 2^k T_{\text{p}}\left(\frac{n}{2^k}\right) \\ - &\in \mathcal{O}(n) + \log_2(n) \mathcal{O}(n) \\ - &= \mathcal{O}(n \log(n)). \\ -\end{align*} +Nach dem Hauptsatz der Laufzeitfunktionen ist mit $a=2, b=2$, $f \in \mathcal{O}(n)$ die Laufzeit im best-case damit in +$\mathcal{O}(n \log n)$. \paragraph{Worst case} Im schlechtesten Fall teilen wir das Array sehr ungünstig: Das Pivotelement ist immer das Maximum oder das Minimum, @@ -162,11 +135,11 @@ Aufwandsklasse der Summenterm liegen könnte. Wir fomalisieren jetzt also die Id oben und $c_1 n$ von unten abschätzen können, sobald die $n$ groß genug werden. Betrachten wir also $\sum_{i=1}^k T_\text{p}(i)$. Mit $T_\text{p} \in Θ(n)$ wissen wir, dass $n_0,c_1,c_2$ existieren, sodass -$c_1 n' \leq T_\text{p}(n') \leq c_2 n'$ für alle $n' > n_0$. Dieses $n_0$ ist aber fix, d.h. für ein groß genuges $k$ (und +$c_1 n' ≤ T_\text{p}(n') ≤ c_2 n'$ für alle $n' > n_0$. Dieses $n_0$ ist aber fix, d.h. für ein groß genuges $k$ (und $k$ soll später gegen unendlich gehen) betrachten wir also $\sum_{i=n_0}^k T_p(i)$. Hier gilt: \[ \underbrace{c_1 \sum_{i=n_0}^k i}_{\text{$\in Ω(k^2)$}} - \leq \sum_{i=n_0}^k T_p(i) \leq + ≤ \sum_{i=n_0}^k T_p(i) ≤ \underbrace{c_2 \sum_{i=n_0}^k i}_{\text{$\in \mathcal{O}(k^2)$}} \] wie uns die Eulersche Summenformel verrät. @@ -200,23 +173,13 @@ Definieren wir zuerst die randomisierte Partition: \KwOut{The partition of $\mathcal{A}$ wrt. the randomized pivot $p$ and its post-partitioning-index $k$} $i \leftarrow \texttt{random\_uniform}(n-1)$ \; $\texttt{swap}(\mathcal{A}[0], \mathcal{A}[i])$ \; - $p \leftarrow \mathcal{A}[n-1]$ \; - $i \leftarrow -1$ \; - \For{$j \leftarrow 0$ \KwTo $n-2$}{ - \If{$\mathcal{A}[j] \leq p$}{ - $\texttt{swap}(\mathcal{A}[i+1], \mathcal{A}[j])$ \; - $i \leftarrow i + 1$ \; - } - } - $\texttt{swap}(\mathcal{A}[i+1], \mathcal{A}[n-1])$ \; - $i \leftarrow i + 1$ \; - \KwRet $(\mathcal{A}, i)$ + \KwRet $\texttt{partition}(\mathcal{A})$ \end{algorithm} -Neu ist also nur das Vertauschen des ersten Elements von $\mathcal{A}$ mit dem durch $\texttt{random\_uniform}$ -zufällig (gleichverteilt) erwählten anderen Elements des Arrays, der Rest ist wie bei $\texttt{partition}$. +Neu ist also nur das Vertauschen des ersten Elements von $\mathcal{A}$ mit einm durch $\texttt{random\_uniform}$ +zufällig (gleichverteilt) gewählten Elements des Arrays, der Rest ist wie bei $\texttt{partition}$. Dabei hat $\texttt{random\_partition}$ die gleichbleibende Laufzeit $T_{\text{rp}} \in Θ(n)$ mit $n = -\texttt{len}(\mathcal{A})$. +\texttt{len}(\mathcal{A})$. Es ist auch nicht schwer einzusehen, das hier $n_0 = 2$ gewählt werden kann. Der Algorithmus $\texttt{randomized\_quicksort}$ hat dabei genau den gleichen Pseudocode wie $\texttt{quicksort}$, nur dass $\texttt{randomized\_partition}$ statt $\texttt{partition}$ in Zeile $[2]$ gerufen wird. Die Laufzeit von @@ -226,63 +189,70 @@ dass $T_{\text{rqs}} \in \mathcal{O}(n \log n)$ liegt. \subsubsection{Herleitung der average case Laufzeit von \texttt{randomized\_quicksort}:} Für die erwartete Laufzeit gilt \begin{displaymath} - T_{\text{rqs}}(n) = \sum_{k=1}^{n-1} P(k) \left( T_{\text{rqs}}(k) + T_{\text{rqs}}(n-k) + T_{\text{rp}}(n) \right), + T_{\text{rqs}}(n) = \sum_{k=1}^{n-1} P(k) \left( T_{\text{rqs}}(k) + T_{\text{rqs}}(n-k-1) + T_{\text{rp}}(n) \right), \end{displaymath} wobei $P(k)$ die Wahrscheinlichkeit ist, dass $\texttt{randomized\_partition}(\mathcal{A})$ den Index $k$ liefert. -Wir nehmen mit $k \in \{1,\dots, n-1\}$ eine Gleichverteilung $P(k) = \frac{1}{n-1}$ an. +Wir nehmen mit $k \in \{0,\dots, n-1\}$ eine Gleichverteilung $P(k) = \frac{1}{n}$ an. % TODO: Mehr Details \begin{align*} - T_{\text{rqs}} & = \sum_{k=1}^{n-1} \left( \frac{1}{n-1} \left( T_{\text{rqs}}(k) + T_{\text{rqs}}(n-k) + T_{\text{rp}}(n) \right) \right) \\ - & = \frac{1}{n-1} \sum_{k=1}^{n-1} \left( T_{\text{rqs}}(k) + T_{\text{rqs}}(n-k) \right) + \frac{n-1}{n-1} T_{\text{rp}}(n) \\ - & = \frac{2}{n-1} \sum_{k=1}^{n-1} T_{\text{rqs}}(k) + T_{\text{rp}}(n). + T_{\text{rqs}} + &= \sum_{k=0}^{n-1} \left( \frac{1}{n} \left( T_{\text{rqs}}(k) + T_{\text{rqs}}(n-k-1) + T_{\text{rp}}(n) \right) \right) \\ + &= \frac{n}{n} T_{\text{rp}}(n) + \frac{1}{n} \sum_{k=0}^{n-1} T_{\text{rqs}}(k) + T_{\text{rqs}}(n-k-1) \\ + &= T_\text{rp}(n) + \frac{2}{n} \sum_{k=0}^{n-1} T_{\text{rqs}}(k) \\ + &≤ T_\text{rp}(n) + d + \frac{2}{n} \sum_{k=2}^{n-1} T_{\text{rqs}}(k). \end{align*} -Wir zeigen nun vermöge einer vollständiger Induktion, dass mit $c = \max\{T_{\text{rqs}}(1) + T_{\text{rp}}(2), 8 c_{\text{rp}}\}$ gilt: +Wobei $d := 2T_\text{rqs}(0) + 2T_\text{rqs}(1) ≤ \frac{2}{n} (T_\text{rqs}(0) + T_\text{rqs}(1))$ ist für $n ≥ 1$. + +Wir zeigen nun vermöge einer vollständigen Induktion, dass mit $c = \max\{T_{\text{rq}}(2) + d, 8(d+c_{\text{rp}})\}$ gilt: \[ - T_{\text{rqs}}(n) \leq c \cdot n \log_2 n \text{ für alle } n > 2. + T_{\text{rqs}}(n) ≤ c \cdot n \log_2 n \text{ für alle } n ≥ 2. \] -Hierbei ist $c_{\text{rp}}$ die Konstante mit welcher $T_\text{rp}(n) \leq c_{\text{rp}} n$ gilt. Streng genommen +Hierbei ist $c_{\text{rp}}$ die Konstante mit welcher $T_\text{rp}(n) ≤ c_{\text{rp}} n$ gilt. Streng genommen gilt das erst ab irgendeinem $n_0$, aber den Aspekt vernachlässigen wir hier, um die Beweisstruktur etwas übersichtlicher zu gestalten. Es ist bei \texttt{randomized\_partition} aber auch leicht ersichtlich, dass $n_0 = 2$ gewählt werden kann. Induktionsanfang $n=2$: \begin{align*} - T_{\text{rqs}}(n) - &\leq 2 T_{\text{rqs}}(1) + T_{\text{rp}}(n) \\ - &\leq c n \log_2 2 + T_{\text{rqs}}(2) + &≤ d + T_{\text{rp}}(2) \\ + &≤ c n \log_2 2 \end{align*} Für $c = \max(T_{\text{rqs}}(1) + T_{\text{rp}}(2), 8 c_{\text{rp}}) \geq T_{\text{rqs}}(1) + T_{\text{rp}}(2)$ ist die Abschätzung definitiv erfüllt. Induktionsschritt $n-1 \mapsto n$: Zu zeigen ist, dass mit mit der Aussage wahr für alle $n \in \{2, \dots, n-1\}$ gilt: -$T_{\text{rqs}}(n) \leq c n \log_2 n$. Wir rechnen: -\begin{align*} - T_{\text{rqs}}(n) &= \frac{2}{n-1} \sum_{k=1}^{n-1} T_{\text{rqs}}(k) + T_{\text{rp}}(n) \\ - &\overset{{\scriptscriptstyle \text{IV}}}{\leq} \frac{2}{n-1} \sum_{k=1}^{n-1} c \cdot k \log_2 k + T_{\text{rp}}(n) \\ - &= \frac{2c}{n-1} \sum_{k=1}^{n-1} k \log_2 k + T_{\text{rp}}(n) \\ - &\overset{\scriptscriptstyle (*)}{\leq} \frac{2c}{n-1} \left( (\log_2 n) \left( \frac{n(n-1)}{2} \right) - \frac{\frac{n}{2}(\frac{n}{2}-1)}{2} \right) + T_{\text{rp}}(n) \\ - &= c \cdot n \log_2 n - c \left( \frac{n}{4} - \frac{1}{2} \right) + \underbrace{T_{\text{rp}}(n)}_{\leq c_{\text{rp}} \cdot n} \\ - &\leq c \cdot n \log_2 n - \left( \frac{2c \frac{n}{2} (\frac{n}{2} - 1)}{(n-1)2} \right) + c_{\text{rp}} \cdot n \\ - &\leq c \cdot n \log_2 n - c \cdot \left( \frac{n}{4} \frac{(n-2)}{(n-1)} \right) + c_{\text{rp}} \cdot n \\ - &\leq c \cdot n \log_2 n. -\end{align*} -Damit der letzte Schritt geht, muss +$T_{\text{rqs}}(n) ≤ c n \log_2 n$. Wir rechnen: \begin{align*} - c &\leq \frac{c_{\text{rp}}}{\frac{n}{4} \frac{n-2}{n-1}} \\ - &= 4 c_{\text{rp}} \underbrace{\frac{(n-1)}{(n-2)}}_{\text{$\leq 2$ für $n > 2$}} \\ - &\leq 8c_{\text{rp}} + T_{\text{rqs}}(n) + &≤ T_{\text{rp}}(n) + d + \frac{2}{n} \sum_{k=2}^{n-1} T_{\text{rqs}}(k) \\ + &\overset{{\scriptscriptstyle \text{IV}}}{≤} T_{\text{rp}}(n) + d \frac{2c}{n} \sum_{k=2}^{n-1} \cdot k \log_2 k \\ + &\overset{\scriptscriptstyle (*)}{≤} T_{\text{rp}}(n) + d + \frac{2c}{n} \left( (\log_2 n) \left( \frac{n(n-1)}{2} \right) - \frac{\frac{n}{2}(\frac{n}{2}-1)}{2} \right) \\ + &= c(n-1) \log_2 n - c \left(\frac{n}{4} - \frac{1}{2} \right) + T_{\text{rp}}(n) + d \\ + &\overset{\scriptscriptstyle (**)}{≤} c \cdot n \log_2 n. \end{align*} -sein. Durch unsere Wahl von $c = \max\{T_{\text{rqs}}(1) + T_{\text{rp}}(2), 8 c_{\text{rp}}\}$ ist das der Fall. +Betrachten wir zuerst $(**)$ genauer: Das gilt genau dann, wenn +\[ + T_\text{rp}(n) + d ≤ c \left( \frac{n}{4} - \frac{1}{2} \right) + \log_2 n. +\] +Mit $n ≥ 2$ ist $\log_2 n > \frac{1}{2}$ und $T_\text{rp}(n)$ lässt sich nach Annahme von oben durch $c_\text{rp} n$ +abschätzen. +Damit vereinfacht sich die Ungleichung auf +\[ + 4(c_\text{rp} n + d) ≤ c n, +\] +was mit obigen $c$ ab $n ≥ 2$ erfüllt ist. Jetzt gilt es nurnoch, die in Schritt $(*)$ getroffene Abschätzung der Summe $\sum_{k=1}^{n-1} k \log_2 k$ zu beweisen: \begin{align*} - \sum_{k=1}^{n-1} k \log_2 k & = \sum_{k=1}^{\ceil*{\frac{n}{2}}-1} k \underbrace{\log_2 k}_{\leq \log_2 \frac{n}{2}} + - \sum_{k=\ceil*{\frac{n}{2}}}^{n-1} k \underbrace{\log_2 k}_{\leq \log_2 n} \\ - &\leq \sum_{k=1}^{\ceil*{\frac{n}{2}}-1} k (\log_2 n - 1) + \sum_{k=\ceil*{\frac{n}{2}}}^{n-1} k \log_2 n \\ + \sum_{k=2}^{n-2} k \log_2 k + &≤ \sum_{k=1}^{n-1} k \log_2 k \\ + &= \sum_{k=1}^{\ceil*{\frac{n}{2}}-1} k \underbrace{\log_2 k}_{≤ \log_2 \frac{n}{2}} + \sum_{k=\ceil*{\frac{n}{2}}}^{n-1} k \underbrace{\log_2 k}_{≤ \log_2 n} \\ + &≤ \sum_{k=1}^{\ceil*{\frac{n}{2}}-1} k (\log_2 n - 1) + \sum_{k=\ceil*{\frac{n}{2}}}^{n-1} k \log_2 n \\ &= \log_2 n \sum_{k=1}^{\ceil*{\frac{n}{2}}-1} k - \sum_{k=1}^{\ceil*{\frac{n}{2}}-1} k + \log_2 n \sum_{k = \ceil*{\frac{n}{2}}}^{n-1} k \\ &= \log_2 n \underbrace{\sum_{k=1}^{n-1} k}_{= \frac{n(n-1)}{2}} - \underbrace{\sum_{k=1}^{\ceil*{\frac{n}{2}}-1} k}_{\geq \frac{\frac{n}{2}(\frac{n}{2}-1)}{2}} \\ - &\leq (\log_2 n) \left( \frac{n(n-1)}{2} \right) - \frac{\frac{n}{2}(\frac{n}{2}-1)}{2} + &≤ (\log_2 n) \left( \frac{n(n-1)}{2} \right) - \frac{\frac{n}{2}(\frac{n}{2}-1)}{2} \end{align*}