123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- %!TEX root = Programmierparadigmen.tex
- \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*}
- Erzeugt die Zahlen $0, 1, 1, 2, 3, 5, 8, 13, \dots$
- \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 lokalen Variablen
- 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}
- Auf Englisch heißen endrekursive Funktionen \textit{tail recursive}.
- \begin{beispiel}[Linear- und endrekursive Funktionen]
- \begin{bspenum}
- \item \texttt{fak n = if (n==0) then 1 else (n * fak (n-1))}\\
- ist eine linear rekursive Funkion, aber nicht endrekursiv,
- da nach der Rückgabe von \texttt{fak (n-1)} noch die Multiplikation
- ausgewertet werden muss.
- \item \texttt{fakAcc n acc = if (n==0) then acc else fakAcc (n-1) (n*acc)}\\
- ist eine endrekursive Funktion.
- \item \texttt{fib n = n <= 1 ? n : fib(n-1) + fib (n-2)}\\
- ist weder linear- noch endrekursiv.
- \end{bspenum}
- \end{beispiel}
- Wenn eine rekursive Funktion nicht terminiert oder wenn
- \index{Rekursion|)}
- \section{Backtracking}
- \index{Backtracking|(}
- Unter \textit{Backtracking} versteht man eine Programmiertechnik, die
- (eventuell implizit) auf einem Suchbaum arbeitet und mittels Tiefensuche versucht
- eine Lösung zu finden.
- \begin{beispiel}[Backtracking]
- Probleme, bei deren (vollständigen) Lösung Backtracking verwendet wird, sind:
- \begin{bspenum}
- \item Damenproblem
- \item Springerproblem
- \item Rucksackproblem
- \end{bspenum}
- \end{beispiel}
- \index{Backtracking|)}
- \section{Funktionen höherer Ordnung}
- Funktionen höherer Ordnung sind Funktionen, die auf Funktionen arbeiten.
- Bekannte Beispiele sind:
- \begin{itemize}
- \item \texttt{map(function, list)}\xindex{map}\\
- \texttt{map} wendet \texttt{function} auf jedes einzelne
- Element aus \texttt{list} an.
- \item \texttt{filter(function, list)}\xindex{filter}\\
- \texttt{filter} gibt eine Liste aus Elementen zurück, für
- die \texttt{function} mit \texttt{true} evaluiert.
- \item \texttt{reduce(function, list)}\xindex{reduce}\\
- \texttt{function} ist für zwei Elemente aus \texttt{list}
- definiert und gibt ein Element des gleichen Typs zurück.
- Nun steckt \texttt{reduce} zuerst zwei Elemente aus \texttt{list}
- in \texttt{function}, merkt sich dann das Ergebnis und nimmt
- so lange weitere Elemente aus \texttt{list}, bis jedes
- Element genommen wurde.\\
- Bei \texttt{reduce} ist die Assoziativität wichtig (vgl. \cpageref{bsp:foldl-und-foldr})
- \end{itemize}
|