Lerninhalte finden
Features
Entdecke
© StudySmarter 2024, all rights reserved.
Du untersuchst eine fiktive Programmiersprache, die auf kontextfreien Grammatiken basiert. Diese Sprache enthält grundlegende Kontrollstrukturen wie Schleifen und Bedingungen. Es gibt zwei Aufgaben: (1) Die formale Spezifikation der Grammatik in erweiterter Backus-Naur-Form (EBNF) zu schreiben und (2) einen Syntaxbaum und die zugehörige operationale Semantik für ein gegebenes Codebeispiel zu erstellen.
Gib die EBNF-Definition für die folgenden Sprachkonstrukte: Variablenzuweisung, If-Anweisung und While-Schleife. Nutze dazu die Notationen für Terminalsymbole, Nichtterminalsymbole, Optionale und Wiederholungen.
Lösung:
Im Folgenden findest Du die EBNF-Definitionen für Variablenzuweisungen, If-Anweisungen und While-Schleifen in der fiktiven Programmiersprache:
::= '=' ';' ::= ::= | '+' | '-' ::= | '*' | '/' ::= | | '(' ')' ::= letter {letter | digit} ::= digit {digit} ::= 'a' | 'b' | 'c' | ... | 'z' | 'A' | 'B' | 'C' | ... | 'Z' ::= '0' | '1' | '2' | ... | '9'
::= 'if' '(' ')' ['else' ] ::= ::= '==' | '!=' | '<' | '>' | '<=' | '>=' ::= '{' { } '}' ::= | | | ';'
::= 'while' '(' ')'
Zeichne einen Syntaxbaum für das folgende Codebeispiel unter Verwendung der in Subexercise 1 definierten Grammatik:
if (x > 0) y = y + 1; else y = y - 1;
Lösung:
Hier ist der Syntaxbaum für das gegebene Codebeispiel:
if (x > 0) y = y + 1; else y = y - 1;
Beschreibe die operationale Semantik für das obige Codebeispiel. Gehe dabei detailliert auf die Zustandsänderungen ein, die bei der Ausführung des Codes auftreten. Berücksichtige dabei auch die Start- und Endzustände der Variablen.
Lösung:
Die operationale Semantik beschreibt, wie sich der Zustand des Programms Schritt für Schritt ändert, wenn der Code ausgeführt wird. Im vorliegenden Codebeispiel handelt es sich um eine If-Anweisung, die abhängig von der Bedingung entweder eine bestimmte Aktion ausführt oder eine alternative Aktion. Beschreiben wir die Zustandsänderungen im Detail:
if (x > 0) y = y + 1; else y = y - 1;
Zusammenfassend führen die einzelnen Schritte zu einer Zustandsänderung, die davon abhängt, ob der Wert der Variablen x größer als 0 ist oder nicht. Diese Änderung wird durch die entsprechende Anweisung (Erhöhung oder Verringerung von y) umgesetzt.
Gegeben: Ein ungerichteter Graph G(V,E) mit den Knoten V und den Kanten E. Wir möchten verschiedene Operationen auf diesem Graphen durchführen, darunter das Auffinden des kürzesten Pfades, das Sortieren der Knoten in einer bestimmten Ordnung und das effiziente Suchen nach einem bestimmten Knoten.
A: Implementiere den Algorithmus von Dijkstra zur Bestimmung der kürzesten Pfade von einem Startknoten s zu allen anderen Knoten in G. Zeige die Schritte des Algorithmus anhand eines Beispielgraphen mit mindestens 5 Knoten und 7 Kanten.
'def dijkstra(graph, start): import heapq priority_queue = [] heapq.heappush(priority_queue, (0, start)) distances = {vertex: float('infinity') for vertex in graph} distances[start] = 0 while priority_queue: current_distance, current_vertex = heapq.heappop(priority_queue) if current_distance > distances[current_vertex]: continue for neighbor, weight in graph[current_vertex].items(): distance = current_distance + weight if distance < distances[neighbor]: distances[neighbor] = distance heapq.heappush(priority_queue, (distance, neighbor)) return distances'
Lösung:
Gegeben: Ein ungerichteter Graph G(V,E) mit den Knoten V und den Kanten E. Wir möchten verschiedene Operationen auf diesem Graphen durchführen, darunter das Auffinden des kürzesten Pfades, das Sortieren der Knoten in einer bestimmten Ordnung und das effiziente Suchen nach einem bestimmten Knoten.
A: Implementiere den Algorithmus von Dijkstra zur Bestimmung der kürzesten Pfade von einem Startknoten s zu allen anderen Knoten in G. Zeige die Schritte des Algorithmus anhand eines Beispielgraphen mit mindestens 5 Knoten und 7 Kanten.
Hier ist der Python-Code, der den Dijkstra-Algorithmus implementiert:
def dijkstra(graph, start): import heapq priority_queue = [] heapq.heappush(priority_queue, (0, start)) distances = {vertex: float('infinity') for vertex in graph} distances[start] = 0 while priority_queue: current_distance, current_vertex = heapq.heappop(priority_queue) if current_distance > distances[current_vertex]: continue for neighbor, weight in graph[current_vertex].items(): distance = current_distance + weight if distance < distances[neighbor]: distances[neighbor] = distance heapq.heappush(priority_queue, (distance, neighbor)) return distances
Um den Algorithmus zu demonstrieren, betrachten wir den folgenden Beispielgraphen:
Der Graph kann als Dictionary dargestellt werden:
graph = { 'A': {'B': 1, 'C': 4}, 'B': {'A': 1, 'C': 2, 'D': 5}, 'C': {'A': 4, 'B': 2, 'D': 1, 'E': 3}, 'D': {'B': 5, 'C': 1, 'E': 2}, 'E': {'C': 3, 'D': 2}}
Wenn wir den Algorithmus von Dijkstra für den Startknoten 'A' anwenden, erhalten wir die kürzesten Distanzen zu allen anderen Knoten:
distances = dijkstra(graph, 'A')print(distances)
Die Ausgabe zeigt die kürzesten Distanzen von 'A' zu allen anderen Knoten:
{ 'A': 0, 'B': 1, 'C': 3, 'D': 4, 'E': 6 }
Dies bedeutet, dass der kürzeste Pfad von 'A' zu 'B' eine Distanz von 1 hat, von 'A' zu 'C' eine Distanz von 3, und so weiter.
B: Sortiere die Knoten des Graphen topologisch, falls der Graph ein DAG (gerichteter, azyklischer Graph) ist. Erläutere den Algorithmus und seine Komplexität. Demonstriere den Algorithmus anhand eines Graphen mit mindestens 6 Knoten und 8 Kanten.
Lösung:
Gegeben: Ein ungerichteter Graph G(V,E) mit den Knoten V und den Kanten E. Wir möchten verschiedene Operationen auf diesem Graphen durchführen, darunter das Auffinden des kürzesten Pfades, das Sortieren der Knoten in einer bestimmten Ordnung und das effiziente Suchen nach einem bestimmten Knoten.
B: Sortiere die Knoten des Graphen topologisch, falls der Graph ein DAG (gerichteter, azyklischer Graph) ist. Erläutere den Algorithmus und seine Komplexität. Demonstriere den Algorithmus anhand eines Graphen mit mindestens 6 Knoten und 8 Kanten.
Der Algorithmus zur topologischen Sortierung eines gerichteten, azyklischen Graphen (DAG) kann mit einem Tiefensuche-Algorithmus (DFS) oder mit dem Kahn-Algorithmus durchgeführt werden. Hier werden wir die DFS-basierte Methode erläutern:
1. Algorithmus:
Die Zeitkomplexität des Algorithmus ist O(V + E), wobei V die Anzahl der Knoten und E die Anzahl der Kanten ist.
Hier ist der Python-Code, um die topologische Sortierung durchzuführen:
def topological_sort(graph): def dfs(vertex, visited, stack): visited.add(vertex) for neighbor in graph.get(vertex, []): if neighbor not in visited: dfs(neighbor, visited, stack) stack.append(vertex) visited = set() stack = [] for vertex in graph: if vertex not in visited: dfs(vertex, visited, stack) stack.reverse() return stack
Um den Algorithmus zu demonstrieren, betrachten wir den folgenden Beispielgraphen:
Der Graph kann als Dictionary dargestellt werden:
graph = { 'A': ['B', 'C'], 'B': ['D', 'E'], 'C': ['D', 'F'], 'D': ['E'], 'E': ['F'], 'F': []}
Wenn wir die Methode zur topologischen Sortierung anwenden, erhalten wir die folgende Sequenz:
sorted_nodes = topological_sort(graph)print(sorted_nodes)
Die Ausgabe zeigt die topologische Sortierung der Knoten:
['A', 'B', 'C', 'D', 'E', 'F']
Dies bedeutet, dass Knoten 'A' vor 'B' und 'C' kommen muss, 'B' und 'C' vor 'D', und so weiter.
C: Führe eine binäre Suche auf einer sortierten Liste der Knoten des Graphen durch, um nach einem bestimmten Knoten x zu suchen. Zeige die Schritte der binären Suche und analysiere ihre Zeitkomplexität.
'def binary_search(sorted_list, target): left, right = 0, len(sorted_list) - 1 while left <= right: mid = (left + right) // 2 if sorted_list[mid] == target: return mid elif sorted_list[mid] < target: left = mid + 1 else: right = mid - 1 return -1'
Lösung:
Gegeben: Ein ungerichteter Graph G(V,E) mit den Knoten V und den Kanten E. Wir möchten verschiedene Operationen auf diesem Graphen durchführen, darunter das Auffinden des kürzesten Pfades, das Sortieren der Knoten in einer bestimmten Ordnung und das effiziente Suchen nach einem bestimmten Knoten.
C: Führe eine binäre Suche auf einer sortierten Liste der Knoten des Graphen durch, um nach einem bestimmten Knoten x zu suchen. Zeige die Schritte der binären Suche und analysiere ihre Zeitkomplexität.
Hier ist der Python-Code, der die binäre Suche implementiert:
def binary_search(sorted_list, target): left, right = 0, len(sorted_list) - 1 while left <= right: mid = (left + right) // 2 if sorted_list[mid] == target: return mid elif sorted_list[mid] < target: left = mid + 1 else: right = mid - 1 return -1
Um den Algorithmus zu demonstrieren, nehmen wir an, die Knoten des Graphen sind alphabetisch sortiert:
sorted_nodes = ['A', 'B', 'C', 'D', 'E', 'F']
Wir möchten beispielsweise nach dem Knoten 'D' suchen:
target = 'D'
Die Schritte der binären Suche sind wie folgt:
Die Ausgabe zeigt den Index des Zielknotens:
index = binary_search(sorted_nodes, target)print(index) # Ausgabe: 3
Da der Index 3 zurückgegeben wird, haben wir den Knoten 'D' erfolgreich gefunden.
Komplexitätsanalyse:
Datenbankdesign und Normalisierung:Datenbankdesign bezieht sich auf die Strukturierung von Datenbanken zur effizienten und widerspruchsfreien Speicherung von Daten. Die Normalisierung ist ein Prozess, der Redundanzen und Inkonsistenzen durch die Aufteilung in kleinere, miteinander verknüpfte Tabellen eliminiert. Die Hauptziele sind Redundanzfreiheit, Konsistenz, Flexibilität und Performance. Die Normalformen helfen bei diesem Prozess:
Gegeben sei die folgende Tabelle einer Datenbank, die Informationen zu den Kursen an der TU München speichert:
KursID | KursName | Dozent | DozentenTel | StudentID | StudentName | Studienfach | Note ------------------------------------------------------------------------------------------ 1 | Datenbankdesign | Dr. Müller | 1234567 | 1001 | Anna Schmidt| Informatik | 1.3 1 | Datenbankdesign | Dr. Müller | 1234567 | 1002 | Max Mustermann | Informatik | 2.0 2 | Algorithmen | Dr. Meier | 7654321 | 1003 | Maria Klein | Mathematik | 2.5 2 | Algorithmen | Dr. Meier | 7654321 | 1004 | Julia Neu | Informatik | 1.7
Lösung:
KursID | KursName | Dozent | DozentenTel | StudentID | StudentName | Studienfach | Note
KursID | KursName | Dozent | DozentenTel | StudentID | StudentName | Studienfach | Note------------------------------------------------------------------------------------------ 1 | Datenbankdesign | Dr. Müller | 1234567 | 1001 | Anna Schmidt | Informatik | 1.3 1 | Datenbankdesign | Dr. Müller | 1234567 | 1002 | Max Mustermann| Informatik | 2.0 2 | Algorithmen | Dr. Meier | 7654321 | 1003 | Maria Klein | Mathematik | 2.5 2 | Algorithmen | Dr. Meier | 7654321 | 1004 | Julia Neu | Informatik | 1.7Die Tabelle entspricht der 1. Normalform, da alle Daten atomar und jede Zeile einzigartig identifizierbar ist.
Überführe die Tabelle, die nun in 1NF ist, in die zweite Normalform (2NF). Zeige den Übergang und die resultierenden Tabellen. Begründe jede deiner Schritte präzise.
Lösung:
KursID | KursName | Dozent | DozentenTel----------------------------------------------------------- 1 | Datenbankdesign | Dr. Müller | 1234567 2 | Algorithmen | Dr. Meier | 7654321
StudentID | StudentName | Studienfach-------------------------------------------- 1001 | Anna Schmidt | Informatik 1002 | Max Mustermann| Informatik 1003 | Maria Klein | Mathematik 1004 | Julia Neu | Informatik
KursID | StudentID | Note------------------------------- 1 | 1001 | 1.3 1 | 1002 | 2.0 2 | 1003 | 2.5 2 | 1004 | 1.7
Nach der Überführung in die 2NF, überführe die neuen Tabellen in die dritte Normalform (3NF). Zeige ebenfalls deine Schritte und erkläre, warum diese notwendig sind.
Lösung:
KursID | KursName | Dozent | DozentenTel----------------------------------------------------------- 1 | Datenbankdesign | Dr. Müller | 1234567 2 | Algorithmen | Dr. Meier | 7654321
StudentID | StudentName | Studienfach---------------------------------------- 1001 | Anna Schmidt | Informatik 1002 | Max Mustermann| Informatik 1003 | Maria Klein | Mathematik 1004 | Julia Neu | Informatik
KursID | StudentID | Note------------------------------- 1 | 1001 | 1.3 1 | 1002 | 2.0 2 | 1003 | 2.5 2 | 1004 | 1.7
KursID | KursName | DozentID------------------------------------ 1 | Datenbankdesign | 1 2 | Algorithmen | 2
DozentID | Dozent | DozentenTel------------------------------------- 1 | Dr. Müller | 1234567 2 | Dr. Meier | 7654321
KursID | KursName | DozentID------------------------------------ 1 | Datenbankdesign | 1 2 | Algorithmen | 2
DozentID | Dozent | DozentenTel------------------------------------- 1 | Dr. Müller | 1234567 2 | Dr. Meier | 7654321
StudentID | StudentName | Studienfach--------------------------------------- 1001 | Anna Schmidt | Informatik 1002 | Max Mustermann| Informatik 1003 | Maria Klein | Mathematik 1004 | Julia Neu | Informatik
KursID | StudentID | Note------------------------------- 1 | 1001 | 1.3 1 | 1002 | 2.0 2 | 1003 | 2.5 2 | 1004 | 1.7
Du bist als Data Scientist beauftragt, ein neuronales Netzwerk zu entwickeln, das handgeschriebene Ziffern erkennen soll. Dafür nutzt Du das MNIST-Datenset, das 60.000 Trainingsbilder und 10.000 Testbilder von Ziffern (0-9) enthält. Du entscheidest Dich für ein tiefes neuronales Netzwerk mit mehreren versteckten Schichten und verwendest Python mit Bibliotheken wie TensorFlow oder PyTorch. Der Ansatz soll folgende Schritte beinhalten: Datenerhebung und -vorverarbeitung, Modellarchitektur, Trainingsprozess, und Evaluierung.
Beschreibe die Architektur des neuronalen Netzwerks, das Du entwickeln möchtest. Gib die Anzahl der Schichten (Input-, versteckte Schichten, Output-Schicht) an und erkläre die Funktion der Aktivierungsfunktionen, die Du verwenden wirst. Verwende Pseudo-Code, um Deine Architektur zu veranschaulichen.
Lösung:
Für die Erkennung handgeschriebener Ziffern plane ich, ein tiefes neuronales Netzwerk zu erstellen. Die Architektur umfasst folgende Schichten:
Aktivierungsfunktionen haben die Aufgabe, Nichtlinearitäten in das Modell einzuführen. In diesem Netzwerk werden folgende Aktivierungsfunktionen verwendet:
Nachfolgend ein Pseudo-Code zur Veranschaulichung der Architektur:
# Pseudo-Code zur Definition der Netzwerkarchitektur# Import der notwendigen Bibliothekimport tensorflow as tf# Initialisiere ein sequentielles Modellmodel = tf.keras.Sequential()# Input-Schichtmodel.add(tf.keras.layers.InputLayer(input_shape=(784,)))# Erste versteckte Schicht (128 Neuronen, ReLU)model.add(tf.keras.layers.Dense(128, activation='relu'))# Zweite versteckte Schicht (64 Neuronen, ReLU)model.add(tf.keras.layers.Dense(64, activation='relu'))# Dritte versteckte Schicht (32 Neuronen, ReLU)model.add(tf.keras.layers.Dense(32, activation='relu'))# Output-Schicht (10 Neuronen, Softmax)model.add(tf.keras.layers.Dense(10, activation='softmax'))# Kompiliere das Modell (Verwende Kreuzentropie als Verlustfunktion und Adam als Optimierer)model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])# Zeige die Modellzusammenfassungmodel.summary()
Um Overfitting zu vermeiden, planst Du, Regularisierungstechniken und Dropout anzuwenden. Beschreibe, wie diese Techniken funktionieren und integriere sie in den Trainingsprozess. Diskutiere weiterhin, wie Du die Leistung des Modells auf dem Testdatensatz evaluieren wirst und welche Metriken Du dafür heranziehst.
Lösung:
Um Overfitting zu vermeiden, können wir Regularisierungstechniken wie L2-Regularisierung und Dropout verwenden:
\[J(\theta) = J(\theta)_{original} + \frac{\lambda}{2m} \sum_{j=1}^{n} \theta_j^2\]
Dabei ist \(\lambda\) der Regularisierungsparameter und \(m\) die Anzahl der Trainingsbeispiele.
Dropout ist eine Technik, bei der während des Trainings zufällig bestimmte Neuronen deaktiviert werden. Dies verhindert, dass sich das Modell zu stark auf bestimmte Neuronen verlässt und somit überanpasst. Dropout wird nur während des Trainings angewandt und nicht während des Testens. Der Dropout-Layer kann einfach mit TensorFlow oder Keras integriert werden:
import tensorflow as tffrom tensorflow.keras.layers import Dense, Dropoutmodel = tf.keras.Sequential([ Dense(128, activation='relu', input_shape=(784,)), Dropout(0.5), Dense(64, activation='relu'), Dropout(0.5), Dense(10, activation='softmax')])
Der Trainingsprozess wird mit diesen Techniken wie folgt erweitert:
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])history = model.fit(train_images, train_labels, epochs=20, validation_split=0.2)
Nach dem Training wird das Modell auf dem Testdatensatz evaluiert, um die Leistungsfähigkeit zu bestimmen:
from sklearn.metrics import accuracy_score, confusion_matrixtest_loss, test_acc = model.evaluate(test_images, test_labels, verbose=2)y_pred = model.predict(test_images)conf_matrix = confusion_matrix(test_labels, y_pred.argmax(axis=1))print(f'Test Accuracy: {test_acc}')print(f'Confusion Matrix:{conf_matrix}')
Zusätzlich können Precision, Recall und F1-Score berechnet werden, um ein detaillierteres Bild der Modellleistung zu bekommen:
from sklearn.metrics import classification_reportprint(classification_report(test_labels, y_pred.argmax(axis=1)))
Mit unserer kostenlosen Lernplattform erhältst du Zugang zu Millionen von Dokumenten, Karteikarten und Unterlagen.
Kostenloses Konto erstellenDu hast bereits ein Konto? Anmelden