Prof. Dr. K.–U. Jahn HTWK Leipzig, Fb IMN Projektaufgaben zur C–Programmierung im WS07/08 Verwenden Sie in den Programmen die in den jeweiligen Aufgaben genannten Bezeichnungen! Für weitere eventuell verwendete Variablen ist deren Verwendungszweck zu kommentieren! Die Programme sind klar zu gliedern und ausreichend mit Kommentaren zu versehen! (Projektaufgabe 5 wird in den Praktika gemeinsam bearbeitet.) Projektaufgabe 1: Lexikographisches Sortieren von Zeichenketten über einem vorgegebenen Alphabet Es sei n die Anzahl der Elemente eines Alphabetes A von druckbaren ASCII–Zeichen, 1 ≤ n ≤ 95. Der Wert von n ist während der Laufzeit zu erfragen. Nachdem der Wert von n bekannt ist, soll ein dynamisches Array a der Länge n mit Elementen vom Typ char erzeugt werden. In dieses sind nach entsprechenden Eingabeaufforderungen n druckbare ASCII–Zeichen von der Tastatur her einzutragen, wobei durch die Eingabereihenfolge eine lineare Ordnung in A definiert werde (das zuerst eingegebene Zeichen werde wie das kleinste Element des künstlichen Alphabets A behandelt usw.). Das Programm soll es nicht zulassen, dass ein Zeichen doppelt eingegeben wird. Nachdem auf diese Weise das Alphabet zusammen mit einer Ordnungsrelation bekannt ist, sollen die Anzahl k von Zeichenketten sowie deren maximale Länge m erfragt und von der Tastatur her eingegeben werden (1 ≤ k, m). Der Computer soll dann ein dynamisches Array z der Länge k mit unterschiedlich langen (jeweils zufällige Länge) nichtleeren zufälligen Zeichenketten maximaler Länge m erzeugen, wobei die Zeichen nur aus dem Alphabet A sein dürfen. Die k Zeichenketten sind dann, jeweils beginnend mit einer neuen Zeile, in der durch z gegebenen Reihenfolge auszugeben. Schließlich sind z und k an eine benutzerdefinierte Funktion sort zu übergeben, die die Zeichenketten lexikographisch sortiert entsprechend der vorgegebenen linearen Ordnung in A. Die Funktion sort soll ihrerseits eine Funktion vgl aufrufen, mit der festgestellt werden kann, in welcher Beziehung zwei Zeichenketten bzgl. der lexikographischen Ordnung zueinander stehen. Zur Kontrolle ist auch das sortierte Array z auszugeben. Die beiden Ausgaben von z sind mit Hilfe einer benutzerdefinierten Funktion print zu veranlassen. Die zum Sortieren benötigte Zeit ist mit Hilfe der Systemuhr zu stoppen und auszugeben! Zur Erinnerung (s. dazu auch Übungsaufgabe 4): Für zwei nichtleere Zeichenketten s und t gilt genau dann s ≤ t, wenn gilt: (length(s) ≤ length(t) ∧ ∀i(0 ≤ i ≤ (length(s) − 1) → si = ti )) ∨ ∃r(0 ≤ r < min{length(s), length(t)} ∧ sr < tr ∧ ∀i(0 ≤ i < r → si = ti )) Projektaufgabe 2: Speicherung von binären Relationen R; Test auf Äquivalenzrelationseigenschaft; Erweiterung zur bzgl. ⊆ kleinsten R umfassenden Äquivalenzrelation Es sollen zunächst über die Tastatur eine natürliche Zahl n und dann geordnete Paare (i, j) mit 0 ≤ i, j < n, die die binäre Relation R ⊆ {0, · · · , n − 1} × {0, · · · , n − 1} ausmachen sollen, eingegeben werden (Abbruch der Eingabe dann, wenn ein Paar mit einer negativen Komponente eingegeben wurde). Diese Relation R ist auf die drei folgenden Weisen abzuspeichern: • durch ihre Adjazenzmatrix (siehe Folie 22), als dynamische Matrix zu definieren • als einfach verkettete Liste Rli aller ihrer Paare • als lineares Array Rar von Nachfolgerlisten der Elemente von {0, · · · , n − 1} (das ist ein Array mit n Elementen, wobei Rar[i] eine einfach verkettete Liste aller Nachfolger von i bzgl. R ist; ist das Array selbst als Liste angelegt, so spricht man auch von der Adjazenzlisten–Darstellung von R). Beispielsweise könnte also die Relation R := {(0, 0), (0, 2), (0, 3), (1, 2), (2, 0), (2, 3)} in der Menge {0, 1, 2, 3} wie folgt als einfach verkettete Liste Rli bzw. als Array Rar von Nachfolgerlisten abgespeichert sein: Rli Rar 0 0 0 0 2 2 0 3 1 2 2 0 2 3 3 2 0 3 (a) Schreiben Sie je eine benutzerdefinierte Funktion, durch die für die Darstellungen von R als Adjazenzmatrix, als einfach verkettete Liste bzw. als Array von einfach verketteten Listen überprüft wird, ob R eine Äquivalenzrelation in {0, · · · , n − 1} ist oder nicht! (b) Falls R keine Äquivalenzrelation ist, so soll durch das Programm die bzgl. ⊆ kleinste Äquivalenzrelation S in {0, · · · , n − 1} bestimmt werden, für die R ⊆ S gilt! Diese soll wieder in jedem der drei obigen Formate abgespeichert werden! (c) R und S sind als Mengen ihrer geordneten Paare auszugeben! Projektaufgabe 3: Speicherung von Mengen mittels Hash–Tabellen; Durchschnitts– und Vereinigungsbildung Eine endliche Menge von ganzen Zahlen kann folgendermaßen mittels einer Hash–Tabelle gespeichert werden: Zunächst wird natürliche Zahl n > 0 vorgegeben. Dann wird ein eindimensionales Array ht der Länge n mit Zeigern auf zunächst leere Listen von ganzen Zahlen definiert, dh., es ist zunächst ht[i]=NULL für alle i mit 0 ≤ i < n. Um eine ganze Zahl k in die Tabelle einzutragen, wird zunächst i:=k mod n gebildet (aufpassen, wenn k negativ ist, dann ist −n ≤ i ≤ n − 1; falls also i < 0 ist, so ist genau einmal n zu i zu addieren, damit dann 0 ≤ i < n ist). Wenn dann k noch nicht in der bei ht[i] beginnenden Liste enthalten ist, so wird k in diese Liste eingetragen, etwa am Anfang dieser Liste. Beispiel: Falls n = 3 ist und man die 5 Zahlen -6, -1, 5, 7 und 9 auf obige Weise abspeichert, so ergibt sich die folgende Darstellung: ht[0] −6 ht[1] 7 ht[2] −1 9 5 (Möchte man beispielsweise Zeichenketten abspeichern, so kann man diesen zunächst einen Zahlenwert zuordnen, etwa indem die ASCII–Codes aller Vorkommen aller Zeichen addiert werden. Dieser Zahlenwert wird dann wieder wie oben modulo n modifiziert.) Nun zur eigentlichen Programmieraufgabe: Das Programm soll anfangs den Wert von obigem n sowie die Anzahlen m1 bzw. m2 der Elemente der Mengen M1 bzw. M2 erfragen. Dann sind die beiden Mengen als Hash– Tabellen mit zufälligen ganzen Zahlen vom Typ int zu füllen, wobei natürlich eine bestimmte Zahl in jeder Menge höchstens einmal vorkommen darf. Es sind dann drei benutzerdefinierte Funktionen print, intersect bzw. union zu schreiben, mit deren Hilfe folgendes getan werden soll: • Die Elemente von M1 und von M2 sind auszugeben! • Es ist die Menge M3 := M1 ∩ M2 zu bilden, als Hash–Tabelle abzuspeichern und auszugeben! • Es ist die Menge M4 := M1 ∪ M2 zu bilden, als Hash–Tabelle abzuspeichern und auszugeben! Projektaufgabe 4: Programmierung eines wissenschaftlichen UPN–Rechners mit double–Zahlen und den zweistelligen Operationen +, –, *, / sowie den einstelligen Operationen abs, neg, inv, sqr und sqrt Mit Hilfe eines C–Programmes ist ein obiger Rechner zu simulieren, wobei auf dem Bildschirm der jeweilige Kellerinhalt darzustellen ist (das jeweilige top–Element soll auch oben sein). Anfangs sei der Keller leer. Jedes Mal, wenn eine Gleitpunktzahl eingegeben wird (Abschluss der Eingabe mit ENTER), ist diese zu kellern. Wird eine einstellige Operation eingegeben (Abschluss der Eingabe wie immer mit ENTER) und der Keller ist nicht leer, so soll diese Operation das oberste Kellerelement entsprechend verändern; falls jedoch der Keller leer ist, so soll eine Fehlermeldung erfolgen. Wird eine zweistellige Operation eingegeben und es sind mindestens zwei Elemente im Keller, so soll diese Operation auf die beiden obersten Kellerelemente angewendet werden, wobei danach der Keller ein Element weniger enthält. Das oberste Kellerelement ist das Ergebnis der Operation. War jedoch höchstens ein Element im Keller, so soll die Nichtdurchführbarkeit der Operation angezeigt werden. Am Schluss einer Termwertberechnung darf dann nur noch eine Zahl im Keller stehen, nämlich das Ergebnis. Der Keller selbst ist als einfach verkettete Liste stack abzuspeichern, wobei die Verkettung von oben nach unten erfolgen soll. Projektaufgabe 5: Prüfungsplanung auf der Basis eines Fächerkonfliktgraphen Vom Programm sind anfangs die Anzahl n > 0 der Fächer (=Anzahl der Knoten des Graphen) sowie die Anzahl k ≤ n ∗ (n − 1)/2 der Kanten des ungerichteten Fächerkonfliktgraphen zu erfragen. Die Fächer mögen durch die natürlichen Zahlen von 1 bis n dargestellt werden, so dass also für die Knotenmenge Kn gilt: Kn:={1, · · · , n}. Es soll Kn als einfach verkettete Liste im Computer abgespeichert werden. Die k Kanten sind wahlweise über die Tastatur einzugeben oder zufällig zu erzeugen. Die Menge Ka der Kanten ist ebenfalls als einfach verkettete Liste von Paaren von Fächern zu speichern. Jetzt sollen nacheinander maximale unabhängige Teilmengen der Knotenmenge gebildet werden, wobei jede solche Menge die Menge der Prüfungsfächer eines Prüfungstages ist. Diese Mengen sind ebenfalls je durch einfach verkettete Listen abzuspeichern, und alle diese Listen sind in einer Liste der Prüfungstage zusammenzufassen. Schließlich sind die Prüfungsfächer je Prüfungstag für alle Prüfungstage auszugeben. Modifikation der Aufgabenstellung: Unmittelbar bevor die Prüfungsfächer eines Prüfungstages berechnet werden, soll der Nutzer gefragt werden, welches Fach er auf jeden Fall an diesem Tag unter den Prüfungsfächern haben möchte. Zur Erinnerung: Eine Menge M heißt genau dann maximale unabhängige Teilmenge der Knotenmenge Kn des Graphen (Kn, Ka), wenn gilt: M ⊆ Kn ∧ M 6= ∅ ∧ ∀a∀b(a ∈ M ∧ b ∈ M ∧ a 6= b → {a, b} ∈ / Ka) ∧ ∀a(a ∈ Kn \ M → ∃b(b ∈ M ∧ {a, b} ∈ Ka))