Browse Source

Abschnitt über Rekursion hinzugefügt

Martin Thoma 11 years ago
parent
commit
7e99fea83a

+ 1 - 0
documents/Programmierparadigmen/Arbeitszeit.md

@@ -7,3 +7,4 @@ in dem Erstellen dieses Skripts steckt:
 |01.02.2014 | 14:00 - 14:45 | Thoma | ASCII-Tabelle in C angefangen; Kapitel "Programmiersprachen" hinzugefügt; erste Definitionen
 |01.02.2014 | 14:45 - 15:30 | Thoma | Haskell angefangen
 |01.02.2014 | 11:15 - 11:45 | Thoma | Haskell Class Hierachy
+|01.02.2014 | 16:00 - 17:00 | Thoma | Abschnitt über Rekursion hinzugefügt

+ 4 - 6
documents/Programmierparadigmen/Haskell.tex

@@ -40,12 +40,7 @@ definiert:
 \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/einfaches-beispiel-klammern.hs}
 
 \subsection{if / else}
-Das folgende Beispiel definiert den Binomialkoeffizienten 
-\[\binom{n}{k} := \begin{cases}
-        1                               &\text{falls } k=0 \lor k = n\\
-        \binom{n-1}{k-1}+\binom{n-1}{k} &\text{sonst}
-        \end{cases}\]
-für $n,k \geq 0$:
+Das folgende Beispiel definiert den Binomialkoeffizienten (vgl. \cref{bsp:binomialkoeffizient})
 
 \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/binomialkoeffizient.hs}
 \inputminted[numbersep=5pt, tabsize=4]{bash}{scripts/haskell/compile-binom.sh}
@@ -65,6 +60,9 @@ hat einen Speicherverbrauch von $\mathcal{O}(n)$. Durch einen
 \textbf{Akkumulator}\xindex{Akkumulator} kann dies verhindert werden:
 \inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/fakultaet-akkumulator.hs}
 
+\subsection{Listen}
+\todo[inline]{Cons-Operator, Unendliche Listen}
+
 \section{Beispiele}
 \subsection{Hello World}
 Speichere folgenden Quelltext als \texttt{hello-world.hs}:

BIN
documents/Programmierparadigmen/Programmierparadigmen.pdf


+ 94 - 1
documents/Programmierparadigmen/Programmiertechniken.tex

@@ -1,5 +1,98 @@
 \chapter{Programmiertechniken}
 \section{Rekursion}
-\todo[inline]{Tail-Recursion}
+\index{Rekursion|(}
+
+\begin{definition}[rekursive Funktion]\xindex{Funktion!rekursive}
+    Eine Funktion $f: X \rightarrow X$ heißt rekursiv definiert,
+    wenn in der Definition der Funktion die Funktion selbst wieder
+    steht.
+\end{definition}
+
+\begin{beispiel}[rekursive Funktionen]
+    \begin{bspenum}
+        \item Fibonacci-Funktion:\xindex{Fibonacci-Funktion}
+                \begin{align*}
+                    fib: \mdn_0 &\rightarrow \mdn_0\\
+                    fib(n) &= \begin{cases}
+                            n                   &\text{falls } n \leq 1\\
+                            fib(n-1) + fib(n-2) &\text{sonst}
+                        \end{cases}
+                \end{align*}
+        \item Fakultät:\xindex{Fakultät}
+                \begin{align*}
+                    !: \mdn_0 &\rightarrow \mdn_0\\
+                    n! &= \begin{cases}
+                            1              &\text{falls } n \leq 1\\
+                            n\cdot (n-1)!  &\text{sonst}
+                        \end{cases}
+                \end{align*}
+        \item \label{bsp:binomialkoeffizient} Binomialkoeffizient:\xindex{Binomialkoeffizient}
+                \begin{align*}
+                    \binom{\cdot}{\cdot}: \mdn_0 \times \mdn_0 &\rightarrow \mdn_0\\
+                            \binom{n}{k}    &= \begin{cases}
+                        1                               &\text{falls } k=0 \lor k = n\\
+                        \binom{n-1}{k-1}+\binom{n-1}{k} &\text{sonst}
+                        \end{cases}
+                \end{align*}
+    \end{bspenum}
+\end{beispiel}
+
+Ein Problem von rekursiven Funktionen in Computerprogrammen ist der 
+Speicherbedarf. Für jeden rekursiven Aufruf müssen alle Umgebungsvariablen
+der aufrufenden Funktion (\enquote{stack frame}) gespeichert bleiben,
+bis der rekursive Aufruf beendet ist. Im Fall der Fibonacci-Funktion
+sieht ist der Call-Stack in \cref{fig:fib-callstack} abgebildet.
+
+\begin{figure}[htp]
+    \centering
+    \includegraphics*[width=0.5\linewidth, keepaspectratio]{figures/fib-callstack.png} 
+    \caption{Call-Stack der Fibonacci-Funktion}
+    \label{fig:fib-callstack}
+\end{figure}
+
+\begin{bemerkung}
+    Die Anzahl der rekursiven Aufrufe der Fibonacci-Funktion $f_C$ ist:
+        \[f_C(n) = \begin{cases}
+                        1 &\text{falls } n=0\\
+                    2 \cdot fib(n) - 1 &\text{falls } n \geq 1
+                    \end{cases}\]
+\end{bemerkung}
+\begin{beweis}\leavevmode
+    \begin{itemize}
+        \item Offensichtlich gilt $f_C(0) = 1$
+        \item Offensichtlich gilt $f_C(1) = 1 = 2 \cdot fib(1) - 1$
+        \item Offensichtlich gilt $f_C(2) = 3 = 2 \cdot fib(2) - 1$
+        \item Für $n \geq 3$:
+           \begin{align*}
+                f_C(n) &= 1 + f_C(n-1) + f_C(n-2)\\
+                       &= 1 + (2\cdot fib(n-1)-1) + (2 \cdot fib(n-2)-1)\\
+                       &=2\cdot (fib(n-1) + fib(n-2)) - 1\\
+                       &=2 \cdot fib(n) - 1
+            \end{align*}
+    \end{itemize}
+\end{beweis}
+
+Mit Hilfe der Formel von Moivre-Binet folgt:
+
+\[f_C \in \mathcal{O} \left (\frac{\varphi^n- \psi^n}{\varphi - \psi} \right) \text{ mit } \varphi := \frac{1+ \sqrt{5}}{2} \text{ und }\psi := 1 - \varphi\]
+
+Dabei ist der Speicherbedarf $\mathcal{O}(n)$. Dieser kann durch
+das Benutzen eines Akkumulators signifikant reduziert werden.\todo{TODO}
+
+\begin{definition}[linear rekursive Funktion]\xindex{Funktion!linear rekursive}
+    Eine Funktion heißt linear rekursiv, wenn in jedem Definitionszweig
+    der Funktion höchstens ein rekursiver Aufruf vorkommt.
+\end{definition}
+
+\begin{definition}[endrekursive Funktion]\xindex{Funktion!endrekursive}\xindex{tail recursive}
+    Eine Funktion heißt endrekursiv, wenn in jedem Definitionszweig
+    der Rekursive aufruf am Ende des Ausdrucks steht. Der rekursive
+    Aufruf darf also insbesondere nicht in einen anderen Ausdruck
+    eingebettet sein.
+\end{definition}
+
+\todo[inline]{Beispiele für linear rekusrive, endrekursive Funktionen (alle Kombinationen+gegenbeispiele}
+
+\index{Rekursion|(}
 \section{Backtracking}
 

+ 2 - 1
documents/Programmierparadigmen/Vorwort.tex

@@ -24,7 +24,8 @@ TODO
 \section*{Erforderliche Vorkenntnisse}
 Grundlegende Kenntnisse vom Programmieren, insbesondere mit Java,
 wie sie am KIT in \enquote{Programmieren} vermittelt werden, werden
-vorausgesetzt.
+vorausgesetzt. Außerdem könnte ein grundlegendes Verständnis für 
+das O-Kalkül aus \enquote{Grundbegriffe der Informatik} hilfreich sein.
 
 Die Unifikation wird wohl auch in \enquote{Formale Systeme}
 erklärt; das könnte also hier von Vorteil sein.

BIN
documents/Programmierparadigmen/figures/fib-callstack.png


+ 14 - 0
documents/Programmierparadigmen/scripts/programmiertechniken/fib-callstack

@@ -0,0 +1,14 @@
+fib(3)
+├ call(fib(2))
+│ ├─ call(fib(1))
+│ │  └─ return 1
+│ ├─ call(fib(0))
+│ │  └─ return 1
+│ └ return fib(1)+fib(0)=1+1
+├ call(fib(1))
+│ ├─ call(fib(1))
+│ │  └─ return 1
+│ ├─ call(fib(0))
+│ │  └─ return 1
+│ └ return fib(1)+fib(0)=1+1
+└ return fib(2)+fib(1)=2+2