\chapter{Programmiertechniken} \section{Rekursion} \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}