Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen der Seite angezeigt.

Link zu der Vergleichsansicht

Beide Seiten, vorherige Überarbeitung Vorherige Überarbeitung
Nächste Überarbeitung
Vorherige Überarbeitung
talit:algorithmen [2024-06-03 05:46] – [Aufgabe 6: Pfadsuche mit BFS] hoftalit:algorithmen [2025-03-03 21:01] (aktuell) – [Aufgabe 4 - Graph Walk] hof
Zeile 16: Zeile 16:
 Wie könnten wir das Weg-Problem (von der KSR an den Bahnhof) abstrakt erfassen? Wir könnten die verschiedenen Wege zum Beispiel so erfassen: Wie könnten wir das Weg-Problem (von der KSR an den Bahnhof) abstrakt erfassen? Wir könnten die verschiedenen Wege zum Beispiel so erfassen:
  
 +<figure graph_romis>
 {{:talit:pasted:20220423-154956.png?600}} {{:talit:pasted:20220423-154956.png?600}}
 +<caption>Graph mit drei Pfaden zum Bahnhof</caption>
 +</figure>
  
 Aus was besteht ein Graph? Aus **Knoten** (en. _nodes_ oder _vertices_) und **Kanten** (_edges_). In unserem Fall haben die Kanten **Gewichte**, die die Länge oder Fahrtdauer der einzelnen Strecken repräsentieren. Aus was besteht ein Graph? Aus **Knoten** (en. _nodes_ oder _vertices_) und **Kanten** (_edges_). In unserem Fall haben die Kanten **Gewichte**, die die Länge oder Fahrtdauer der einzelnen Strecken repräsentieren.
  
-Wie könnten wir diesen Graphen in Python repräsentieren +Wie könnten wir diesen Graphen in Python repräsentieren? 
-?+
 #### Adjazenzmatrix #### Adjazenzmatrix
  
Zeile 110: Zeile 113:
 </code> </code>
 ++++ ++++
- 
 ### Tiefensuche ### Tiefensuche
  
 Bevor wir den schnellsten Weg an den Bahnhof zu finden versuchen, wären wir schon mal nur froh, überhaupt einen Weg zu finden. Es könnte ja auch sein, dass wir uns in die Turnhalle begeben, von wo kein Weg zum Bahnhof führt. Oder dass wir von der Bahnhofstrasse zurück zur KSR gelangen: Bevor wir den schnellsten Weg an den Bahnhof zu finden versuchen, wären wir schon mal nur froh, überhaupt einen Weg zu finden. Es könnte ja auch sein, dass wir uns in die Turnhalle begeben, von wo kein Weg zum Bahnhof führt. Oder dass wir von der Bahnhofstrasse zurück zur KSR gelangen:
 +
 +<figure graph_romis2>
 +[[http://magjac.com/graphviz-visual-editor/?dot=digraph%20Romanshorn%20%7B%0A%20%20%20%20rankdir%3DTB%3B%0A%09node%20%5Bshape%20%3D%20doublecircle%3B%20width%3D1%5D%3B%20KSR%20Bahnhof%3B%0A%09node%20%5Bshape%20%3D%20circle%3B%20width%3D1%3B%20fixedwidth%3Dtrue%5D%3B%0A%09KSR%20-%3E%20Turnhalle%20%5B%20label%20%3D%201%20%5D%3B%0A%09KSR%20-%3E%20Weitenzelgstr%20%5B%20label%20%3D%201%20%5D%3B%0A%09Weitenzelgstr%20-%3E%20Bahnhofstr%20%5B%20label%20%3D%208%20%5D%3B%0A%09KSR%20-%3E%20Sek%20%5B%20label%20%3D%202%20%5D%3B%0A%09Sek%20-%3E%20Bahnhofstr%20%5B%20label%20%3D%203%20%5D%3B%0A%09Bahnhofstr%20-%3E%20Bahnhof%20%5B%20label%20%3D%207%20%5D%3B%0A%09Bahnhofstr%20-%3E%20KSR%20%5B%20label%20%3D%204%20%5D%3B%0A%09Sek%20-%3E%20Hafenstrasse%20%5B%20label%20%3D%205%20%5D%3B%0A%09Hafenstrasse%20-%3E%20Bahnhof%20%5B%20label%20%3D%204%20%5D%3B%0A%09Sek%20-%3E%20Zelgstrasse%20%5B%20label%20%3D%207%20%5D%3B%0A%09Zelgstrasse%20-%3E%20Bahnhof%20%5B%20label%20%3D%205%20%5D%3B%0A%7D%0A|{{:talit:pasted:20220521-145117.png?400|Graph}}]]
 +<caption>Detaillierter Graph von der KSR zum Bahnhof</caption>
 +</figure>
  
  
-[[http://magjac.com/graphviz-visual-editor/?dot=digraph%20Romanshorn%20%7B%0A%20%20%20%20rankdir%3DTB%3B%0A%09node%20%5Bshape%20%3D%20doublecircle%3B%20width%3D1%5D%3B%20KSR%20Bahnhof%3B%0A%09node%20%5Bshape%20%3D%20circle%3B%20width%3D1%3B%20fixedwidth%3Dtrue%5D%3B%0A%09KSR%20-%3E%20Turnhalle%20%5B%20label%20%3D%201%20%5D%3B%0A%09KSR%20-%3E%20Weitenzelgstr%20%5B%20label%20%3D%201%20%5D%3B%0A%09Weitenzelgstr%20-%3E%20Bahnhofstr%20%5B%20label%20%3D%208%20%5D%3B%0A%09KSR%20-%3E%20Sek%20%5B%20label%20%3D%202%20%5D%3B%0A%09Sek%20-%3E%20Bahnhofstr%20%5B%20label%20%3D%203%20%5D%3B%0A%09Bahnhofstr%20-%3E%20Bahnhof%20%5B%20label%20%3D%207%20%5D%3B%0A%09Bahnhofstr%20-%3E%20KSR%20%5B%20label%20%3D%204%20%5D%3B%0A%09Sek%20-%3E%20Hafenstrasse%20%5B%20label%20%3D%205%20%5D%3B%0A%09Hafenstrasse%20-%3E%20Bahnhof%20%5B%20label%20%3D%204%20%5D%3B%0A%09Sek%20-%3E%20Zelgstrasse%20%5B%20label%20%3D%207%20%5D%3B%0A%09Zelgstrasse%20-%3E%20Bahnhof%20%5B%20label%20%3D%205%20%5D%3B%0A%7D%0A|{{:talit:pasted:20220521-145117.png?400}}]] 
 #### Aufgabe 2 #### Aufgabe 2
  
Zeile 219: Zeile 225:
   * Funktioniert der Code auch, wenn sich die Zyklen überschneiden?   * Funktioniert der Code auch, wenn sich die Zyklen überschneiden?
  
-<nodisp 2>+<nodisp 1>
 ++++Lösung:| ++++Lösung:|
 <code python> <code python>
Zeile 260: Zeile 266:
 **Aufgabe**: Schreibe eine Funktion `dfs(graph)`, die die Knoten eines Graphen mittels Tiefensuche ausgibt. **Aufgabe**: Schreibe eine Funktion `dfs(graph)`, die die Knoten eines Graphen mittels Tiefensuche ausgibt.
   * Die Ausgabe kann mit `print()` erfolgen oder eine Liste zurückgeben.   * Die Ausgabe kann mit `print()` erfolgen oder eine Liste zurückgeben.
-  * Herausforderung: schreibe eine [[https://en.wikipedia.org/wiki/Generator_(computer_programming)#Python|Generator-Funktion]] mit dem `yield` Keyword.+  * Herausforderung: schreibe eine [[talit:generators|Generator-Funktion]] mit dem `yield` Keyword.
  
 <nodisp 1> <nodisp 1>
Zeile 323: Zeile 329:
     * Entdeckte, aber noch nicht besuchte Knoten in einer Liste: `candidates = [start]`     * Entdeckte, aber noch nicht besuchte Knoten in einer Liste: `candidates = [start]`
     * Noch unbekannte Knoten (brauchen wir uns nicht zu merken).     * Noch unbekannte Knoten (brauchen wir uns nicht zu merken).
-  * Jeweils der vorderste Knoten `node` in `candidates` wird entfernt.+  * Solange `candidates` nicht leer ist, wird der vorderste Knoten `node` in `candidates` entfernt.
     * Falls `node` bereits in `visited` ist, gehen wir zum nächsten (er wurde mehrfach entdeckt über verschiedene Pfade).     * Falls `node` bereits in `visited` ist, gehen wir zum nächsten (er wurde mehrfach entdeckt über verschiedene Pfade).
     * Andernfalls: wird `node` zu `visited` hinzugefügt.     * Andernfalls: wird `node` zu `visited` hinzugefügt.
       * Falls `node == end` sind wir fertig.       * Falls `node == end` sind wir fertig.
-      * Alle Nachbarknoten, die noch nicht in `visited` sind, werden hinten in die `candidates` eingereiht+      * Alle Nachbarknoten von `node`, die noch nicht in `visited` sind, werden hinten in die `candidates` eingereiht.
-      * Wiederholen.+
  
 <nodisp 1> <nodisp 1>
Zeile 369: Zeile 374:
 ++++ ++++
 </nodisp> </nodisp>
 +
 +
 #### Aufgabe 6: Pfadsuche mit BFS #### Aufgabe 6: Pfadsuche mit BFS
  
Zeile 380: Zeile 387:
   * Zusätzlich merken wir uns:   * Zusätzlich merken wir uns:
     * alle besuchten Knoten (`visited`)     * alle besuchten Knoten (`visited`)
-    den Vorgänger-Knoten für jeden Knoten (`parent = {}`)+      Statt einem Set verwenden wir ein Dictionary für `visited`
 +      * Wir merken uns für jeden Knoten dessen ersten Vorgängerknoten, um daraus am Schluss den Pfad zu bauen.
 ++++ ++++
  
Zeile 405: Zeile 413:
     }     }
     return result     return result
- + 
-from collections import deque+
 def find_path_bfs(graph, start, end): def find_path_bfs(graph, start, end):
     """Finds a path from start to end using BFS. """     """Finds a path from start to end using BFS. """
-    # Deque: a double-ended list with efficient modifications at either end. +    candidates = [start] 
-    candidates = deque() +    visited = {start: None}
-    candidates.append(start) +
-    visited = {start+
-    parent = {}+
     while candidates:     while candidates:
-        next = candidates.popleft()+        next = candidates.pop(0)
         for neighbor in graph[next]:         for neighbor in graph[next]:
             if neighbor not in visited:             if neighbor not in visited:
-                visited.add(neighbor)+                visited[neighbor] = next
                 candidates.append(neighbor)                 candidates.append(neighbor)
-                parent[neighbor] = next 
                 if neighbor == end:                 if neighbor == end:
-                    return build_path_from_parents(graph, parent, start, end)+                    return build_path_from_parents(graph, visited, start, end)
     return None  # No path found     return None  # No path found
 </code> </code>
Zeile 440: Zeile 443:
 {'Amriswil': 5, 'Egnach': 1, 'Konstanz': 17, 'Kreuzlingen Hafen': 14, 'Neukirch-Egnach': 2, 'Romanshorn': 3, 'Romanshorn (See)': 5, 'Romanshorn Autoquai': 6, 'Romanshorn, Bahnhof': 3, 'St. Gallen': 18, 'Uttwil': 3, 'Weinfelden': 13, 'Wittenbach': 11} {'Amriswil': 5, 'Egnach': 1, 'Konstanz': 17, 'Kreuzlingen Hafen': 14, 'Neukirch-Egnach': 2, 'Romanshorn': 3, 'Romanshorn (See)': 5, 'Romanshorn Autoquai': 6, 'Romanshorn, Bahnhof': 3, 'St. Gallen': 18, 'Uttwil': 3, 'Weinfelden': 13, 'Wittenbach': 11}
 </code> </code>
 +
 #### Aufgabe 7: Real-World Beispiel #### Aufgabe 7: Real-World Beispiel
  
Zeile 481: Zeile 485:
  
 Der Algorithmus basiert auf dem folgenden Prinzip: Der Algorithmus basiert auf dem folgenden Prinzip:
-  * Die Kandidaten sind nach aufsteigender Distanz zum Startknoten sortiert. Für die effiziente Sortierung der Kandidaten benützen wir eine `PriorityQueue`.+  * Die Kandidaten sind nach aufsteigender Distanz zum Startknoten sortiert. 
 +    * Hier liegt der Unterschied zu BFS: statt die neu-entdeckten Kandidaten einfach hinten einzureihen, werden sie nach aufsteigender Distanz zum Start sortiert. 
 +    * Für die effiziente Sortierung der Kandidaten benützen wir eine [[wpde>Vorrangwarteschlange|PriorityQueue]].
   * Der vorderste Kandidat ist immer der nächste Knoten in aufsteigender Distanz von `start` her.   * Der vorderste Kandidat ist immer der nächste Knoten in aufsteigender Distanz von `start` her.
-  * Beweis: gäbe es einen Knoten `x` der näher bei `start` liegt, so wäre dessen Vorläufer-Knoten bereits besucht worden und die geringere Distanz von `x` wäre bereits bekannt geworden. +    * Beweis: gäbe es einen Knoten `x` der näher bei `start` liegt, so wäre dessen Vorläufer-Knoten bereits besucht worden und die geringere Distanz von `x` wäre bereits bekannt geworden. 
-  * Achtung: für alle weiteren Kandidaten stimmt die Sortierung noch nicht definitiv: möglicherweise gibt es einen kürzeren Pfad als den bekannten via einen weiter vorne liegenden Kandidaten!+    * Achtung: für alle weiteren Kandidaten stimmt die Sortierung noch nicht definitiv: möglicherweise gibt es einen kürzeren Pfad als den bekannten via einen weiter vorne liegenden Kandidaten, dessen Nachbarn noch nicht bekannt sind!
  
 <code python> <code python>
Zeile 576: Zeile 582:
 ++++ ++++
 </nodisp> </nodisp>
 +### Visualisierung
 +Mit Folium lassen sich die gefundenen Pfade hübsch visualiseren. Der Code für untenstehende Grafik findet sich auf [[https://github.com/tkilla77/ksr_talenta_graphs/blob/main/sbb.ipynb|github]].
 +
 +<figure shortest_path>
 +{{:talit:algorithmen:pasted:20250302-193146.png?nolink}}<caption>Pfad von Romanshorn nach Bern --- Rot: BFS, Grün: DFS, Blau: Dijkstra</caption>
 +</figure>
  • talit/algorithmen.1717393594.txt.gz
  • Zuletzt geändert: 2024-06-03 05:46
  • von hof