2014-03-01 20:04:19 +01:00
|
|
|
%!TEX root = Programmierparadigmen.tex
|
2014-02-01 19:44:40 +01:00
|
|
|
\chapter{Programmiertechniken}
|
|
|
|
\section{Rekursion}
|
2014-02-02 17:48:55 +01:00
|
|
|
\index{Rekursion|(}
|
|
|
|
|
2014-09-18 12:08:55 +02:00
|
|
|
\begin{definition}[rekursive Funktion]\xindex{Funktion!rekursive}%
|
2014-02-02 17:48:55 +01:00
|
|
|
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*}
|
2014-02-04 14:07:24 +01:00
|
|
|
Erzeugt die Zahlen $0, 1, 1, 2, 3, 5, 8, 13, \dots$
|
2014-02-02 17:48:55 +01:00
|
|
|
\item Fakultät:\xindex{Fakultät}
|
|
|
|
\begin{align*}
|
2014-02-04 14:07:24 +01:00
|
|
|
! &: \mdn_0 \rightarrow \mdn_0\\
|
2014-02-02 17:48:55 +01:00
|
|
|
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*}
|
2014-02-04 14:07:24 +01:00
|
|
|
\binom{\cdot}{\cdot} &: \mdn_0 \times \mdn_0 \rightarrow \mdn_0\\
|
2014-02-02 17:48:55 +01:00
|
|
|
\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
|
2014-04-05 18:18:47 +02:00
|
|
|
Speicherbedarf. Für jeden rekursiven Aufruf müssen alle lokalen Variablen
|
2014-02-02 17:48:55 +01:00
|
|
|
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}
|
|
|
|
|
2014-02-04 14:07:24 +01:00
|
|
|
\begin{definition}[linear rekursive Funktion]\xindex{Funktion!linear rekursive}%
|
2014-02-02 17:48:55 +01:00
|
|
|
Eine Funktion heißt linear rekursiv, wenn in jedem Definitionszweig
|
|
|
|
der Funktion höchstens ein rekursiver Aufruf vorkommt.
|
|
|
|
\end{definition}
|
|
|
|
|
2014-02-04 14:07:24 +01:00
|
|
|
\begin{definition}[endrekursive Funktion]\xindex{Funktion!endrekursive}\xindex{tail recursive}%
|
2014-02-02 17:48:55 +01:00
|
|
|
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}
|
|
|
|
|
2014-02-04 14:07:24 +01:00
|
|
|
Auf Englisch heißen endrekursive Funktionen \textit{tail recursive}.
|
2014-02-02 17:48:55 +01:00
|
|
|
|
2014-02-04 14:07:24 +01:00
|
|
|
\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}
|
|
|
|
|
2014-03-01 20:04:19 +01:00
|
|
|
Wenn eine rekursive Funktion nicht terminiert oder wenn
|
|
|
|
|
2014-02-04 14:07:24 +01:00
|
|
|
\index{Rekursion|)}
|
2014-02-01 19:44:40 +01:00
|
|
|
\section{Backtracking}
|
2014-02-04 14:07:24 +01:00
|
|
|
\index{Backtracking|(}
|
2014-03-18 15:45:40 +01:00
|
|
|
Unter \textit{Backtracking} versteht man eine Programmiertechnik, die
|
|
|
|
(eventuell implizit) auf einem Suchbaum arbeitet und mittels Tiefensuche versucht
|
|
|
|
eine Lösung zu finden.
|
2014-02-04 14:07:24 +01:00
|
|
|
|
2014-03-18 15:45:40 +01:00
|
|
|
\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}
|
2014-02-04 14:07:24 +01:00
|
|
|
\index{Backtracking|)}
|
2014-02-01 19:44:40 +01:00
|
|
|
|
2014-02-04 14:07:24 +01:00
|
|
|
\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
|
2014-03-01 20:04:19 +01:00
|
|
|
Element genommen wurde.\\
|
2014-03-02 19:35:37 +01:00
|
|
|
Bei \texttt{reduce} ist die Assoziativität wichtig (vgl. \cpageref{bsp:foldl-und-foldr})
|
2014-02-04 14:07:24 +01:00
|
|
|
\end{itemize}
|