Testklausur zur Vorlesung Angewandte Informatik Prof. Dr. Nikolaus Wulff 18. Januar 2006 Diese Klausur besteht aus fünf Aufgaben, von denen Sie drei bearbeiten und lösen müssen, d.h. maximal drei richtig gelöste Aufgaben entsprechen 100% der Gesamtpunktzahl. 1. Schauen Sie sich daher die Aufgaben zu nächst in Ruhe an und wählen Sie dann die Aufgaben, die Ihnen am leichtesten erscheinen. Sofern Sie mehr als drei Lösungen abgeben, werden die drei schlechtesten Aufgaben bewertet. 2. Schreiben Sie auf jedes Lösungsblatt Ihre Matrikelnummer und zu welcher Aufgabe es gehört. Nummerieren Sie die Lösungsblätter durch. 3. Schreiben Sie leserlich, Hieroglyphen werden nicht gewertet. 4. Kommentieren Sie ihre Programmlistings, so dass klar ist, was Sie damit bezwecken. 5. Für diese Klausur sind keine Hilfsmittel zugelassen. Viel Erfolg! 1 1 Zahlen Ring √ √ Die Menge Z[ 2] := a + 2 · b | ∀ a, b ∈ Z wird mit den beiden binären √ √ √ Operationen ⊕ und ⊗ : Z[ 2] × Z[ 2] → Z[ 2] √ √ √ (a + 2 · b) ⊕ (c + 2 · d) 7→ (a + c) + 2 · (b + d) (1) √ √ √ (a + 2 · b) ⊗ (c + 2 · d) 7→ (ac + 2bd) + 2 · (ad + bc) (2) √ zu einem Ring (Z[ 2], ⊕, ⊗). Modellieren Sie diesen Datentyp in C, in einer Datei Ring.h: /** Ring.h */ #ifndef __RING_H #define __RING_H typedef struct ring_struct Ring; struct ring_struct { /* fields missing ... */ }; /* methods missing ... */ #endif /* __RING_H defined */ mit geeignet ausgeprägten Feldern und Methoden√für die Operationen +, − und ∗. Achtung Division ist für einen Ring über Z[ 2] nicht definiert. Geben Sie die Implementierung der Operationen + und ∗ aus der Datei Ring.c an: /** Ring.c */ #include "Ring.h" Ring add(Ring p, Ring q) { ... } Tip: √ Sie benötigen die 2-Funktion nicht zur Implementierung. Sie hat eine analoge Bedeutung, wie die Imaginäre Zahl i bei den komplexen Zahlen und √ wird nicht explizit ausgewertet. Die Elemente z ∈ Z[ 2] sind also Tupel (a, b) mit a, b ∈ Z, die lediglich mit etwas ”anderen Verknüpfungen” für +, − und ∗ versehen sind. 2 2 Ermittlung der Nullstelle einer reelen Funktion Entwickeln Sie einen Algorithmus zur Bestimmung der Nullstelle einer reelwertigen, stetigen Funktion f (x) nach dem Newtonverfahren. Hierzu wird eine Folge (xn ) von Punkten rekursiv nach der Vorschrift xn+1 := xn − f (xn ) , ∀n ∈ N f 0 (xn ) (3) gebildet. Diese Vorschrift macht natürlich nur für f 0 (x) 6= 0 im betrachteten Bereich Sinn, wovon Sie hier der Einfachheit halber stillschweigend von ausgehen dürfen. Ihre Aufgabe ist es zu der C Header Datei /** Newton.h */ #ifndef __NEWTON_H #define __NEWTON_H /* define a function pointer as type */ typedef double (*Function)(double x); /** * newton returns double x with f(x)=0 */ double newton( Function F, /* function to use */ Function dF, /* first derivative of F */ double x0, /* x_0 iteration start point */ double epsilon); /* precision of x_n */ #endif eine geeignete Implementierung zu entwickeln. Die Routine newton berechnet unter Ausnutzung von Formel (3) eine Approximation der Nullstelle x0 bei gegebenen Funktionen f (x) ≡ F (x) und f 0 (x) ≡ dF (x), zu einem vorgegebenem Approximationsstartwert x0 und einer Fehlerschranke . Sehen Sie eine maximale Anzahl an Iterationsschritten vor, die sie per #define MAX_STEPS xx definieren, so dass der Algorithmus abbricht falls keine Konvergenz eintritt. Überlegen Sie sich, wie sie eine geeignete Abbruchbedingung überprüfen. Verwenden Sie zur Auswertung von Formel (3) eine geeignete Unterroutine innerhalb der Methode newton, so dass der Code modular aufgebaut ist. 3 3 Stapel Modellieren Sie den Datentypen eines Stapels (engl. Stack) als einfach verkettete Liste, nach dem LIFO Prinzip (last in first out), wie in Abbildung 1 dargestellt, mit einem möglichen Top Element, einmal als C struct und einmal als Java Klasse. Geben Sie hierzu die C Header-Datei stack.h und die Java Klasse Stack.java an. Für C reicht eine ausformulierte Header Datei, mit allen vorgesehenen typedefs. Sollte allerding Java nicht die Sprache Ihrer Wahl sein, muss stattdessen eine entsprechende Implementierungsdatei stack.c abgegeben werden. 0,1 Stack Object Q Q push(Object o) cellar pop(): Object size(): int Abbildung 1: UML Diagramm eines Stacks. Eine solcher Stapel wird intern als Liste rekursiv definiert, indem das erste Stack Objekt eine Referenz auf das folgende Stack Stapel Objekt der letzten push Operation enthält, Abbildung 2. In der pop Operation wird dieses oberste Keller Stack Element entfernt, nachdem vorher die Keller Referenz auf das dann aktuelle oberste Stack Element umgehängt wurde. Entnehmen Sie die zu implementierenden Methoden dem UML Diagramm 1. In der C Implementierung modellieren Sie einen generischen Pointer auf ein Objekt als void*. Bedenken Sie, dass in der C Implementierung die Signatur der Methoden mehr Parameter erfordert als in Java. head t t ? obj cellar - t t cellar - ? obj t t cellar - ? obj Abbildung 2: Sich referenzierende Stack Objekte. 4 d d /** Stack.h */ #ifndef __STACK_H #define __STACK_H typedef void* Object; typedef struct ... struct stack_struct { /* fields missing ... */ }; /* methods missing ... */ #endif /** * Stack.java */ public class Stack { // attributes missing // functions missing } // class Stack Stack head = ... int objs[] = {1,2,3,4,5}; int i, n = sizeof(objs)/sizeof(objs[0]); for (i=0; i<n; i++) { push(head,objs+i); } printf("stack size: %d\n",size(head)); for (i=0; i<n; i++) { Object obj = pop(head); int o = *(int *) obj; printf("%d obj: %d \n",i, o ); } Das Beispiel verdeutlicht die Verwendung der C Stackimplementation. Der Aufrufende erhält nur eine Referenz auf das Stack head Element, ohne die weiteren Stackinstanzen zu verwenden oder zu kennen. 5 4 Interpolation von Messwerten Gegenen sei eine Folge (yk ) von n+1 Messewerten, mit den Stützstellen (xk ), wobei der Laufindex k von k = 0, ...n läuft. Gesucht ist der interpolierte Wert y an einer beliebigen Stelle x aus dem Interval [x0 , xn ]. Entwickeln Sie hierzu die Methode interpolate(x), die zu gegebenen Messwerten die Interpolation an der Stelle x liefert. Nach der Methode von Aitken und Neville wird hierzu rekursiv ein Satz von Polynomen Pj,k (x) definiert, mit der Vorschrift P0,k (x) = yk für 0 ≤ k ≤ n (x − xk−j )Pj−1,k (x) − (x − xk )Pj−1,k−1 (x) ,1 ≤ j ≤ k ≤ n Pj,k (x) = xk − xk−j Das Polynom Pn,n (x) ≡ interpolate(x) liefert dann genau den gesuchten interpolierenden Rückgabewert. Die Methode interpolate(x) wird nach der Initialisierung der Interpolator Klasse mit den Stützstellen (xk ) und den Messpunkten (yk ) aufgerufen. Die Implementierungsdatei Interpolator.java verwendet hierzu intern das Polynom Pn,n (x) zum Berechnen des gesuchten y Wertes. /** Interpolator.java */ public class Interpolator { private double[] xv, yv; public Interpolator(double[] x, double[] y) { this.xv = x; this.yv = y; } public double interpolate(double x) { int n= xv.length - 1; return polynom(n,n,x); } private double polynom(int j, int k, double x) { // implementation still missing ... return ??? } } Geben Sie die Implementierung der Methode polynom(j,k,x) an und zeichnen Sie den Aufrufgraph für den Aufruf von P2,2 (x). 6 5 Gerichteter Graph Gegeben sei der gerichtete Graph G = (V, E) mit V = {A, B, C, D, E} und der Relation E = {(A, B), (A, D), (B, E), (C, A), (C, E), (D, E)}. • Zeichnen Sie den Graph G. • Wie lautet die Adjazenzmatrix AG ? • Zeichnen Sie den Graph der Transitiven Hülle von G. • Wie lautet die Adjazenzmatrix RG der Transitiven Hülle? • Wie lautet die Relation E(RG ) der Transitiven Hülle? • Enthält der Graph Schleifen? Begründen Sie Ihre Ergebnisse/Aussagen. 7