<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
		<id>https://alda.iwr.uni-heidelberg.de/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=LDream</id>
		<title>Alda - User contributions [en]</title>
		<link rel="self" type="application/atom+xml" href="https://alda.iwr.uni-heidelberg.de/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=LDream"/>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php/Special:Contributions/LDream"/>
		<updated>2026-05-08T16:04:55Z</updated>
		<subtitle>User contributions</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Greedy-Algorithmen_und_Dynamische_Programmierung&amp;diff=2498</id>
		<title>Greedy-Algorithmen und Dynamische Programmierung</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Greedy-Algorithmen_und_Dynamische_Programmierung&amp;diff=2498"/>
				<updated>2008-07-21T09:00:59Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* Beispiel zur Dynamischen Programmierung: Wighted Intervall Scheduling */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
:Viele Probleme sind durch einen Entscheidungsbaum systematisch lösbar.&lt;br /&gt;
:Dabei wird die zu suchende Lösung auf den optimalen Weg durch den Entscheidungsbaum reduziert.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel ===&lt;br /&gt;
:''Erklärung des Algorithmus ist zu finden in [[Graphen und Graphenalgorithmen]]''&lt;br /&gt;
:Traveling Salesman Problem mit 4 Knoten&lt;br /&gt;
&lt;br /&gt;
[[Image:tsm_points.JPG]]&lt;br /&gt;
&lt;br /&gt;
:'''Dabei entsteht folgender Entscheidungsbaum:'''&lt;br /&gt;
&lt;br /&gt;
[[Image:tsm4.JPG]]&lt;br /&gt;
&lt;br /&gt;
::'''Vorteil des Entscheidungsbaums:''' Lösungsmöglichkeiten werden nicht übersehen&lt;br /&gt;
::'''Nachteil:''' Eventuell muss der gesamte Baum durchsucht werden (exponentielle Komplexität)&lt;br /&gt;
:Um diesen Nachteil auszugleichen gibt es verschiedene Verfahren:&lt;br /&gt;
:* Divide &amp;amp; Conquer (Problem auf triviale Teilprobleme zurückführen, welche jeweils einfach zu lösen sind)&lt;br /&gt;
:* Greedy Algorithmen&lt;br /&gt;
:* Dynamische Programmierung&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Greedy Algorithmen ==&lt;br /&gt;
:Greedy (dt. &amp;quot;Gierig&amp;quot;) Algorithmen entscheiden an jedem Knoten '''lokal''' über die beste Fortsetzung der Suche,&lt;br /&gt;
:d.h. es wird jeweils die beste Entscheidung im Kleinen getroffen - ohne Rücksicht auf Konsequenzen für den gesamten Suchverlauf.&lt;br /&gt;
&lt;br /&gt;
=== Beispiele ===&lt;br /&gt;
==== Anwendung beim Traveling Salesman Problem ====&lt;br /&gt;
:''Erklärung des Algorithmus ist zu finden in [[Graphen und Graphenalgorithmen]]''&lt;br /&gt;
:Reise immer zum nächstgelegenen, noch nicht besuchten Knoten:&lt;br /&gt;
&lt;br /&gt;
[[Image:tsm_greedyb.JPG]]&lt;br /&gt;
&lt;br /&gt;
:In diesem Beispiel wurde eine optimale Lösung gefunden. '''Dies muss im Allgemeinen aber nicht immer der Fall sein!'''&lt;br /&gt;
&lt;br /&gt;
==== Anwendung beim Algorithmus von Kruskal für Minium Spanning Tree ====&lt;br /&gt;
:''Erklärung des Algorithmus ist zu finden in [[Graphen und Graphenalgorithmen]]''&lt;br /&gt;
:* Sortiere die Kanten nach Gewicht&lt;br /&gt;
:* Wähle stets die Kante mit niedrigstem Gewicht (d.h. im Allgemeinen die nächsgelegene), die keinen Zyklus verursacht&lt;br /&gt;
&lt;br /&gt;
:Hierbei wird der Minimum Spanning Tree stets gefunden.&lt;br /&gt;
&lt;br /&gt;
== Dynamische Programmierung ==&lt;br /&gt;
(''Programmierung'' hat hier eine Bedeutung die sich nicht auf Programmiersprachen bezieht)&lt;br /&gt;
&lt;br /&gt;
:Oft ist dasselbe Teilproblem in mehreren Pfaden vorhanden.&lt;br /&gt;
=== Beispiel ===&lt;br /&gt;
&lt;br /&gt;
[[Image:fib1.JPG]]&lt;br /&gt;
&lt;br /&gt;
:Im Beispiel mit Fibonacci-Zahlen wird Fib(2) gleich dreimal benötigt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:Zur Erinnerung: Die Fibonacci-Folge &amp;lt;math&amp;gt;(f_0, f_1,\ldots)&amp;lt;/math&amp;gt; ist durch das rekursive Bildungsgesetz&lt;br /&gt;
:&amp;lt;math&amp;gt; f_n = f_{n-1} + f_{n-2}\ &amp;lt;/math&amp;gt; &amp;amp;nbsp; für &amp;lt;math&amp;gt;n\geq 2&amp;lt;/math&amp;gt;&lt;br /&gt;
:mit den Anfangswerten&lt;br /&gt;
:&amp;lt;math&amp;gt;f_0=0\ &amp;lt;/math&amp;gt; &amp;amp;nbsp; und &amp;amp;nbsp; &amp;lt;math&amp;gt;f_1=1\ &amp;lt;/math&amp;gt;&lt;br /&gt;
:definiert.&lt;br /&gt;
&lt;br /&gt;
=== Konzept der Dynamische Programmierung ===&lt;br /&gt;
:Jedes Teilproblem soll nur einmal gelöst werden, d.h. einige Knoten werden mehrmals genutzt:&lt;br /&gt;
&lt;br /&gt;
[[Image:fib2.JPG]]&lt;br /&gt;
&lt;br /&gt;
:Wie im Beispiel erkennbar, hat sich die Zahl der Knoten drastisch reduziert (von 9 auf 5). &lt;br /&gt;
:Allerdings müssen die '''Graphen jetzt gerichtet''' sein.&lt;br /&gt;
&lt;br /&gt;
:Wenn der neue Graph '''azyklisch''' ist, kann man die Teilprobleme so anordnen, dass jedes&lt;br /&gt;
:* nur einmal gelöst wird &lt;br /&gt;
:* nur von bereits gelösten Teilproblemen abhängt&lt;br /&gt;
&lt;br /&gt;
:Wenn der Graph nicht azyklisch ist (weil z.B. Teilproblem A die Lösung von Teilproblem B erfordert und umgekehrt),&lt;br /&gt;
:ist die Dynamische Programmierung auf dieses Problem nicht anwendbar.&lt;br /&gt;
&lt;br /&gt;
==== Dynamisch programmierter Dijkstra Alrogithmus ====&lt;br /&gt;
:''Erklärung des Algorithmus ist zu finden in [[Graphen und Graphenalgorithmen]]''&lt;br /&gt;
:Löse Teilprobleme entsprechend ihrer Priorität, d.h. Priorität definiert die Ordnung&lt;br /&gt;
&lt;br /&gt;
:'''Problem:''' Der Suchbaum ist bei diesem Algorithmus ungerichtet&lt;br /&gt;
&lt;br /&gt;
:'''Lösung:''' Die Richtung der Kanten wird festgelegt, wenn man die Nachbarn eines Knotens in die Queue eingefügt&lt;br /&gt;
:Wenn man den Abstand vom Start bestimmt (Teilproblem), ist der Abstand von allen näher gelegenen bereits bekannt.&lt;br /&gt;
&lt;br /&gt;
[[Image:dijkstra.JPG]]&lt;br /&gt;
&lt;br /&gt;
== Greedy oder Dynamische Programmierung? ==&lt;br /&gt;
:Für viele Probleme gibt es unterschiedliche Entscheidungsräume und/oder unterschiedliche Entscheidungskriterien.&lt;br /&gt;
:Ein und dasselbe Problem kann also mit einer der Darstellungen (Greedy, Dynamische Programmierung, weitere...) effizient lösbar sein, mit anderen eventuell nicht.&lt;br /&gt;
:Das finden einer geeigneten Darstellung ist also eine zentrale Herausforderung.&lt;br /&gt;
&lt;br /&gt;
== Anwendungsbeispiel: Interval Scheduling ==&lt;br /&gt;
:'''gegeben:'''&lt;br /&gt;
::Mehrere Aufgaben mit unterschiedlichen Anfangszeiten &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; und Endzeiten &amp;lt;math&amp;gt;f_i&amp;lt;/math&amp;gt;. &lt;br /&gt;
::Es kann immer nur eine Aufgabe gleichzeitig bearbeitet werden: zwei Aktivitäten sind kompatibel, wenn deren Zeiten sich nicht überlappen.&lt;br /&gt;
::&amp;lt;math&amp;gt;a_k\,\text{komp}\, a_j  \Leftrightarrow  s_k\geq f_j\, \or \, s_j\geq f_k&amp;lt;/math&amp;gt;&lt;br /&gt;
:'''gesucht:'''&lt;br /&gt;
::Arbeitsplan um möglichst viele Aufgaben nacheinander abzuarbeiten.&lt;br /&gt;
::Dabei haben alle Aufgaben dieselbe Priorität, obwohl die Dauer oft unterschiedlich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Mögliche Lösungsansätze für einen Greedy Algorithmus ===&lt;br /&gt;
:# Wähle (unter kompatiblen) die Aktivität, die als erste startet&lt;br /&gt;
:# '''Wähle (unter kompatiblen) die Aktivität, die als erste endet (oder: die als letzte startet)'''&lt;br /&gt;
:# Wähle (unter kompatiblen) die Aktivität, die am kürzesten dauert&lt;br /&gt;
:# Wähle (unter kompatiblen) die Aktivität, die die wenigsten Inkompatibilitäten (überlappungen mit anderen Aktivitäten) hat&lt;br /&gt;
&lt;br /&gt;
'''Ungünstige Ansätze:'''&lt;br /&gt;
:In den folgenden Beispielen werden die Aktivitäten mit | als Anfangs- bzw Endzeit markiert, --- steht für den Verlauf einer Aktivität.&lt;br /&gt;
:Weiter rechts bedeutet später in der Zeit.&lt;br /&gt;
:'''Gegenbeispiel zu 1.'''&lt;br /&gt;
     &amp;lt;math&amp;gt;L_1=&amp;lt;/math&amp;gt;  |--| |--| |--| |--|&lt;br /&gt;
     &amp;lt;math&amp;gt;L_2=&amp;lt;/math&amp;gt;|---------------------|&lt;br /&gt;
     &amp;lt;math&amp;gt;|L_1|&amp;lt;/math&amp;gt;=4 &amp;lt;math&amp;gt;|L_2|&amp;lt;/math&amp;gt;=1&lt;br /&gt;
:Der Ansatz würde Lösung 2 wählen, da die lange Aktivität am frühesten beginnt. Es wird dann nur 1 statt der optimalen 4 abgearbeitet.&lt;br /&gt;
:'''Gegenbeispiel zu 3.'''&lt;br /&gt;
     &amp;lt;math&amp;gt;L_1=&amp;lt;/math&amp;gt;|---------| |---------|&lt;br /&gt;
     &amp;lt;math&amp;gt;L_2=&amp;lt;/math&amp;gt;        |----|&lt;br /&gt;
     &amp;lt;math&amp;gt;|L_1|&amp;lt;/math&amp;gt;=2 &amp;lt;math&amp;gt;|L_2|&amp;lt;/math&amp;gt;=1&lt;br /&gt;
:Der Ansatz würde Lösung 2 wählen, da die mittlere Aktivität am kürzesten dauert. Es wird dann nur 1 statt der optimalen 2 abgearbeitet.&lt;br /&gt;
:'''Gegenbeispiel zu 4. (Anzahl der Inkompatibilitäten stehen jeweils in der Mitte der Aktivität)'''&lt;br /&gt;
     |-3-| |-4-| |-4-| |-3-|&lt;br /&gt;
        |-4-| |-2-| |-4-|&lt;br /&gt;
        |-4-|       |-4-|&lt;br /&gt;
        |-4-|       |-4-|&lt;br /&gt;
:Der Ansatz würde erst die mit 2, dann die beiden mit 3 Inkompatibilitäten wählen. Es werden dann nur 3 statt der optimalen 4 (obere Zeile) abgearbeitet.&lt;br /&gt;
&lt;br /&gt;
Es verbleibt der 2. Ansatz, dessen Optimalität noch zu beweisen ist...&lt;br /&gt;
&lt;br /&gt;
== Greedy Stays Ahead ==&lt;br /&gt;
==== Idee der Beweismethode ====&lt;br /&gt;
'''Es genügt zu zeigen:''' &lt;br /&gt;
:die Greedy-Lösung ist nicht schlechter als die optimale Lösung&lt;br /&gt;
&lt;br /&gt;
=== Beweis der Optimalität des 2. Ansatzes mit ''Greedy Stays Ahead'' ===&lt;br /&gt;
==== Ansatz ====&lt;br /&gt;
:Wähle (unter kompatiblen) die Aktivität, die als erste endet (oder: die als letzte startet)&lt;br /&gt;
:Die Wahl dieses Ansatzes sei &amp;lt;math&amp;gt;U={i_1,...,i_k}&amp;lt;/math&amp;gt;.&lt;br /&gt;
:Eine (unbekannte) optimale Lösung sei &amp;lt;math&amp;gt;O={j_1,...,j_m}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Ziel ====&lt;br /&gt;
:Die Lösung des Ansatzes soll genausoviele Aktivitäten schaffen wie die optimale Lösung (d.h. k=m)&lt;br /&gt;
==== Voraussetzungen ====&lt;br /&gt;
:* Sortiere &amp;lt;math&amp;gt;i_1,...,i_k&amp;lt;/math&amp;gt; nach aufsteigender Endzeit &amp;lt;math&amp;gt;f_i&amp;lt;/math&amp;gt;&lt;br /&gt;
:* Sortiere &amp;lt;math&amp;gt;j_1,...,j_m&amp;lt;/math&amp;gt; nach aufsteigender Endzeit &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Da die Aktivitäten kompatibel sind, werden die Anfangszeiten automatisch auch sortiert)&lt;br /&gt;
&lt;br /&gt;
==== Schritt 1 ====&lt;br /&gt;
:Für die Indizes &amp;lt;math&amp;gt;p\leq r&amp;lt;/math&amp;gt; (inbesondere &amp;lt;math&amp;gt;p=r&amp;lt;/math&amp;gt;) gilt: &amp;lt;math&amp;gt;f(i_p)\leq f(j_r)&amp;lt;/math&amp;gt;&lt;br /&gt;
==== Beweis durch vollständige Induktion ====&lt;br /&gt;
:'''Induktions-Anfang:'''&lt;br /&gt;
::&amp;lt;math&amp;gt;f(i_1)\leq f(j_1)&amp;lt;/math&amp;gt;, da &amp;lt;math&amp;gt;i_1&amp;lt;/math&amp;gt; die erste Aktivität ist, die überhaupt endet&lt;br /&gt;
:'''Induktions-Voraussetzung:'''&lt;br /&gt;
::&amp;lt;math&amp;gt;f(i_{r-1})\leq f(j_{r-1})&amp;lt;/math&amp;gt;&lt;br /&gt;
:'''Induktions-Schritt:'''&lt;br /&gt;
::Wegen Kompatibilität gilt:&lt;br /&gt;
::&amp;lt;math&amp;gt;f(j_{r-1})\leq s(j_r)&amp;lt;/math&amp;gt;&lt;br /&gt;
::=&amp;gt; &amp;lt;math&amp;gt;f(i_{r-1})\leq s(i_r)&amp;lt;/math&amp;gt;&lt;br /&gt;
::=&amp;gt; Die Greedy Strategie ''kann'' Aktivität &amp;lt;math&amp;gt;j_r&amp;lt;/math&amp;gt; wählen, denn sie ist kompatibel mit &amp;lt;math&amp;gt;i_{r-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
::* Wenn die Greedy Strategie tatsächlich &amp;lt;math&amp;gt;j_r&amp;lt;/math&amp;gt; wählt, folgt daraus: &lt;br /&gt;
:::&amp;lt;math&amp;gt;f(i_r)=f(j_r)&amp;lt;/math&amp;gt;&lt;br /&gt;
::* Wenn nicht, kann nur gelten:&lt;br /&gt;
:::&amp;lt;math&amp;gt;f(i_r)\leq f(j_r)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schritt 2 ====&lt;br /&gt;
:Zu zeigen: &amp;lt;math&amp;gt;k=m&amp;lt;/math&amp;gt;&lt;br /&gt;
==== Beweis durch Widerspruchsannahme ====&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;m&amp;lt;k&amp;lt;/math&amp;gt;, wäre die Lösung der Strategie besser als die optimale.&lt;br /&gt;
:* Angenommen &amp;lt;math&amp;gt;m&amp;gt;k&amp;lt;/math&amp;gt;, dann enthält &amp;lt;math&amp;gt;O&amp;lt;/math&amp;gt; eine Aktivität &amp;lt;math&amp;gt;j_{k+1}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
:Nach Schritt 1 gilt:&lt;br /&gt;
::&amp;lt;math&amp;gt;f(i_k)\leq f(j_k)\leq f(j_{k+1})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Wegen Kompatibilität gilt aber:&lt;br /&gt;
::&amp;lt;math&amp;gt;s(j_{k+1})\geq f(j_k)\geq f(i_k)&amp;lt;/math&amp;gt;&lt;br /&gt;
:-&amp;gt; Die Greedy Strategie hätte also noch die Aktivität &amp;lt;math&amp;gt;j_{k+1}&amp;lt;/math&amp;gt; wählen können.&lt;br /&gt;
:-&amp;gt; Widerspruch zur Annahme, dass die Greedy Strategie durchgelaufen ist, bis keine Aktivität mehr hinzugefügt werden kann&lt;br /&gt;
:-&amp;gt; m&amp;gt;k ist falsch&lt;br /&gt;
:-&amp;gt; m=k ist richtig&lt;br /&gt;
&lt;br /&gt;
=== Beispiel zur Dynamischen Programmierung: Weighted Intervall Scheduling ===&lt;br /&gt;
:Die Problemstellung ähnelt dem des normalen Intervall Scheduling, hier haben die Aktivitäten aber Gewichte &amp;lt;math&amp;gt;w_i&amp;lt;/math&amp;gt;&lt;br /&gt;
:(z.B. Bringt eine längere Aufgabe in einem Übunsgzettel in der Regel auch mehr Punkte, d.h. sie hat eine hohe Gewichtung)&lt;br /&gt;
==== Ziel ====&lt;br /&gt;
:Wähle die Aktivitäten so, dass der Gewinn (Summe der Gewichtungen der bearbeiteten Aktivitäten) maximal wird.&lt;br /&gt;
==== Ansatz ====&lt;br /&gt;
:* Sortiere Aktivitäten nach ihrer Endzeit.&lt;br /&gt;
:* Definiere eine Funktion &amp;lt;math&amp;gt;p(i)&amp;lt;/math&amp;gt;, welche für die Aktivität steht, die vor &amp;lt;math&amp;gt;a_i&amp;lt;/math&amp;gt; endet, mit &amp;lt;math&amp;gt;a_i&amp;lt;/math&amp;gt; kompatibel ist, unter allen Aktivitäten mit diesen Eigenschaften die letzte (d.h. mit der spätesten Endzeit) ist. &lt;br /&gt;
&lt;br /&gt;
In folgendem Beispiel wird die Aktivität &amp;lt;math&amp;gt;a_i&amp;lt;/math&amp;gt; mit der Symbolik |-!-| betrachtet um deren p-Funktion zu evaluieren.&lt;br /&gt;
&lt;br /&gt;
Für die p-Funktion kommen lediglich die Funktionen mit der Symbolik |===| und |====| in Frage, die untere der beiden ist die gesuchte Aktivität.&lt;br /&gt;
    |====|        |-!-| |--|&lt;br /&gt;
        |===| |-----|         |----|&lt;br /&gt;
&lt;br /&gt;
:Trivial ist, dass &amp;lt;math&amp;gt;a_n&amp;lt;/math&amp;gt; entweder zur Lösung gehört, oder nicht:&lt;br /&gt;
&lt;br /&gt;
[[Image:wis1.JPG]]&lt;br /&gt;
&lt;br /&gt;
:Dadurch ergibt sich folgende Funktion:&lt;br /&gt;
:&amp;lt;math&amp;gt;OPT(n)=\begin{cases}&lt;br /&gt;
\ \;\, \{a_n\}\cup OPT(p(n))\\&lt;br /&gt;
\ \;\, OPT(a_{n-1})&lt;br /&gt;
\end{cases}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Um den höchstmöglichen Gewinn zu erzielen, wird &amp;lt;math&amp;gt;\{a_n\}\cup OPT(p(n))&amp;lt;/math&amp;gt; verwendet falls gilt:&lt;br /&gt;
:&amp;lt;math&amp;gt;w_n + Gewinn(OPT(p(n))\leq Gewinn(OPT(n-1))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Ansonsten wird &amp;lt;math&amp;gt;OPT(a_{n-1})&amp;lt;/math&amp;gt; angewandt.&lt;br /&gt;
&lt;br /&gt;
== Erinnerung: Intervalle ==&lt;br /&gt;
&lt;br /&gt;
[[Image:bild1.JPG]]&lt;br /&gt;
&lt;br /&gt;
Sortiere Intervalle nach aufsteigender &amp;lt;math&amp;gt; f_{i} &amp;lt;/math&amp;gt;:&lt;br /&gt;
:Gewinn &amp;lt;math&amp;gt;(a_{n})&amp;lt;/math&amp;gt; = max &amp;lt;math&amp;gt;(w_{n})&amp;lt;/math&amp;gt; + Gewinn (p(n)), Gewinn &amp;lt;math&amp;gt; (a_{n-1})&amp;lt;/math&amp;gt;&lt;br /&gt;
:Gewinn &amp;lt;math&amp;gt; (a_{n-1})&amp;lt;/math&amp;gt; = max (&amp;lt;math&amp;gt;w_{n-1}&amp;lt;/math&amp;gt; + Gewinn &amp;lt;math&amp;gt;(p(n-1)&amp;lt;/math&amp;gt;), Gewinn &amp;lt;math&amp;gt; (a_{n-2})&amp;lt;/math&amp;gt;&lt;br /&gt;
:usw.&lt;br /&gt;
&lt;br /&gt;
[[Image:bild2.JPG]]&lt;br /&gt;
&lt;br /&gt;
Bearbeite Teilprobleme in Reihenfolge der Sortierung&lt;br /&gt;
:=&amp;gt; azyklischer Graph&lt;br /&gt;
:(a2 hängt von a1 ab, a3 von a2, a4 von a2 und a3 usw.)&lt;br /&gt;
&lt;br /&gt;
Wie erkennt man, ob der Graph azyklisch ist, und wie fndet man die Reihenfolge?&lt;br /&gt;
* azyklischer, gerichteter Graph: „directed acyclic graph“ – DAG&lt;br /&gt;
* Ein Graph ist genau dann ein DAG, wenn es eine topologische Sortierung der Knoten gibt.&lt;br /&gt;
:Def.: Zeichne die Knoten so auf eine Gerade, dass alle Kanten nach rechts/in dieselbe Richtung zeigen&lt;br /&gt;
&lt;br /&gt;
[[Image:bild3.JPG]]&lt;br /&gt;
&lt;br /&gt;
:=&amp;gt; arbeite topologische Sortierung von rechts nach links ab.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel ===&lt;br /&gt;
Wie erklärt man einem zerstreuten Professor, wie er sich morgens anziehen soll?&lt;br /&gt;
&lt;br /&gt;
[[Image:bild4.JPG]]&lt;br /&gt;
&lt;br /&gt;
:=&amp;gt; Die topologische Sortierung ist hier nicht eindeutig, z.B. ist nicht klar, ob zuerst die Strümpfe angezogen werden sollen oder die Unterhose. Wann die Uhr angelegt werden soll, ist überhaupt nicht festgelegt. Mit dieser Beschreibung käme der arme Professor wohl kaum zurecht.&lt;br /&gt;
&lt;br /&gt;
[[Image:bild5.JPG]]&lt;br /&gt;
&lt;br /&gt;
== Zwei Algorithmen zum Finden der topologischen Sortierung ==&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus 1 ===&lt;br /&gt;
# Suche einen Knoten mit Eingangsgrad 0 (ohne eingehende Pfeile), =&amp;gt; in einem gerichteten azyklischen Graphen gibt es immer einen solchen Knoten&lt;br /&gt;
# Platziere diesen Knoten auf der Geraden (beliebig)&lt;br /&gt;
# Entferne den Knoten aus dem Graphen zusammen mit den ausgehenden Kanten&lt;br /&gt;
# Gehe zu 1., aber platziere in 2. immer rechts der vorhandenen Knoten (also der Knoten, die schon auf der Geraden vorhanden sind)&lt;br /&gt;
: =&amp;gt; Wenn noch Knoten übrig sind, aber keiner Eingangsgrad 0 hat, muss der Graph zyklisch sein.&lt;br /&gt;
&lt;br /&gt;
[[Image:bild6.JPG]]&lt;br /&gt;
&lt;br /&gt;
Bild: Ein zyklischer Graph&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus 2 ===&lt;br /&gt;
Verwende Tiefensuche, um die Finishing Time zu bestimmen&lt;br /&gt;
: =&amp;gt; zeichne Knoten nach abnehmender Finishing Time auf die Gerade&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Anwendung: Sequence Alignment / Edit Distance ==&lt;br /&gt;
&lt;br /&gt;
:gegeben: zwei Wörter (allgemein: beliebige Zeichenfolgen)&lt;br /&gt;
:gesucht: Wie kann man die Buchstaben am besten in Übereinstimmung bringen?&lt;br /&gt;
&lt;br /&gt;
:Beispiel: worte – norden&lt;br /&gt;
&lt;br /&gt;
[[Image:bild7.JPG]]&lt;br /&gt;
&lt;br /&gt;
Fälle:&lt;br /&gt;
# Matche a[i] mit b[j]. Falls a[i] == b[j], ist das gut. Andernfalls entstehen Kosten &amp;lt;math&amp;gt;K_{a[i], b[j]}&amp;lt;/math&amp;gt;&lt;br /&gt;
# Wir überspringen a[i] oder b[j], =&amp;gt; Kosten L&lt;br /&gt;
&lt;br /&gt;
:gesucht: Alignment mit minimalen Kosten&lt;br /&gt;
&lt;br /&gt;
[[Image:bild8.JPG]]&lt;br /&gt;
&lt;br /&gt;
Lösung:&lt;br /&gt;
:Suche kürzesten Pfad von links oben nach rechts unten (z.B. mit dem [[Graphen und Graphenalgorithmen#Algorithmus von Dijkstra|Algorithmus von Dijkstra]])&lt;br /&gt;
:In unserem Beispiel von oben:&lt;br /&gt;
&lt;br /&gt;
[[Image:bild9.JPG]]&lt;br /&gt;
&lt;br /&gt;
=== Problemlösung durch einen Entscheidungsbaum ===&lt;br /&gt;
Greedy Algorithm und dynamische Programmierung: &lt;br /&gt;
Transformationen des Problems, sodass '''nicht''' der ganze Entscheidungsbaum durchlaufen werden muss.&lt;br /&gt;
=&amp;gt; effizient&lt;br /&gt;
&lt;br /&gt;
* bei vielen Problemen ist keine Möglichkeit bekannt, das vollständige Durchlaufen des Entscheidungsbaumes zu vermeiden, z.B. [[Problem des Handlungsreisenden]] (TSP), 3-SAT&lt;br /&gt;
* Frage: Gibt es prinzipiell keinen effizienten Algorithmus, oder sind wir nur zu blöd?&lt;br /&gt;
* Derzeitiger Stand: viele dieser Probleme sind fundamental äquivalent „NP complete problems“ – „NP vollständig“ (NP = &amp;quot;nicht-deterministisch polynomiell&amp;quot;)&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2197</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2197"/>
				<updated>2008-07-08T20:23:12Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* strongCC */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= [http://de.wikipedia.org/wiki/Erfüllbarkeitsproblem_der_Aussagenlogik Erfüllbarkeitsproblem] =&lt;br /&gt;
&lt;br /&gt;
geg.: &lt;br /&gt;
* n Boolsche Variablen &amp;lt;math&amp;gt;x_i \in \{True,False\}&amp;lt;/math&amp;gt; und deren Negation &amp;lt;math&amp;gt;\neg x_i (i=1..n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Logischer Ausdruck in &amp;lt;math&amp;gt;x_i,\neg x_i&amp;lt;/math&amp;gt;&lt;br /&gt;
** zB &amp;lt;math&amp;gt;(x_1 \vee x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines logischen Ausdrucks(in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= &amp;amp;lt;CONJ&amp;amp;gt; | &amp;amp;lt;DISJ&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;TERM&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;TERM&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;TERM&amp;amp;gt; ::= ( &amp;amp;lt;EXPR&amp;amp;gt; ) | &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ges.: Eine Belegung der &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, so dass der gegebene Ausdruck &amp;quot;True&amp;quot; wird&lt;br /&gt;
&lt;br /&gt;
=== Naive Lösung ===&lt;br /&gt;
Probiere alle Bedingungen aus &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; Komplexität &amp;lt;math&amp;gt;\mathcal{O}(2^{n}) \!&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Im Allgemeinen ist das der effizienteste bekannte Algorithmus'''&lt;br /&gt;
&lt;br /&gt;
== '''Normalformen''' von logischen Ausdrücken ==&lt;br /&gt;
&lt;br /&gt;
=== k-Konjunktionen-Normalform(k-CNF) ===&lt;br /&gt;
&lt;br /&gt;
* ein &amp;quot;Literal&amp;quot; ist eine Variable &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; oder deren Negation&lt;br /&gt;
* jeweils ''k'' Literale werden mit &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; in einer '''Disjunktion''' verknüpft&lt;br /&gt;
* Disjunktionen werden mit &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; in einer '''Konjunktion''' verbunden&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in k-CNF(wieder in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;DISJ&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; ... &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; ) &amp;amp;lt;!-- k Literale --&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* 3-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2 \vee x_4) \wedge (x_2 \vee x_3 \vee \neg x_4) \wedge (\neg x_1 \vee x_4 \vee \neg x_5)&amp;lt;/math&amp;gt;&lt;br /&gt;
* 2-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* Jeder logische Ausdruck kann in polynomieller Zeit in 3-CNF umgewandelt werden&lt;br /&gt;
* Im Allgemeinen kann ein logischer Ausdruck nicht in 2-CNF umgeschrieben werden&lt;br /&gt;
&lt;br /&gt;
=== Implikationen-Normalform(INF) ===&lt;br /&gt;
&lt;br /&gt;
Konjunktionen von Implikationen:&lt;br /&gt;
* zB &amp;lt;math&amp;gt;(x_1 \to x_2) \wedge (x_2 \to \neg x_3) \wedge (x_4 \to x_3)&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in INF(you know the drill ;)):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;IMPL&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;IMPL&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;IMPL&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; )&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* jeder Ausdruck in 2-CNF kann in INF umgewandelt werden: &lt;br /&gt;
*: &amp;lt;math&amp;gt; (x_i \vee x_j) \Leftrightarrow (\neg x_i \to x_j) \wedge (\neg x_j \to x_i) &amp;lt;/math&amp;gt; ([http://de.wikipedia.org/wiki/Implikation#Eigenschaften_und_logische_Gesetze warum?])&lt;br /&gt;
&lt;br /&gt;
Außerdem kann jeder Ausdruck in INF als gerichteter Graph dargestellt werden&lt;br /&gt;
# Jede Variable und ihre Negation sind 1 Knoten(dh insgesamt 2 Knoten)&lt;br /&gt;
# Jede Implikation ist eine gerichtete Kante&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
=== postOrderTime ===&lt;br /&gt;
&lt;br /&gt;
    ## In einem Baum: besuche erst die Kinder, dann die Wurzel&lt;br /&gt;
    def postOrderTime(graph):&lt;br /&gt;
        visited = [None] * len(graph) &lt;br /&gt;
        def visit(node, count):&lt;br /&gt;
            #markiert, dass 'node' besucht wurde, aber noch nicht fertig ist&lt;br /&gt;
            visited[node] = -1&lt;br /&gt;
            for  neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor] is not None: continue&lt;br /&gt;
                count = visit(neighbor, count)&lt;br /&gt;
            visited[node] = count&lt;br /&gt;
            count += 1&lt;br /&gt;
            return count&lt;br /&gt;
        count = 0&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            if visited[node] is not None: continue&lt;br /&gt;
            count = visit(node, count)&lt;br /&gt;
        return visited&lt;br /&gt;
&lt;br /&gt;
=== transpose ===&lt;br /&gt;
&lt;br /&gt;
    ## Kehre die Richtung der Pfeile in einem Graphen um (tut nichts fuer ungerichtete Pfeile und Graphen).&lt;br /&gt;
    def transpose(graph):&lt;br /&gt;
        grapht = [[] for k in range(len(graph))]&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                grapht[neighbor].append(node)&lt;br /&gt;
        return grapht&lt;br /&gt;
&lt;br /&gt;
=== strongCC ===&lt;br /&gt;
&lt;br /&gt;
    ## Jede Komponente durch e. Ankerknoten repräsentiert&lt;br /&gt;
    ## Jedes SCC ist die Menge aller Knoten mit identischem Ankterknoten&lt;br /&gt;
    def strongCC(graph):&lt;br /&gt;
        # Prinzip: Tiefensuche mit absteigender Post-Order-Time&lt;br /&gt;
        postOrder = postOrderTime(graph)&lt;br /&gt;
        # ordered = [(knotenindex, POT), ...]&lt;br /&gt;
        ordered = zip(range(len(graph)), postOrder)&lt;br /&gt;
        ordered.sort(lambda x,y: -cmp(x[1],y[1]))&lt;br /&gt;
        &lt;br /&gt;
        grapht = transpose(graph)&lt;br /&gt;
        anchors = [None] * len(graph)&lt;br /&gt;
        def visit(node, anchor):&lt;br /&gt;
            if anchors[node] is not None: return&lt;br /&gt;
            anchors[node] = anchor&lt;br /&gt;
            for neighbor in grapht[node]:&lt;br /&gt;
                visit(neighbor, anchor)&lt;br /&gt;
        &lt;br /&gt;
        for node in ordered:&lt;br /&gt;
            visit(node[0], node[0])&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;x_i == \neg x_i&amp;lt;/math&amp;gt; ein Widerspruch ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit von INF: teste diese Eigenschaft für jede stark zusammenhängende Komponente&lt;br /&gt;
des Implikationengraphen&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2195</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2195"/>
				<updated>2008-07-08T20:20:23Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* transpose */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= [http://de.wikipedia.org/wiki/Erfüllbarkeitsproblem_der_Aussagenlogik Erfüllbarkeitsproblem] =&lt;br /&gt;
&lt;br /&gt;
geg.: &lt;br /&gt;
* n Boolsche Variablen &amp;lt;math&amp;gt;x_i \in \{True,False\}&amp;lt;/math&amp;gt; und deren Negation &amp;lt;math&amp;gt;\neg x_i (i=1..n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Logischer Ausdruck in &amp;lt;math&amp;gt;x_i,\neg x_i&amp;lt;/math&amp;gt;&lt;br /&gt;
** zB &amp;lt;math&amp;gt;(x_1 \vee x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines logischen Ausdrucks(in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= &amp;amp;lt;CONJ&amp;amp;gt; | &amp;amp;lt;DISJ&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;TERM&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;TERM&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;TERM&amp;amp;gt; ::= ( &amp;amp;lt;EXPR&amp;amp;gt; ) | &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ges.: Eine Belegung der &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, so dass der gegebene Ausdruck &amp;quot;True&amp;quot; wird&lt;br /&gt;
&lt;br /&gt;
=== Naive Lösung ===&lt;br /&gt;
Probiere alle Bedingungen aus &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; Komplexität &amp;lt;math&amp;gt;\mathcal{O}(2^{n}) \!&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Im Allgemeinen ist das der effizienteste bekannte Algorithmus'''&lt;br /&gt;
&lt;br /&gt;
== '''Normalformen''' von logischen Ausdrücken ==&lt;br /&gt;
&lt;br /&gt;
=== k-Konjunktionen-Normalform(k-CNF) ===&lt;br /&gt;
&lt;br /&gt;
* ein &amp;quot;Literal&amp;quot; ist eine Variable &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; oder deren Negation&lt;br /&gt;
* jeweils ''k'' Literale werden mit &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; in einer '''Disjunktion''' verknüpft&lt;br /&gt;
* Disjunktionen werden mit &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; in einer '''Konjunktion''' verbunden&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in k-CNF(wieder in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;DISJ&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; ... &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; ) &amp;amp;lt;!-- k Literale --&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* 3-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2 \vee x_4) \wedge (x_2 \vee x_3 \vee \neg x_4) \wedge (\neg x_1 \vee x_4 \vee \neg x_5)&amp;lt;/math&amp;gt;&lt;br /&gt;
* 2-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* Jeder logische Ausdruck kann in polynomieller Zeit in 3-CNF umgewandelt werden&lt;br /&gt;
* Im Allgemeinen kann ein logischer Ausdruck nicht in 2-CNF umgeschrieben werden&lt;br /&gt;
&lt;br /&gt;
=== Implikationen-Normalform(INF) ===&lt;br /&gt;
&lt;br /&gt;
Konjunktionen von Implikationen:&lt;br /&gt;
* zB &amp;lt;math&amp;gt;(x_1 \to x_2) \wedge (x_2 \to \neg x_3) \wedge (x_4 \to x_3)&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in INF(you know the drill ;)):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;IMPL&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;IMPL&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;IMPL&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; )&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* jeder Ausdruck in 2-CNF kann in INF umgewandelt werden: &lt;br /&gt;
*: &amp;lt;math&amp;gt; (x_i \vee x_j) \Leftrightarrow (\neg x_i \to x_j) \wedge (\neg x_j \to x_i) &amp;lt;/math&amp;gt; ([http://de.wikipedia.org/wiki/Implikation#Eigenschaften_und_logische_Gesetze warum?])&lt;br /&gt;
&lt;br /&gt;
Außerdem kann jeder Ausdruck in INF als gerichteter Graph dargestellt werden&lt;br /&gt;
# Jede Variable und ihre Negation sind 1 Knoten(dh insgesamt 2 Knoten)&lt;br /&gt;
# Jede Implikation ist eine gerichtete Kante&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
=== postOrderTime ===&lt;br /&gt;
&lt;br /&gt;
    ## In einem Baum: besuche erst die Kinder, dann die Wurzel&lt;br /&gt;
    def postOrderTime(graph):&lt;br /&gt;
        visited = [None] * len(graph) &lt;br /&gt;
        def visit(node, count):&lt;br /&gt;
            #markiert, dass 'node' besucht wurde, aber noch nicht fertig ist&lt;br /&gt;
            visited[node] = -1&lt;br /&gt;
            for  neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor] is not None: continue&lt;br /&gt;
                count = visit(neighbor, count)&lt;br /&gt;
            visited[node] = count&lt;br /&gt;
            count += 1&lt;br /&gt;
            return count&lt;br /&gt;
        count = 0&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            if visited[node] is not None: continue&lt;br /&gt;
            count = visit(node, count)&lt;br /&gt;
        return visited&lt;br /&gt;
&lt;br /&gt;
=== transpose ===&lt;br /&gt;
&lt;br /&gt;
    ## Kehre die Richtung der Pfeile in einem Graphen um (tut nichts fuer ungerichtete Pfeile und Graphen).&lt;br /&gt;
    def transpose(graph):&lt;br /&gt;
        grapht = [[] for k in range(len(graph))]&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                grapht[neighbor].append(node)&lt;br /&gt;
        return grapht&lt;br /&gt;
&lt;br /&gt;
=== strongCC ===&lt;br /&gt;
&lt;br /&gt;
    def strongCC(graph):&lt;br /&gt;
        # Prinzip: Tiefensuche mit absteigender Post-Order-Time&lt;br /&gt;
        postOrder = postOrderTime(graph)&lt;br /&gt;
        ordered = zip(range(len(graph)), postOrder)&lt;br /&gt;
        ordered.sort(lambda x,y: -cmp(x[1],y[1]))&lt;br /&gt;
        &lt;br /&gt;
        grapht = transpose(graph)&lt;br /&gt;
        anchors = [None] * len(graph)&lt;br /&gt;
        def visit(node, anchor):&lt;br /&gt;
            if anchors[node] is not None: return&lt;br /&gt;
            anchors[node] = anchor&lt;br /&gt;
            for neighbor in grapht[node]:&lt;br /&gt;
                visit(neighbor, anchor)&lt;br /&gt;
        &lt;br /&gt;
        for node in ordered:&lt;br /&gt;
            visit(node[0], node[0])&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;x_i == \neg x_i&amp;lt;/math&amp;gt; ein Widerspruch ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit von INF: teste diese Eigenschaft für jede stark zusammenhängende Komponente&lt;br /&gt;
des Implikationengraphen&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2194</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2194"/>
				<updated>2008-07-08T20:19:47Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* transpose */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= [http://de.wikipedia.org/wiki/Erfüllbarkeitsproblem_der_Aussagenlogik Erfüllbarkeitsproblem] =&lt;br /&gt;
&lt;br /&gt;
geg.: &lt;br /&gt;
* n Boolsche Variablen &amp;lt;math&amp;gt;x_i \in \{True,False\}&amp;lt;/math&amp;gt; und deren Negation &amp;lt;math&amp;gt;\neg x_i (i=1..n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Logischer Ausdruck in &amp;lt;math&amp;gt;x_i,\neg x_i&amp;lt;/math&amp;gt;&lt;br /&gt;
** zB &amp;lt;math&amp;gt;(x_1 \vee x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines logischen Ausdrucks(in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= &amp;amp;lt;CONJ&amp;amp;gt; | &amp;amp;lt;DISJ&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;TERM&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;TERM&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;TERM&amp;amp;gt; ::= ( &amp;amp;lt;EXPR&amp;amp;gt; ) | &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ges.: Eine Belegung der &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, so dass der gegebene Ausdruck &amp;quot;True&amp;quot; wird&lt;br /&gt;
&lt;br /&gt;
=== Naive Lösung ===&lt;br /&gt;
Probiere alle Bedingungen aus &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; Komplexität &amp;lt;math&amp;gt;\mathcal{O}(2^{n}) \!&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Im Allgemeinen ist das der effizienteste bekannte Algorithmus'''&lt;br /&gt;
&lt;br /&gt;
== '''Normalformen''' von logischen Ausdrücken ==&lt;br /&gt;
&lt;br /&gt;
=== k-Konjunktionen-Normalform(k-CNF) ===&lt;br /&gt;
&lt;br /&gt;
* ein &amp;quot;Literal&amp;quot; ist eine Variable &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; oder deren Negation&lt;br /&gt;
* jeweils ''k'' Literale werden mit &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; in einer '''Disjunktion''' verknüpft&lt;br /&gt;
* Disjunktionen werden mit &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; in einer '''Konjunktion''' verbunden&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in k-CNF(wieder in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;DISJ&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; ... &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; ) &amp;amp;lt;!-- k Literale --&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* 3-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2 \vee x_4) \wedge (x_2 \vee x_3 \vee \neg x_4) \wedge (\neg x_1 \vee x_4 \vee \neg x_5)&amp;lt;/math&amp;gt;&lt;br /&gt;
* 2-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* Jeder logische Ausdruck kann in polynomieller Zeit in 3-CNF umgewandelt werden&lt;br /&gt;
* Im Allgemeinen kann ein logischer Ausdruck nicht in 2-CNF umgeschrieben werden&lt;br /&gt;
&lt;br /&gt;
=== Implikationen-Normalform(INF) ===&lt;br /&gt;
&lt;br /&gt;
Konjunktionen von Implikationen:&lt;br /&gt;
* zB &amp;lt;math&amp;gt;(x_1 \to x_2) \wedge (x_2 \to \neg x_3) \wedge (x_4 \to x_3)&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in INF(you know the drill ;)):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;IMPL&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;IMPL&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;IMPL&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; )&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* jeder Ausdruck in 2-CNF kann in INF umgewandelt werden: &lt;br /&gt;
*: &amp;lt;math&amp;gt; (x_i \vee x_j) \Leftrightarrow (\neg x_i \to x_j) \wedge (\neg x_j \to x_i) &amp;lt;/math&amp;gt; ([http://de.wikipedia.org/wiki/Implikation#Eigenschaften_und_logische_Gesetze warum?])&lt;br /&gt;
&lt;br /&gt;
Außerdem kann jeder Ausdruck in INF als gerichteter Graph dargestellt werden&lt;br /&gt;
# Jede Variable und ihre Negation sind 1 Knoten(dh insgesamt 2 Knoten)&lt;br /&gt;
# Jede Implikation ist eine gerichtete Kante&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
=== postOrderTime ===&lt;br /&gt;
&lt;br /&gt;
    ## In einem Baum: besuche erst die Kinder, dann die Wurzel&lt;br /&gt;
    def postOrderTime(graph):&lt;br /&gt;
        visited = [None] * len(graph) &lt;br /&gt;
        def visit(node, count):&lt;br /&gt;
            #markiert, dass 'node' besucht wurde, aber noch nicht fertig ist&lt;br /&gt;
            visited[node] = -1&lt;br /&gt;
            for  neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor] is not None: continue&lt;br /&gt;
                count = visit(neighbor, count)&lt;br /&gt;
            visited[node] = count&lt;br /&gt;
            count += 1&lt;br /&gt;
            return count&lt;br /&gt;
        count = 0&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            if visited[node] is not None: continue&lt;br /&gt;
            count = visit(node, count)&lt;br /&gt;
        return visited&lt;br /&gt;
&lt;br /&gt;
=== transpose ===&lt;br /&gt;
&lt;br /&gt;
    ## Kehre die Richtung der Pfeile in einem Graphen um(tut nichts fuer ungerichtete Pfeile und Graphen).&lt;br /&gt;
    def transpose(graph):&lt;br /&gt;
        grapht = [[] for k in range(len(graph))]&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                grapht[neighbor].append(node)&lt;br /&gt;
        return grapht&lt;br /&gt;
&lt;br /&gt;
=== strongCC ===&lt;br /&gt;
&lt;br /&gt;
    def strongCC(graph):&lt;br /&gt;
        # Prinzip: Tiefensuche mit absteigender Post-Order-Time&lt;br /&gt;
        postOrder = postOrderTime(graph)&lt;br /&gt;
        ordered = zip(range(len(graph)), postOrder)&lt;br /&gt;
        ordered.sort(lambda x,y: -cmp(x[1],y[1]))&lt;br /&gt;
        &lt;br /&gt;
        grapht = transpose(graph)&lt;br /&gt;
        anchors = [None] * len(graph)&lt;br /&gt;
        def visit(node, anchor):&lt;br /&gt;
            if anchors[node] is not None: return&lt;br /&gt;
            anchors[node] = anchor&lt;br /&gt;
            for neighbor in grapht[node]:&lt;br /&gt;
                visit(neighbor, anchor)&lt;br /&gt;
        &lt;br /&gt;
        for node in ordered:&lt;br /&gt;
            visit(node[0], node[0])&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;x_i == \neg x_i&amp;lt;/math&amp;gt; ein Widerspruch ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit von INF: teste diese Eigenschaft für jede stark zusammenhängende Komponente&lt;br /&gt;
des Implikationengraphen&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2193</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2193"/>
				<updated>2008-07-08T20:18:26Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* postOrderTime */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= [http://de.wikipedia.org/wiki/Erfüllbarkeitsproblem_der_Aussagenlogik Erfüllbarkeitsproblem] =&lt;br /&gt;
&lt;br /&gt;
geg.: &lt;br /&gt;
* n Boolsche Variablen &amp;lt;math&amp;gt;x_i \in \{True,False\}&amp;lt;/math&amp;gt; und deren Negation &amp;lt;math&amp;gt;\neg x_i (i=1..n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Logischer Ausdruck in &amp;lt;math&amp;gt;x_i,\neg x_i&amp;lt;/math&amp;gt;&lt;br /&gt;
** zB &amp;lt;math&amp;gt;(x_1 \vee x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines logischen Ausdrucks(in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= &amp;amp;lt;CONJ&amp;amp;gt; | &amp;amp;lt;DISJ&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;TERM&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;TERM&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;TERM&amp;amp;gt; ::= ( &amp;amp;lt;EXPR&amp;amp;gt; ) | &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ges.: Eine Belegung der &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, so dass der gegebene Ausdruck &amp;quot;True&amp;quot; wird&lt;br /&gt;
&lt;br /&gt;
=== Naive Lösung ===&lt;br /&gt;
Probiere alle Bedingungen aus &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; Komplexität &amp;lt;math&amp;gt;\mathcal{O}(2^{n}) \!&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Im Allgemeinen ist das der effizienteste bekannte Algorithmus'''&lt;br /&gt;
&lt;br /&gt;
== '''Normalformen''' von logischen Ausdrücken ==&lt;br /&gt;
&lt;br /&gt;
=== k-Konjunktionen-Normalform(k-CNF) ===&lt;br /&gt;
&lt;br /&gt;
* ein &amp;quot;Literal&amp;quot; ist eine Variable &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; oder deren Negation&lt;br /&gt;
* jeweils ''k'' Literale werden mit &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; in einer '''Disjunktion''' verknüpft&lt;br /&gt;
* Disjunktionen werden mit &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; in einer '''Konjunktion''' verbunden&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in k-CNF(wieder in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;DISJ&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; ... &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; ) &amp;amp;lt;!-- k Literale --&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* 3-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2 \vee x_4) \wedge (x_2 \vee x_3 \vee \neg x_4) \wedge (\neg x_1 \vee x_4 \vee \neg x_5)&amp;lt;/math&amp;gt;&lt;br /&gt;
* 2-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* Jeder logische Ausdruck kann in polynomieller Zeit in 3-CNF umgewandelt werden&lt;br /&gt;
* Im Allgemeinen kann ein logischer Ausdruck nicht in 2-CNF umgeschrieben werden&lt;br /&gt;
&lt;br /&gt;
=== Implikationen-Normalform(INF) ===&lt;br /&gt;
&lt;br /&gt;
Konjunktionen von Implikationen:&lt;br /&gt;
* zB &amp;lt;math&amp;gt;(x_1 \to x_2) \wedge (x_2 \to \neg x_3) \wedge (x_4 \to x_3)&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in INF(you know the drill ;)):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;IMPL&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;IMPL&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;IMPL&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; )&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* jeder Ausdruck in 2-CNF kann in INF umgewandelt werden: &lt;br /&gt;
*: &amp;lt;math&amp;gt; (x_i \vee x_j) \Leftrightarrow (\neg x_i \to x_j) \wedge (\neg x_j \to x_i) &amp;lt;/math&amp;gt; ([http://de.wikipedia.org/wiki/Implikation#Eigenschaften_und_logische_Gesetze warum?])&lt;br /&gt;
&lt;br /&gt;
Außerdem kann jeder Ausdruck in INF als gerichteter Graph dargestellt werden&lt;br /&gt;
# Jede Variable und ihre Negation sind 1 Knoten(dh insgesamt 2 Knoten)&lt;br /&gt;
# Jede Implikation ist eine gerichtete Kante&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
=== postOrderTime ===&lt;br /&gt;
&lt;br /&gt;
    ## In einem Baum: besuche erst die Kinder, dann die Wurzel&lt;br /&gt;
    def postOrderTime(graph):&lt;br /&gt;
        visited = [None] * len(graph) &lt;br /&gt;
        def visit(node, count):&lt;br /&gt;
            #markiert, dass 'node' besucht wurde, aber noch nicht fertig ist&lt;br /&gt;
            visited[node] = -1&lt;br /&gt;
            for  neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor] is not None: continue&lt;br /&gt;
                count = visit(neighbor, count)&lt;br /&gt;
            visited[node] = count&lt;br /&gt;
            count += 1&lt;br /&gt;
            return count&lt;br /&gt;
        count = 0&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            if visited[node] is not None: continue&lt;br /&gt;
            count = visit(node, count)&lt;br /&gt;
        return visited&lt;br /&gt;
&lt;br /&gt;
=== transpose ===&lt;br /&gt;
&lt;br /&gt;
    def transpose(graph):&lt;br /&gt;
        grapht = [[] for k in range(len(graph))]&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                grapht[neighbor].append(node)&lt;br /&gt;
        return grapht&lt;br /&gt;
&lt;br /&gt;
=== strongCC ===&lt;br /&gt;
&lt;br /&gt;
    def strongCC(graph):&lt;br /&gt;
        # Prinzip: Tiefensuche mit absteigender Post-Order-Time&lt;br /&gt;
        postOrder = postOrderTime(graph)&lt;br /&gt;
        ordered = zip(range(len(graph)), postOrder)&lt;br /&gt;
        ordered.sort(lambda x,y: -cmp(x[1],y[1]))&lt;br /&gt;
        &lt;br /&gt;
        grapht = transpose(graph)&lt;br /&gt;
        anchors = [None] * len(graph)&lt;br /&gt;
        def visit(node, anchor):&lt;br /&gt;
            if anchors[node] is not None: return&lt;br /&gt;
            anchors[node] = anchor&lt;br /&gt;
            for neighbor in grapht[node]:&lt;br /&gt;
                visit(neighbor, anchor)&lt;br /&gt;
        &lt;br /&gt;
        for node in ordered:&lt;br /&gt;
            visit(node[0], node[0])&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;x_i == \neg x_i&amp;lt;/math&amp;gt; ein Widerspruch ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit von INF: teste diese Eigenschaft für jede stark zusammenhängende Komponente&lt;br /&gt;
des Implikationengraphen&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2192</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2192"/>
				<updated>2008-07-08T20:17:21Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* postOrderTime */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= [http://de.wikipedia.org/wiki/Erfüllbarkeitsproblem_der_Aussagenlogik Erfüllbarkeitsproblem] =&lt;br /&gt;
&lt;br /&gt;
geg.: &lt;br /&gt;
* n Boolsche Variablen &amp;lt;math&amp;gt;x_i \in \{True,False\}&amp;lt;/math&amp;gt; und deren Negation &amp;lt;math&amp;gt;\neg x_i (i=1..n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Logischer Ausdruck in &amp;lt;math&amp;gt;x_i,\neg x_i&amp;lt;/math&amp;gt;&lt;br /&gt;
** zB &amp;lt;math&amp;gt;(x_1 \vee x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines logischen Ausdrucks(in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= &amp;amp;lt;CONJ&amp;amp;gt; | &amp;amp;lt;DISJ&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;TERM&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;TERM&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;TERM&amp;amp;gt; ::= ( &amp;amp;lt;EXPR&amp;amp;gt; ) | &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ges.: Eine Belegung der &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, so dass der gegebene Ausdruck &amp;quot;True&amp;quot; wird&lt;br /&gt;
&lt;br /&gt;
=== Naive Lösung ===&lt;br /&gt;
Probiere alle Bedingungen aus &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; Komplexität &amp;lt;math&amp;gt;\mathcal{O}(2^{n}) \!&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Im Allgemeinen ist das der effizienteste bekannte Algorithmus'''&lt;br /&gt;
&lt;br /&gt;
== '''Normalformen''' von logischen Ausdrücken ==&lt;br /&gt;
&lt;br /&gt;
=== k-Konjunktionen-Normalform(k-CNF) ===&lt;br /&gt;
&lt;br /&gt;
* ein &amp;quot;Literal&amp;quot; ist eine Variable &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; oder deren Negation&lt;br /&gt;
* jeweils ''k'' Literale werden mit &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; in einer '''Disjunktion''' verknüpft&lt;br /&gt;
* Disjunktionen werden mit &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; in einer '''Konjunktion''' verbunden&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in k-CNF(wieder in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;DISJ&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; ... &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; ) &amp;amp;lt;!-- k Literale --&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* 3-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2 \vee x_4) \wedge (x_2 \vee x_3 \vee \neg x_4) \wedge (\neg x_1 \vee x_4 \vee \neg x_5)&amp;lt;/math&amp;gt;&lt;br /&gt;
* 2-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* Jeder logische Ausdruck kann in polynomieller Zeit in 3-CNF umgewandelt werden&lt;br /&gt;
* Im Allgemeinen kann ein logischer Ausdruck nicht in 2-CNF umgeschrieben werden&lt;br /&gt;
&lt;br /&gt;
=== Implikationen-Normalform(INF) ===&lt;br /&gt;
&lt;br /&gt;
Konjunktionen von Implikationen:&lt;br /&gt;
* zB &amp;lt;math&amp;gt;(x_1 \to x_2) \wedge (x_2 \to \neg x_3) \wedge (x_4 \to x_3)&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in INF(you know the drill ;)):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;IMPL&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;IMPL&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;IMPL&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; )&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* jeder Ausdruck in 2-CNF kann in INF umgewandelt werden: &lt;br /&gt;
*: &amp;lt;math&amp;gt; (x_i \vee x_j) \Leftrightarrow (\neg x_i \to x_j) \wedge (\neg x_j \to x_i) &amp;lt;/math&amp;gt; ([http://de.wikipedia.org/wiki/Implikation#Eigenschaften_und_logische_Gesetze warum?])&lt;br /&gt;
&lt;br /&gt;
Außerdem kann jeder Ausdruck in INF als gerichteter Graph dargestellt werden&lt;br /&gt;
# Jede Variable und ihre Negation sind 1 Knoten(dh insgesamt 2 Knoten)&lt;br /&gt;
# Jede Implikation ist eine gerichtete Kante&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
=== postOrderTime ===&lt;br /&gt;
&lt;br /&gt;
    ## In einem Baum: besuche erst die Kinder, dann die Wurzel&lt;br /&gt;
    def postOrderTime(graph):&lt;br /&gt;
        visited = [None] * len(graph) &lt;br /&gt;
        def visit(node, count):&lt;br /&gt;
            visited[node] = -1&lt;br /&gt;
            for  neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor] is not None: continue&lt;br /&gt;
                count = visit(neighbor, count)&lt;br /&gt;
            visited[node] = count&lt;br /&gt;
            count += 1&lt;br /&gt;
            return count&lt;br /&gt;
        count = 0&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            if visited[node] is not None: continue&lt;br /&gt;
            count = visit(node, count)&lt;br /&gt;
        return visited&lt;br /&gt;
&lt;br /&gt;
=== transpose ===&lt;br /&gt;
&lt;br /&gt;
    def transpose(graph):&lt;br /&gt;
        grapht = [[] for k in range(len(graph))]&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                grapht[neighbor].append(node)&lt;br /&gt;
        return grapht&lt;br /&gt;
&lt;br /&gt;
=== strongCC ===&lt;br /&gt;
&lt;br /&gt;
    def strongCC(graph):&lt;br /&gt;
        # Prinzip: Tiefensuche mit absteigender Post-Order-Time&lt;br /&gt;
        postOrder = postOrderTime(graph)&lt;br /&gt;
        ordered = zip(range(len(graph)), postOrder)&lt;br /&gt;
        ordered.sort(lambda x,y: -cmp(x[1],y[1]))&lt;br /&gt;
        &lt;br /&gt;
        grapht = transpose(graph)&lt;br /&gt;
        anchors = [None] * len(graph)&lt;br /&gt;
        def visit(node, anchor):&lt;br /&gt;
            if anchors[node] is not None: return&lt;br /&gt;
            anchors[node] = anchor&lt;br /&gt;
            for neighbor in grapht[node]:&lt;br /&gt;
                visit(neighbor, anchor)&lt;br /&gt;
        &lt;br /&gt;
        for node in ordered:&lt;br /&gt;
            visit(node[0], node[0])&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;x_i == \neg x_i&amp;lt;/math&amp;gt; ein Widerspruch ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit von INF: teste diese Eigenschaft für jede stark zusammenhängende Komponente&lt;br /&gt;
des Implikationengraphen&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2191</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2191"/>
				<updated>2008-07-08T20:13:05Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* strongSCC */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= [http://de.wikipedia.org/wiki/Erfüllbarkeitsproblem_der_Aussagenlogik Erfüllbarkeitsproblem] =&lt;br /&gt;
&lt;br /&gt;
geg.: &lt;br /&gt;
* n Boolsche Variablen &amp;lt;math&amp;gt;x_i \in \{True,False\}&amp;lt;/math&amp;gt; und deren Negation &amp;lt;math&amp;gt;\neg x_i (i=1..n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Logischer Ausdruck in &amp;lt;math&amp;gt;x_i,\neg x_i&amp;lt;/math&amp;gt;&lt;br /&gt;
** zB &amp;lt;math&amp;gt;(x_1 \vee x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines logischen Ausdrucks(in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= &amp;amp;lt;CONJ&amp;amp;gt; | &amp;amp;lt;DISJ&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;TERM&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;TERM&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;TERM&amp;amp;gt; ::= ( &amp;amp;lt;EXPR&amp;amp;gt; ) | &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ges.: Eine Belegung der &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, so dass der gegebene Ausdruck &amp;quot;True&amp;quot; wird&lt;br /&gt;
&lt;br /&gt;
=== Naive Lösung ===&lt;br /&gt;
Probiere alle Bedingungen aus &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; Komplexität &amp;lt;math&amp;gt;\mathcal{O}(2^{n}) \!&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Im Allgemeinen ist das der effizienteste bekannte Algorithmus'''&lt;br /&gt;
&lt;br /&gt;
== '''Normalformen''' von logischen Ausdrücken ==&lt;br /&gt;
&lt;br /&gt;
=== k-Konjunktionen-Normalform(k-CNF) ===&lt;br /&gt;
&lt;br /&gt;
* ein &amp;quot;Literal&amp;quot; ist eine Variable &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; oder deren Negation&lt;br /&gt;
* jeweils ''k'' Literale werden mit &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; in einer '''Disjunktion''' verknüpft&lt;br /&gt;
* Disjunktionen werden mit &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; in einer '''Konjunktion''' verbunden&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in k-CNF(wieder in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;DISJ&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; ... &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; ) &amp;amp;lt;!-- k Literale --&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* 3-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2 \vee x_4) \wedge (x_2 \vee x_3 \vee \neg x_4) \wedge (\neg x_1 \vee x_4 \vee \neg x_5)&amp;lt;/math&amp;gt;&lt;br /&gt;
* 2-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* Jeder logische Ausdruck kann in polynomieller Zeit in 3-CNF umgewandelt werden&lt;br /&gt;
* Im Allgemeinen kann ein logischer Ausdruck nicht in 2-CNF umgeschrieben werden&lt;br /&gt;
&lt;br /&gt;
=== Implikationen-Normalform(INF) ===&lt;br /&gt;
&lt;br /&gt;
Konjunktionen von Implikationen:&lt;br /&gt;
* zB &amp;lt;math&amp;gt;(x_1 \to x_2) \wedge (x_2 \to \neg x_3) \wedge (x_4 \to x_3)&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in INF(you know the drill ;)):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;IMPL&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;IMPL&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;IMPL&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; )&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* jeder Ausdruck in 2-CNF kann in INF umgewandelt werden: &lt;br /&gt;
*: &amp;lt;math&amp;gt; (x_i \vee x_j) \Leftrightarrow (\neg x_i \to x_j) \wedge (\neg x_j \to x_i) &amp;lt;/math&amp;gt; ([http://de.wikipedia.org/wiki/Implikation#Eigenschaften_und_logische_Gesetze warum?])&lt;br /&gt;
&lt;br /&gt;
Außerdem kann jeder Ausdruck in INF als gerichteter Graph dargestellt werden&lt;br /&gt;
# Jede Variable und ihre Negation sind 1 Knoten(dh insgesamt 2 Knoten)&lt;br /&gt;
# Jede Implikation ist eine gerichtete Kante&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
=== postOrderTime ===&lt;br /&gt;
&lt;br /&gt;
    def postOrderTime(graph):&lt;br /&gt;
        visited = [None] * len(graph) &lt;br /&gt;
        def visit(node, count):&lt;br /&gt;
            visited[node] = -1&lt;br /&gt;
            for  neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor] is not None: continue&lt;br /&gt;
                count = visit(neighbor, count)&lt;br /&gt;
            visited[node] = count&lt;br /&gt;
            count += 1&lt;br /&gt;
            return count&lt;br /&gt;
        count = 0&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            if visited[node] is not None: continue&lt;br /&gt;
            count = visit(node, count)&lt;br /&gt;
        return visited&lt;br /&gt;
&lt;br /&gt;
=== transpose ===&lt;br /&gt;
&lt;br /&gt;
    def transpose(graph):&lt;br /&gt;
        grapht = [[] for k in range(len(graph))]&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                grapht[neighbor].append(node)&lt;br /&gt;
        return grapht&lt;br /&gt;
&lt;br /&gt;
=== strongCC ===&lt;br /&gt;
&lt;br /&gt;
    def strongCC(graph):&lt;br /&gt;
        # Prinzip: Tiefensuche mit absteigender Post-Order-Time&lt;br /&gt;
        postOrder = postOrderTime(graph)&lt;br /&gt;
        ordered = zip(range(len(graph)), postOrder)&lt;br /&gt;
        ordered.sort(lambda x,y: -cmp(x[1],y[1]))&lt;br /&gt;
        &lt;br /&gt;
        grapht = transpose(graph)&lt;br /&gt;
        anchors = [None] * len(graph)&lt;br /&gt;
        def visit(node, anchor):&lt;br /&gt;
            if anchors[node] is not None: return&lt;br /&gt;
            anchors[node] = anchor&lt;br /&gt;
            for neighbor in grapht[node]:&lt;br /&gt;
                visit(neighbor, anchor)&lt;br /&gt;
        &lt;br /&gt;
        for node in ordered:&lt;br /&gt;
            visit(node[0], node[0])&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;x_i == \neg x_i&amp;lt;/math&amp;gt; ein Widerspruch ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit von INF: teste diese Eigenschaft für jede stark zusammenhängende Komponente&lt;br /&gt;
des Implikationengraphen&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2188</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2188"/>
				<updated>2008-07-08T19:58:48Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* Erfüllbarkeitsproblem */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= [http://de.wikipedia.org/wiki/Erfüllbarkeitsproblem_der_Aussagenlogik Erfüllbarkeitsproblem] =&lt;br /&gt;
&lt;br /&gt;
geg.: &lt;br /&gt;
* n Boolsche Variablen &amp;lt;math&amp;gt;x_i \in \{True,False\}&amp;lt;/math&amp;gt; und deren Negation &amp;lt;math&amp;gt;\neg x_i (i=1..n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Logischer Ausdruck in &amp;lt;math&amp;gt;x_i,\neg x_i&amp;lt;/math&amp;gt;&lt;br /&gt;
** zB &amp;lt;math&amp;gt;(x_1 \vee x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines logischen Ausdrucks(in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= &amp;amp;lt;CONJ&amp;amp;gt; | &amp;amp;lt;DISJ&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;TERM&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;TERM&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;TERM&amp;amp;gt; ::= ( &amp;amp;lt;EXPR&amp;amp;gt; ) | &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ges.: Eine Belegung der &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, so dass der gegebene Ausdruck &amp;quot;True&amp;quot; wird&lt;br /&gt;
&lt;br /&gt;
=== Naive Lösung ===&lt;br /&gt;
Probiere alle Bedingungen aus &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; Komplexität &amp;lt;math&amp;gt;\mathcal{O}(2^{n}) \!&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Im Allgemeinen ist das der effizienteste bekannte Algorithmus'''&lt;br /&gt;
&lt;br /&gt;
== '''Normalformen''' von logischen Ausdrücken ==&lt;br /&gt;
&lt;br /&gt;
=== k-Konjunktionen-Normalform(k-CNF) ===&lt;br /&gt;
&lt;br /&gt;
* ein &amp;quot;Literal&amp;quot; ist eine Variable &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; oder deren Negation&lt;br /&gt;
* jeweils ''k'' Literale werden mit &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; in einer '''Disjunktion''' verknüpft&lt;br /&gt;
* Disjunktionen werden mit &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; in einer '''Konjunktion''' verbunden&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in k-CNF(wieder in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;DISJ&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; ... &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; ) &amp;amp;lt;!-- k Literale --&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* 3-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2 \vee x_4) \wedge (x_2 \vee x_3 \vee \neg x_4) \wedge (\neg x_1 \vee x_4 \vee \neg x_5)&amp;lt;/math&amp;gt;&lt;br /&gt;
* 2-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* Jeder logische Ausdruck kann in polynomieller Zeit in 3-CNF umgewandelt werden&lt;br /&gt;
* Im Allgemeinen kann ein logischer Ausdruck nicht in 2-CNF umgeschrieben werden&lt;br /&gt;
&lt;br /&gt;
=== Implikationen-Normalform(INF) ===&lt;br /&gt;
&lt;br /&gt;
Konjunktionen von Implikationen:&lt;br /&gt;
* zB &amp;lt;math&amp;gt;(x_1 \to x_2) \wedge (x_2 \to \neg x_3) \wedge (x_4 \to x_3)&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in INF(you know the drill ;)):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;IMPL&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;IMPL&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;IMPL&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; )&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* jeder Ausdruck in 2-CNF kann in INF umgewandelt werden: &lt;br /&gt;
*: &amp;lt;math&amp;gt; (x_i \vee x_j) \Leftrightarrow (\neg x_i \to x_j) \wedge (\neg x_j \to x_i) &amp;lt;/math&amp;gt; ([http://de.wikipedia.org/wiki/Implikation#Eigenschaften_und_logische_Gesetze warum?])&lt;br /&gt;
&lt;br /&gt;
Außerdem kann jeder Ausdruck in INF als gerichteter Graph dargestellt werden&lt;br /&gt;
# Jede Variable und ihre Negation sind 1 Knoten(dh insgesamt 2 Knoten)&lt;br /&gt;
# Jede Implikation ist eine gerichtete Kante&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
=== postOrderTime ===&lt;br /&gt;
&lt;br /&gt;
    def postOrderTime(graph):&lt;br /&gt;
        visited = [None] * len(graph) &lt;br /&gt;
        def visit(node, count):&lt;br /&gt;
            visited[node] = -1&lt;br /&gt;
            for  neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor] is not None: continue&lt;br /&gt;
                count = visit(neighbor, count)&lt;br /&gt;
            visited[node] = count&lt;br /&gt;
            count += 1&lt;br /&gt;
            return count&lt;br /&gt;
        count = 0&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            if visited[node] is not None: continue&lt;br /&gt;
            count = visit(node, count)&lt;br /&gt;
        return visited&lt;br /&gt;
&lt;br /&gt;
=== transpose ===&lt;br /&gt;
&lt;br /&gt;
    def transpose(graph):&lt;br /&gt;
        grapht = [[] for k in range(len(graph))]&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                grapht[neighbor].append(node)&lt;br /&gt;
        return grapht&lt;br /&gt;
&lt;br /&gt;
=== strongSCC ===&lt;br /&gt;
&lt;br /&gt;
    def strongSCC(graph):&lt;br /&gt;
        # Prinzip: Tiefensuche mit absteigender Post-Order-Time&lt;br /&gt;
        postOrder = postOrderTime(graph)&lt;br /&gt;
        ordered = zip(range(len(graph)), postOrder)&lt;br /&gt;
        ordered.sort(lambda x,y: -cmp(x[1],y[1]))&lt;br /&gt;
        &lt;br /&gt;
        grapht = transpose(graph)&lt;br /&gt;
        anchors = [None] * len(graph)&lt;br /&gt;
        def visit(node, anchor):&lt;br /&gt;
            if anchors[node] is not None: return&lt;br /&gt;
            anchors[node] = anchor&lt;br /&gt;
            for neighbor in grapht[node]:&lt;br /&gt;
                visit(neighbor, anchor)&lt;br /&gt;
        &lt;br /&gt;
        for node in ordered:&lt;br /&gt;
            visit(node[0], node[0])&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;x_i == \neg x_i&amp;lt;/math&amp;gt; ein Widerspruch ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit von INF: teste diese Eigenschaft für jede stark zusammenhängende Komponente&lt;br /&gt;
des Implikationengraphen&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2187</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2187"/>
				<updated>2008-07-08T19:57:54Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* Anwendung auf 2-SAT Problem */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= [http://de.wikipedia.org/wiki/Erfüllbarkeitsproblem_der_Aussagenlogik Erfüllbarkeitsproblem] =&lt;br /&gt;
&lt;br /&gt;
geg.: &lt;br /&gt;
* n Boolsche Variablen &amp;lt;math&amp;gt;x_i \in \{True,False\}&amp;lt;/math&amp;gt; und deren Negation &amp;lt;math&amp;gt;\neg x_i (i=1..n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Logischer Ausdruck in &amp;lt;math&amp;gt;x_i,\neg x_i&amp;lt;/math&amp;gt;&lt;br /&gt;
** zB &amp;lt;math&amp;gt;(x_1 \vee x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines logischen Ausdrucks(in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= &amp;amp;lt;CONJ&amp;amp;gt; | &amp;amp;lt;DISJ&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;TERM&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;TERM&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;TERM&amp;amp;gt; ::= ( &amp;amp;lt;EXPR&amp;amp;gt; ) | &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ges.: Eine Belegung der &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, so dass der gegebene Ausdruck &amp;quot;True&amp;quot; wird&lt;br /&gt;
&lt;br /&gt;
=== Naive Lösung ===&lt;br /&gt;
Probiere alle Bedingungen aus &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; Komplexität &amp;lt;math&amp;gt;\mathcal{O}(2^{n}) \!&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Im Allgemeinen ist das der effizienteste bekannte Algorithmus'''&lt;br /&gt;
&lt;br /&gt;
== '''Normalformen''' von logischen Ausdrücken ==&lt;br /&gt;
&lt;br /&gt;
=== k-Konjunktionen-Normalform(k-CNF) ===&lt;br /&gt;
&lt;br /&gt;
* ein &amp;quot;Literal&amp;quot; ist eine Variable &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; oder deren Negation&lt;br /&gt;
* jeweils ''k'' Literale werden mit &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; in einer '''Disjunktion''' verknüpft&lt;br /&gt;
* Disjunktionen werden mit &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; in einer '''Konjunktion''' verbunden&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in k-CNF(wieder in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;DISJ&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; ... &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; ) &amp;amp;lt;!-- k Literale --&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* 3-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2 \vee x_4) \wedge (x_2 \vee x_3 \vee \neg x_4) \wedge (\neg x_1 \vee x_4 \vee \neg x_5)&amp;lt;/math&amp;gt;&lt;br /&gt;
* 2-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* Jeder logische Ausdruck kann in polynomieller Zeit in 3-CNF umgewandelt werden&lt;br /&gt;
* Im Allgemeinen kann ein logischer Ausdruck nicht in 2-CNF umgeschrieben werden&lt;br /&gt;
&lt;br /&gt;
=== Implikationen-Normalform(INF) ===&lt;br /&gt;
&lt;br /&gt;
Konjunktionen von Implikationen:&lt;br /&gt;
* zB &amp;lt;math&amp;gt;(x_1 \to x_2) \wedge (x_2 \to \neg x_3) \wedge (x_4 \to x_3)&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in INF(you know the drill ;)):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;IMPL&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;IMPL&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;IMPL&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; )&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* jeder Ausdruck in 2-CNF kann in INF umgewandelt werden: &lt;br /&gt;
*: &amp;lt;math&amp;gt; (x_i \vee x_j) \Leftrightarrow (\neg x_i \to x_j) \wedge (\neg x_j \to x_i) &amp;lt;/math&amp;gt; ([http://de.wikipedia.org/wiki/Implikation#Eigenschaften_und_logische_Gesetze warum?])&lt;br /&gt;
&lt;br /&gt;
Außerdem kann jeder Ausdruck in INF als gerichteter Graph dargestellt werden&lt;br /&gt;
# Jede Variable und ihre Negation sind 1 Knoten(dh insgesamt 2 Knoten)&lt;br /&gt;
# Jede Implikation ist eine gerichtete Kante&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
&lt;br /&gt;
=== postOrderTime ===&lt;br /&gt;
&lt;br /&gt;
    def postOrderTime(graph):&lt;br /&gt;
        visited = [None] * len(graph) &lt;br /&gt;
        def visit(node, count):&lt;br /&gt;
            visited[node] = -1&lt;br /&gt;
            for  neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor] is not None: continue&lt;br /&gt;
                count = visit(neighbor, count)&lt;br /&gt;
            visited[node] = count&lt;br /&gt;
            count += 1&lt;br /&gt;
            return count&lt;br /&gt;
        count = 0&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            if visited[node] is not None: continue&lt;br /&gt;
            count = visit(node, count)&lt;br /&gt;
        return visited&lt;br /&gt;
&lt;br /&gt;
=== transpose ===&lt;br /&gt;
&lt;br /&gt;
    def transpose(graph):&lt;br /&gt;
        grapht = [[] for k in range(len(graph))]&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                grapht[neighbor].append(node)&lt;br /&gt;
        return grapht&lt;br /&gt;
&lt;br /&gt;
=== strongSCC ===&lt;br /&gt;
&lt;br /&gt;
    def strongSCC(graph):&lt;br /&gt;
        # Prinzip: Tiefensuche mit absteigender Post-Order-Time&lt;br /&gt;
        postOrder = postOrderTime(graph)&lt;br /&gt;
        ordered = zip(range(len(graph)), postOrder)&lt;br /&gt;
        ordered.sort(lambda x,y: -cmp(x[1],y[1]))&lt;br /&gt;
        &lt;br /&gt;
        grapht = transpose(graph)&lt;br /&gt;
        anchors = [None] * len(graph)&lt;br /&gt;
        def visit(node, anchor):&lt;br /&gt;
            if anchors[node] is not None: return&lt;br /&gt;
            anchors[node] = anchor&lt;br /&gt;
            for neighbor in grapht[node]:&lt;br /&gt;
                visit(neighbor, anchor)&lt;br /&gt;
        &lt;br /&gt;
        for node in ordered:&lt;br /&gt;
            visit(node[0], node[0])&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;x_i == \neg x_i&amp;lt;/math&amp;gt; ein Widerspruch ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit von INF: teste diese Eigenschaft für jede stark zusammenhängende Komponente&lt;br /&gt;
des Implikationengraphen&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2186</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2186"/>
				<updated>2008-07-08T19:52:27Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* strongSCC */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= [http://de.wikipedia.org/wiki/Erfüllbarkeitsproblem_der_Aussagenlogik Erfüllbarkeitsproblem] =&lt;br /&gt;
&lt;br /&gt;
geg.: &lt;br /&gt;
* n Boolsche Variablen &amp;lt;math&amp;gt;x_i \in \{True,False\}&amp;lt;/math&amp;gt; und deren Negation &amp;lt;math&amp;gt;\neg x_i (i=1..n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Logischer Ausdruck in &amp;lt;math&amp;gt;x_i,\neg x_i&amp;lt;/math&amp;gt;&lt;br /&gt;
** zB &amp;lt;math&amp;gt;(x_1 \vee x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines logischen Ausdrucks(in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= &amp;amp;lt;CONJ&amp;amp;gt; | &amp;amp;lt;DISJ&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;TERM&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;TERM&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;TERM&amp;amp;gt; ::= ( &amp;amp;lt;EXPR&amp;amp;gt; ) | &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ges.: Eine Belegung der &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, so dass der gegebene Ausdruck &amp;quot;True&amp;quot; wird&lt;br /&gt;
&lt;br /&gt;
=== Naive Lösung ===&lt;br /&gt;
Probiere alle Bedingungen aus &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; Komplexität &amp;lt;math&amp;gt;\mathcal{O}(2^{n}) \!&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Im Allgemeinen ist das der effizienteste bekannte Algorithmus'''&lt;br /&gt;
&lt;br /&gt;
== '''Normalformen''' von logischen Ausdrücken ==&lt;br /&gt;
&lt;br /&gt;
=== k-Konjunktionen-Normalform(k-CNF) ===&lt;br /&gt;
&lt;br /&gt;
* ein &amp;quot;Literal&amp;quot; ist eine Variable &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; oder deren Negation&lt;br /&gt;
* jeweils ''k'' Literale werden mit &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; in einer '''Disjunktion''' verknüpft&lt;br /&gt;
* Disjunktionen werden mit &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; in einer '''Konjunktion''' verbunden&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in k-CNF(wieder in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;DISJ&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; ... &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; ) &amp;amp;lt;!-- k Literale --&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* 3-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2 \vee x_4) \wedge (x_2 \vee x_3 \vee \neg x_4) \wedge (\neg x_1 \vee x_4 \vee \neg x_5)&amp;lt;/math&amp;gt;&lt;br /&gt;
* 2-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* Jeder logische Ausdruck kann in polynomieller Zeit in 3-CNF umgewandelt werden&lt;br /&gt;
* Im Allgemeinen kann ein logischer Ausdruck nicht in 2-CNF umgeschrieben werden&lt;br /&gt;
&lt;br /&gt;
=== Implikationen-Normalform(INF) ===&lt;br /&gt;
&lt;br /&gt;
Konjunktionen von Implikationen:&lt;br /&gt;
* zB &amp;lt;math&amp;gt;(x_1 \to x_2) \wedge (x_2 \to \neg x_3) \wedge (x_4 \to x_3)&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in INF(you know the drill ;)):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;IMPL&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;IMPL&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;IMPL&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; )&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* jeder Ausdruck in 2-CNF kann in INF umgewandelt werden: &lt;br /&gt;
*: &amp;lt;math&amp;gt; (x_i \vee x_j) \Leftrightarrow (\neg x_i \to x_j) \wedge (\neg x_j \to x_i) &amp;lt;/math&amp;gt; ([http://de.wikipedia.org/wiki/Implikation#Eigenschaften_und_logische_Gesetze warum?])&lt;br /&gt;
&lt;br /&gt;
Außerdem kann jeder Ausdruck in INF als gerichteter Graph dargestellt werden&lt;br /&gt;
# Jede Variable und ihre Negation sind 1 Knoten(dh insgesamt 2 Knoten)&lt;br /&gt;
# Jede Implikation ist eine gerichtete Kante&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
&lt;br /&gt;
=== postOrderTime ===&lt;br /&gt;
&lt;br /&gt;
    def postOrderTime(graph):&lt;br /&gt;
        visited = [None] * len(graph) &lt;br /&gt;
        def visit(node, count):&lt;br /&gt;
            visited[node] = -1&lt;br /&gt;
            for  neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor] is not None: continue&lt;br /&gt;
                count = visit(neighbor, count)&lt;br /&gt;
            visited[node] = count&lt;br /&gt;
            count += 1&lt;br /&gt;
            return count&lt;br /&gt;
        count = 0&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            if visited[node] is not None: continue&lt;br /&gt;
            count = visit(node, count)&lt;br /&gt;
        return visited&lt;br /&gt;
&lt;br /&gt;
=== transpose ===&lt;br /&gt;
&lt;br /&gt;
    def transpose(graph):&lt;br /&gt;
        grapht = [[] for k in range(len(graph))]&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                grapht[neighbor].append(node)&lt;br /&gt;
        return grapht&lt;br /&gt;
&lt;br /&gt;
=== strongSCC ===&lt;br /&gt;
&lt;br /&gt;
    def strongSCC(graph):&lt;br /&gt;
        # Prinzip: Tiefensuche mit absteigender Post-Order-Time&lt;br /&gt;
        postOrder = postOrderTime(graph)&lt;br /&gt;
        ordered = zip(range(len(graph)), postOrder)&lt;br /&gt;
        ordered.sort(lambda x,y: -cmp(x[1],y[1]))&lt;br /&gt;
        &lt;br /&gt;
        grapht = transpose(graph)&lt;br /&gt;
        anchors = [None] * len(graph)&lt;br /&gt;
        def visit(node, anchor):&lt;br /&gt;
            if anchors[node] is not None: return&lt;br /&gt;
            anchors[node] = anchor&lt;br /&gt;
            for neighbor in grapht[node]:&lt;br /&gt;
                visit(neighbor, anchor)&lt;br /&gt;
        &lt;br /&gt;
        for node in ordered:&lt;br /&gt;
            visit(node[0], node[0])&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;\rightarrow \; x_i == \neg x_i&amp;lt;/math&amp;gt; unmöglich ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit: teste die Eigenschaft&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2185</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2185"/>
				<updated>2008-07-08T19:51:42Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= [http://de.wikipedia.org/wiki/Erfüllbarkeitsproblem_der_Aussagenlogik Erfüllbarkeitsproblem] =&lt;br /&gt;
&lt;br /&gt;
geg.: &lt;br /&gt;
* n Boolsche Variablen &amp;lt;math&amp;gt;x_i \in \{True,False\}&amp;lt;/math&amp;gt; und deren Negation &amp;lt;math&amp;gt;\neg x_i (i=1..n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Logischer Ausdruck in &amp;lt;math&amp;gt;x_i,\neg x_i&amp;lt;/math&amp;gt;&lt;br /&gt;
** zB &amp;lt;math&amp;gt;(x_1 \vee x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines logischen Ausdrucks(in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= &amp;amp;lt;CONJ&amp;amp;gt; | &amp;amp;lt;DISJ&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;TERM&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;TERM&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;TERM&amp;amp;gt; ::= ( &amp;amp;lt;EXPR&amp;amp;gt; ) | &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ges.: Eine Belegung der &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, so dass der gegebene Ausdruck &amp;quot;True&amp;quot; wird&lt;br /&gt;
&lt;br /&gt;
=== Naive Lösung ===&lt;br /&gt;
Probiere alle Bedingungen aus &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; Komplexität &amp;lt;math&amp;gt;\mathcal{O}(2^{n}) \!&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Im Allgemeinen ist das der effizienteste bekannte Algorithmus'''&lt;br /&gt;
&lt;br /&gt;
== '''Normalformen''' von logischen Ausdrücken ==&lt;br /&gt;
&lt;br /&gt;
=== k-Konjunktionen-Normalform(k-CNF) ===&lt;br /&gt;
&lt;br /&gt;
* ein &amp;quot;Literal&amp;quot; ist eine Variable &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; oder deren Negation&lt;br /&gt;
* jeweils ''k'' Literale werden mit &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; in einer '''Disjunktion''' verknüpft&lt;br /&gt;
* Disjunktionen werden mit &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; in einer '''Konjunktion''' verbunden&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in k-CNF(wieder in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;DISJ&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; ... &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; ) &amp;amp;lt;!-- k Literale --&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* 3-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2 \vee x_4) \wedge (x_2 \vee x_3 \vee \neg x_4) \wedge (\neg x_1 \vee x_4 \vee \neg x_5)&amp;lt;/math&amp;gt;&lt;br /&gt;
* 2-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* Jeder logische Ausdruck kann in polynomieller Zeit in 3-CNF umgewandelt werden&lt;br /&gt;
* Im Allgemeinen kann ein logischer Ausdruck nicht in 2-CNF umgeschrieben werden&lt;br /&gt;
&lt;br /&gt;
=== Implikationen-Normalform(INF) ===&lt;br /&gt;
&lt;br /&gt;
Konjunktionen von Implikationen:&lt;br /&gt;
* zB &amp;lt;math&amp;gt;(x_1 \to x_2) \wedge (x_2 \to \neg x_3) \wedge (x_4 \to x_3)&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in INF(you know the drill ;)):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;IMPL&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;IMPL&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;IMPL&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; )&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* jeder Ausdruck in 2-CNF kann in INF umgewandelt werden: &lt;br /&gt;
*: &amp;lt;math&amp;gt; (x_i \vee x_j) \Leftrightarrow (\neg x_i \to x_j) \wedge (\neg x_j \to x_i) &amp;lt;/math&amp;gt; ([http://de.wikipedia.org/wiki/Implikation#Eigenschaften_und_logische_Gesetze warum?])&lt;br /&gt;
&lt;br /&gt;
Außerdem kann jeder Ausdruck in INF als gerichteter Graph dargestellt werden&lt;br /&gt;
# Jede Variable und ihre Negation sind 1 Knoten(dh insgesamt 2 Knoten)&lt;br /&gt;
# Jede Implikation ist eine gerichtete Kante&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
&lt;br /&gt;
=== postOrderTime ===&lt;br /&gt;
&lt;br /&gt;
    def postOrderTime(graph):&lt;br /&gt;
        visited = [None] * len(graph) &lt;br /&gt;
        def visit(node, count):&lt;br /&gt;
            visited[node] = -1&lt;br /&gt;
            for  neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor] is not None: continue&lt;br /&gt;
                count = visit(neighbor, count)&lt;br /&gt;
            visited[node] = count&lt;br /&gt;
            count += 1&lt;br /&gt;
            return count&lt;br /&gt;
        count = 0&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            if visited[node] is not None: continue&lt;br /&gt;
            count = visit(node, count)&lt;br /&gt;
        return visited&lt;br /&gt;
&lt;br /&gt;
=== transpose ===&lt;br /&gt;
&lt;br /&gt;
    def transpose(graph):&lt;br /&gt;
        grapht = [[] for k in range(len(graph))]&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                grapht[neighbor].append(node)&lt;br /&gt;
        return grapht&lt;br /&gt;
&lt;br /&gt;
=== strongSCC ===&lt;br /&gt;
&lt;br /&gt;
    def strongSCC(graph):&lt;br /&gt;
        # Prinzip: Tiefensuche mit absteigender Post-Order-Time&lt;br /&gt;
        postOrder = postOrderTime(graph)&lt;br /&gt;
        ordered = zip(range(len(graph)), postOrder)&lt;br /&gt;
        ordered.sort(lambda x,y: -cmp(x[1],y[1]))&lt;br /&gt;
        &lt;br /&gt;
        grapht = transpose(graph)&lt;br /&gt;
        anchors = [None] * len(graph)&lt;br /&gt;
&lt;br /&gt;
        def visit(node, anchor):&lt;br /&gt;
            if anchors[node] is not None: return&lt;br /&gt;
            anchors[node] = anchor&lt;br /&gt;
            for neighbor in grapht[node]:&lt;br /&gt;
                visit(neighbor, anchor)&lt;br /&gt;
        &lt;br /&gt;
        for node in ordered:&lt;br /&gt;
            visit(node[0], node[0])&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;\rightarrow \; x_i == \neg x_i&amp;lt;/math&amp;gt; unmöglich ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit: teste die Eigenschaft&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2184</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2184"/>
				<updated>2008-07-08T19:41:50Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= [http://de.wikipedia.org/wiki/Erfüllbarkeitsproblem_der_Aussagenlogik Erfüllbarkeitsproblem] =&lt;br /&gt;
&lt;br /&gt;
geg.: &lt;br /&gt;
* n Boolsche Variablen &amp;lt;math&amp;gt;x_i \in \{True,False\}&amp;lt;/math&amp;gt; und deren Negation &amp;lt;math&amp;gt;\neg x_i (i=1..n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Logischer Ausdruck in &amp;lt;math&amp;gt;x_i,\neg x_i&amp;lt;/math&amp;gt;&lt;br /&gt;
** zB &amp;lt;math&amp;gt;(x_1 \vee x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines logischen Ausdrucks(in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= &amp;amp;lt;CONJ&amp;amp;gt; | &amp;amp;lt;DISJ&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;TERM&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;TERM&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;TERM&amp;amp;gt; ::= ( &amp;amp;lt;EXPR&amp;amp;gt; ) | &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ges.: Eine Belegung der &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, so dass der gegebene Ausdruck &amp;quot;True&amp;quot; wird&lt;br /&gt;
&lt;br /&gt;
=== Naive Lösung ===&lt;br /&gt;
Probiere alle Bedingungen aus &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; Komplexität &amp;lt;math&amp;gt;\mathcal{O}(2^{n}) \!&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Im Allgemeinen ist das der effizienteste bekannte Algorithmus'''&lt;br /&gt;
&lt;br /&gt;
== '''Normalformen''' von logischen Ausdrücken ==&lt;br /&gt;
&lt;br /&gt;
=== k-Konjunktionen-Normalform(k-CNF) ===&lt;br /&gt;
&lt;br /&gt;
* ein &amp;quot;Literal&amp;quot; ist eine Variable &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; oder deren Negation&lt;br /&gt;
* jeweils ''k'' Literale werden mit &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; in einer '''Disjunktion''' verknüpft&lt;br /&gt;
* Disjunktionen werden mit &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; in einer '''Konjunktion''' verbunden&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in k-CNF(wieder in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;DISJ&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; ... &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; ) &amp;amp;lt;!-- k Literale --&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* 3-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2 \vee x_4) \wedge (x_2 \vee x_3 \vee \neg x_4) \wedge (\neg x_1 \vee x_4 \vee \neg x_5)&amp;lt;/math&amp;gt;&lt;br /&gt;
* 2-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* Jeder logische Ausdruck kann in polynomieller Zeit in 3-CNF umgewandelt werden&lt;br /&gt;
* Im Allgemeinen kann ein logischer Ausdruck nicht in 2-CNF umgeschrieben werden&lt;br /&gt;
&lt;br /&gt;
=== Implikationen-Normalform(INF) ===&lt;br /&gt;
&lt;br /&gt;
Konjunktionen von Implikationen:&lt;br /&gt;
* zB &amp;lt;math&amp;gt;(x_1 \to x_2) \wedge (x_2 \to \neg x_3) \wedge (x_4 \to x_3)&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in INF(you know the drill ;)):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;IMPL&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;IMPL&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;IMPL&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; )&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* jeder Ausdruck in 2-CNF kann in INF umgewandelt werden: &lt;br /&gt;
*: &amp;lt;math&amp;gt; (x_i \vee x_j) \Leftrightarrow (\neg x_i \to x_j) \wedge (\neg x_j \to x_i) &amp;lt;/math&amp;gt; ([http://de.wikipedia.org/wiki/Implikation#Eigenschaften_und_logische_Gesetze warum?])&lt;br /&gt;
&lt;br /&gt;
Außerdem kann jeder Ausdruck in INF als gerichteter Graph dargestellt werden&lt;br /&gt;
# Jede Variable und ihre Negation sind 1 Knoten(dh insgesamt 2 Knoten)&lt;br /&gt;
# Jede Implikation ist eine gerichtete Kante&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
&lt;br /&gt;
=== postOrderTime ===&lt;br /&gt;
&lt;br /&gt;
    def postOrderTime(graph):&lt;br /&gt;
        visited = [None] * len(graph) &lt;br /&gt;
        def visit(node, count):&lt;br /&gt;
            visited[node] = -1&lt;br /&gt;
            for  neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor] is not None: continue&lt;br /&gt;
                count = visit(neighbor, count)&lt;br /&gt;
            visited[node] = count&lt;br /&gt;
            count += 1&lt;br /&gt;
            return count&lt;br /&gt;
        count = 0&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            if visited[node] is not None: continue&lt;br /&gt;
            count = visit(node, count)&lt;br /&gt;
        return visited&lt;br /&gt;
&lt;br /&gt;
=== transpose ===&lt;br /&gt;
&lt;br /&gt;
    def transpose(graph):&lt;br /&gt;
        grapht = [[] for k in range(len(graph))]&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                grapht[neighbor].append(node)&lt;br /&gt;
        return grapht&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;\rightarrow \; x_i == \neg x_i&amp;lt;/math&amp;gt; unmöglich ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit: teste die Eigenschaft&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2183</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2183"/>
				<updated>2008-07-08T19:39:19Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* Stark zusammenhängende Komponenten */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= [http://de.wikipedia.org/wiki/Erfüllbarkeitsproblem_der_Aussagenlogik Erfüllbarkeitsproblem] =&lt;br /&gt;
&lt;br /&gt;
geg.: &lt;br /&gt;
* n Boolsche Variablen &amp;lt;math&amp;gt;x_i \in \{True,False\}&amp;lt;/math&amp;gt; und deren Negation &amp;lt;math&amp;gt;\neg x_i (i=1..n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Logischer Ausdruck in &amp;lt;math&amp;gt;x_i,\neg x_i&amp;lt;/math&amp;gt;&lt;br /&gt;
** zB &amp;lt;math&amp;gt;(x_1 \vee x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines logischen Ausdrucks(in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= &amp;amp;lt;CONJ&amp;amp;gt; | &amp;amp;lt;DISJ&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;TERM&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;TERM&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;TERM&amp;amp;gt; ::= ( &amp;amp;lt;EXPR&amp;amp;gt; ) | &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ges.: Eine Belegung der &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, so dass der gegebene Ausdruck &amp;quot;True&amp;quot; wird&lt;br /&gt;
&lt;br /&gt;
=== Naive Lösung ===&lt;br /&gt;
Probiere alle Bedingungen aus &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; Komplexität &amp;lt;math&amp;gt;\mathcal{O}(2^{n}) \!&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Im Allgemeinen ist das der effizienteste bekannte Algorithmus'''&lt;br /&gt;
&lt;br /&gt;
== '''Normalformen''' von logischen Ausdrücken ==&lt;br /&gt;
&lt;br /&gt;
=== k-Konjunktionen-Normalform(k-CNF) ===&lt;br /&gt;
&lt;br /&gt;
* ein &amp;quot;Literal&amp;quot; ist eine Variable &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; oder deren Negation&lt;br /&gt;
* jeweils ''k'' Literale werden mit &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; in einer '''Disjunktion''' verknüpft&lt;br /&gt;
* Disjunktionen werden mit &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; in einer '''Konjunktion''' verbunden&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in k-CNF(wieder in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;DISJ&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; ... &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; ) &amp;amp;lt;!-- k Literale --&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* 3-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2 \vee x_4) \wedge (x_2 \vee x_3 \vee \neg x_4) \wedge (\neg x_1 \vee x_4 \vee \neg x_5)&amp;lt;/math&amp;gt;&lt;br /&gt;
* 2-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* Jeder logische Ausdruck kann in polynomieller Zeit in 3-CNF umgewandelt werden&lt;br /&gt;
* Im Allgemeinen kann ein logischer Ausdruck nicht in 2-CNF umgeschrieben werden&lt;br /&gt;
&lt;br /&gt;
=== Implikationen-Normalform(INF) ===&lt;br /&gt;
&lt;br /&gt;
Konjunktionen von Implikationen:&lt;br /&gt;
* zB &amp;lt;math&amp;gt;(x_1 \to x_2) \wedge (x_2 \to \neg x_3) \wedge (x_4 \to x_3)&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in INF(you know the drill ;)):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;IMPL&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;IMPL&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;IMPL&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; )&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* jeder Ausdruck in 2-CNF kann in INF umgewandelt werden: &lt;br /&gt;
*: &amp;lt;math&amp;gt; (x_i \vee x_j) \Leftrightarrow (\neg x_i \to x_j) \wedge (\neg x_j \to x_i) &amp;lt;/math&amp;gt; ([http://de.wikipedia.org/wiki/Implikation#Eigenschaften_und_logische_Gesetze warum?])&lt;br /&gt;
&lt;br /&gt;
Außerdem kann jeder Ausdruck in INF als gerichteter Graph dargestellt werden&lt;br /&gt;
# Jede Variable und ihre Negation sind 1 Knoten(dh insgesamt 2 Knoten)&lt;br /&gt;
# Jede Implikation ist eine gerichtete Kante&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
&lt;br /&gt;
=== postOrderTime ===&lt;br /&gt;
&lt;br /&gt;
    def postOrderTime(graph):&lt;br /&gt;
        visited = [None] * len(graph) &lt;br /&gt;
        def visit(node, count):&lt;br /&gt;
            visited[node] = -1&lt;br /&gt;
            for  neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor] is not None: continue&lt;br /&gt;
                count = visit(neighbor, count)&lt;br /&gt;
            visited[node] = count&lt;br /&gt;
            count += 1&lt;br /&gt;
            return count&lt;br /&gt;
        count = 0&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
            if visited[node] is not None: continue&lt;br /&gt;
            count = visit(node, count)&lt;br /&gt;
        return visited&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;\rightarrow \; x_i == \neg x_i&amp;lt;/math&amp;gt; unmöglich ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit: teste die Eigenschaft&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2178</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2178"/>
				<updated>2008-07-08T19:23:53Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* Implikationen-Normalform(INF) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= [http://de.wikipedia.org/wiki/Erfüllbarkeitsproblem_der_Aussagenlogik Erfüllbarkeitsproblem] =&lt;br /&gt;
&lt;br /&gt;
geg.: &lt;br /&gt;
* n Boolsche Variablen &amp;lt;math&amp;gt;x_i \in \{True,False\}&amp;lt;/math&amp;gt; und deren Negation &amp;lt;math&amp;gt;\neg x_i (i=1..n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Logischer Ausdruck in &amp;lt;math&amp;gt;x_i,\neg x_i&amp;lt;/math&amp;gt;&lt;br /&gt;
** zB &amp;lt;math&amp;gt;(x_1 \vee x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines logischen Ausdrucks(in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= &amp;amp;lt;CONJ&amp;amp;gt; | &amp;amp;lt;DISJ&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;TERM&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;TERM&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;TERM&amp;amp;gt; ::= ( &amp;amp;lt;EXPR&amp;amp;gt; ) | &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ges.: Eine Belegung der &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, so dass der gegebene Ausdruck &amp;quot;True&amp;quot; wird&lt;br /&gt;
&lt;br /&gt;
=== Naive Lösung ===&lt;br /&gt;
Probiere alle Bedingungen aus &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; Komplexität &amp;lt;math&amp;gt;\mathcal{O}(2^{n}) \!&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Im Allgemeinen ist das der effizienteste bekannte Algorithmus'''&lt;br /&gt;
&lt;br /&gt;
== '''Normalformen''' von logischen Ausdrücken ==&lt;br /&gt;
&lt;br /&gt;
=== k-Konjunktionen-Normalform(k-CNF) ===&lt;br /&gt;
&lt;br /&gt;
* ein &amp;quot;Literal&amp;quot; ist eine Variable &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; oder deren Negation&lt;br /&gt;
* jeweils ''k'' Literale werden mit &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; in einer '''Disjunktion''' verknüpft&lt;br /&gt;
* Disjunktionen werden mit &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; in einer '''Konjunktion''' verbunden&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in k-CNF(wieder in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;DISJ&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; ... &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; ) &amp;amp;lt;!-- k Literale --&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* 3-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2 \vee x_4) \wedge (x_2 \vee x_3 \vee \neg x_4) \wedge (\neg x_1 \vee x_4 \vee \neg x_5)&amp;lt;/math&amp;gt;&lt;br /&gt;
* 2-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* Jeder logische Ausdruck kann in polynomieller Zeit in 3-CNF umgewandelt werden&lt;br /&gt;
* Im Allgemeinen kann ein logischer Ausdruck nicht in 2-CNF umgeschrieben werden&lt;br /&gt;
&lt;br /&gt;
=== Implikationen-Normalform(INF) ===&lt;br /&gt;
&lt;br /&gt;
Konjunktionen von Implikationen:&lt;br /&gt;
* zB &amp;lt;math&amp;gt;(x_1 \to x_2) \wedge (x_2 \to \neg x_3) \wedge (x_4 \to x_3)&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in INF(you know the drill ;)):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;IMPL&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;IMPL&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;IMPL&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; )&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* jeder Ausdruck in 2-CNF kann in INF umgewandelt werden: &lt;br /&gt;
*: &amp;lt;math&amp;gt; (x_i \vee x_j) \Leftrightarrow (\neg x_i \to x_j) \wedge (\neg x_j \to x_i) &amp;lt;/math&amp;gt; ([http://de.wikipedia.org/wiki/Implikation#Eigenschaften_und_logische_Gesetze warum?])&lt;br /&gt;
&lt;br /&gt;
Außerdem kann jeder Ausdruck in INF als gerichteter Graph dargestellt werden&lt;br /&gt;
# Jede Variable und ihre Negation sind 1 Knoten(dh insgesamt 2 Knoten)&lt;br /&gt;
# Jede Implikation ist eine gerichtete Kante&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;\rightarrow \; x_i == \neg x_i&amp;lt;/math&amp;gt; unmöglich ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit: teste die Eigenschaft&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2177</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2177"/>
				<updated>2008-07-08T19:22:45Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* Implikationen-Normalform(INF) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= [http://de.wikipedia.org/wiki/Erfüllbarkeitsproblem_der_Aussagenlogik Erfüllbarkeitsproblem] =&lt;br /&gt;
&lt;br /&gt;
geg.: &lt;br /&gt;
* n Boolsche Variablen &amp;lt;math&amp;gt;x_i \in \{True,False\}&amp;lt;/math&amp;gt; und deren Negation &amp;lt;math&amp;gt;\neg x_i (i=1..n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Logischer Ausdruck in &amp;lt;math&amp;gt;x_i,\neg x_i&amp;lt;/math&amp;gt;&lt;br /&gt;
** zB &amp;lt;math&amp;gt;(x_1 \vee x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines logischen Ausdrucks(in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= &amp;amp;lt;CONJ&amp;amp;gt; | &amp;amp;lt;DISJ&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;TERM&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;TERM&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;TERM&amp;amp;gt; ::= ( &amp;amp;lt;EXPR&amp;amp;gt; ) | &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ges.: Eine Belegung der &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, so dass der gegebene Ausdruck &amp;quot;True&amp;quot; wird&lt;br /&gt;
&lt;br /&gt;
=== Naive Lösung ===&lt;br /&gt;
Probiere alle Bedingungen aus &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; Komplexität &amp;lt;math&amp;gt;\mathcal{O}(2^{n}) \!&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Im Allgemeinen ist das der effizienteste bekannte Algorithmus'''&lt;br /&gt;
&lt;br /&gt;
== '''Normalformen''' von logischen Ausdrücken ==&lt;br /&gt;
&lt;br /&gt;
=== k-Konjunktionen-Normalform(k-CNF) ===&lt;br /&gt;
&lt;br /&gt;
* ein &amp;quot;Literal&amp;quot; ist eine Variable &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; oder deren Negation&lt;br /&gt;
* jeweils ''k'' Literale werden mit &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; in einer '''Disjunktion''' verknüpft&lt;br /&gt;
* Disjunktionen werden mit &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; in einer '''Konjunktion''' verbunden&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in k-CNF(wieder in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;DISJ&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; ... &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; ) &amp;amp;lt;!-- k Literale --&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* 3-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2 \vee x_4) \wedge (x_2 \vee x_3 \vee \neg x_4) \wedge (\neg x_1 \vee x_4 \vee \neg x_5)&amp;lt;/math&amp;gt;&lt;br /&gt;
* 2-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* Jeder logische Ausdruck kann in polynomieller Zeit in 3-CNF umgewandelt werden&lt;br /&gt;
* Im Allgemeinen kann ein logischer Ausdruck nicht in 2-CNF umgeschrieben werden&lt;br /&gt;
&lt;br /&gt;
=== Implikationen-Normalform(INF) ===&lt;br /&gt;
&lt;br /&gt;
Konjunktionen von Implikationen:&lt;br /&gt;
* zB &amp;lt;math&amp;gt;(x_1 \to x_2) \wedge (x_2 \to \neg x_3) \wedge (x_4 \to x_3)&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in INF(you know the drill ;)):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;IMPL&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;IMPL&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;IMPL&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; )&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* jeder Ausdruck in 2-CNF kann in INF umgewandelt werden: &lt;br /&gt;
*: &amp;lt;math&amp;gt; (x_i \vee x_j) \Leftrightarrow (\neg x_i \to x_j) \vee (\neg x_j \to x_i) &amp;lt;/math&amp;gt; ([http://de.wikipedia.org/wiki/Implikation#Eigenschaften_und_logische_Gesetze warum?])&lt;br /&gt;
&lt;br /&gt;
Außerdem kann jeder Ausdruck in INF als gerichteter Graph dargestellt werden&lt;br /&gt;
# Jede Variable und ihre Negation sind 1 Knoten(dh insgesamt 2 Knoten)&lt;br /&gt;
# Jede Implikation ist eine gerichtete Kante&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;\rightarrow \; x_i == \neg x_i&amp;lt;/math&amp;gt; unmöglich ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit: teste die Eigenschaft&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2176</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2176"/>
				<updated>2008-07-08T19:21:32Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* Implikationen-Normalform(INF) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= [http://de.wikipedia.org/wiki/Erfüllbarkeitsproblem_der_Aussagenlogik Erfüllbarkeitsproblem] =&lt;br /&gt;
&lt;br /&gt;
geg.: &lt;br /&gt;
* n Boolsche Variablen &amp;lt;math&amp;gt;x_i \in \{True,False\}&amp;lt;/math&amp;gt; und deren Negation &amp;lt;math&amp;gt;\neg x_i (i=1..n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Logischer Ausdruck in &amp;lt;math&amp;gt;x_i,\neg x_i&amp;lt;/math&amp;gt;&lt;br /&gt;
** zB &amp;lt;math&amp;gt;(x_1 \vee x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines logischen Ausdrucks(in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= &amp;amp;lt;CONJ&amp;amp;gt; | &amp;amp;lt;DISJ&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;TERM&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;TERM&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;TERM&amp;amp;gt; ::= ( &amp;amp;lt;EXPR&amp;amp;gt; ) | &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ges.: Eine Belegung der &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, so dass der gegebene Ausdruck &amp;quot;True&amp;quot; wird&lt;br /&gt;
&lt;br /&gt;
=== Naive Lösung ===&lt;br /&gt;
Probiere alle Bedingungen aus &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; Komplexität &amp;lt;math&amp;gt;\mathcal{O}(2^{n}) \!&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Im Allgemeinen ist das der effizienteste bekannte Algorithmus'''&lt;br /&gt;
&lt;br /&gt;
== '''Normalformen''' von logischen Ausdrücken ==&lt;br /&gt;
&lt;br /&gt;
=== k-Konjunktionen-Normalform(k-CNF) ===&lt;br /&gt;
&lt;br /&gt;
* ein &amp;quot;Literal&amp;quot; ist eine Variable &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; oder deren Negation&lt;br /&gt;
* jeweils ''k'' Literale werden mit &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; in einer '''Disjunktion''' verknüpft&lt;br /&gt;
* Disjunktionen werden mit &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; in einer '''Konjunktion''' verbunden&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in k-CNF(wieder in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;DISJ&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; ... &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; ) &amp;amp;lt;!-- k Literale --&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* 3-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2 \vee x_4) \wedge (x_2 \vee x_3 \vee \neg x_4) \wedge (\neg x_1 \vee x_4 \vee \neg x_5)&amp;lt;/math&amp;gt;&lt;br /&gt;
* 2-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* Jeder logische Ausdruck kann in polynomieller Zeit in 3-CNF umgewandelt werden&lt;br /&gt;
* Im Allgemeinen kann ein logischer Ausdruck nicht in 2-CNF umgeschrieben werden&lt;br /&gt;
&lt;br /&gt;
=== Implikationen-Normalform(INF) ===&lt;br /&gt;
&lt;br /&gt;
Konjunktionen von Implikationen:&lt;br /&gt;
* zB &amp;lt;math&amp;gt;(x_1 \to x_2) \wedge (x_2 \to \neg x_3) \wedge (x_4 \to x_3)&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in INF(you know the drill ;)):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;IMPL&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;IMPL&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;IMPL&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; )&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* jeder Ausdruck in 2-CNF kann in INF umgewandelt werden: &lt;br /&gt;
*: &amp;lt;math&amp;gt; (x_i \vee x_j) \Leftrightarrow (\neg x_i \to x_j) \vee (\neg x_j \to x_i) &amp;lt;/math&amp;gt; ([http://de.wikipedia.org/wiki/Implikation#Eigenschaften_und_logische_Gesetze warum?])&lt;br /&gt;
&lt;br /&gt;
Außerdem kann jeder Ausdruck in INF als gerichteter Graph dargestellt werden&lt;br /&gt;
# Jede Variable und ihre Negation sind 1 Knoten(dh insgesamt 2 Knoten)&lt;br /&gt;
# Jede Implikation ist eine gerichtete Kante&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;\rightarrow \; x_i == \neg x_i&amp;lt;/math&amp;gt; unmöglich ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit: teste die Eigenschaft&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2175</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2175"/>
				<updated>2008-07-08T19:15:48Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* Erfüllbarkeitsproblem */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= [http://de.wikipedia.org/wiki/Erfüllbarkeitsproblem_der_Aussagenlogik Erfüllbarkeitsproblem] =&lt;br /&gt;
&lt;br /&gt;
geg.: &lt;br /&gt;
* n Boolsche Variablen &amp;lt;math&amp;gt;x_i \in \{True,False\}&amp;lt;/math&amp;gt; und deren Negation &amp;lt;math&amp;gt;\neg x_i (i=1..n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Logischer Ausdruck in &amp;lt;math&amp;gt;x_i,\neg x_i&amp;lt;/math&amp;gt;&lt;br /&gt;
** zB &amp;lt;math&amp;gt;(x_1 \vee x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines logischen Ausdrucks(in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= &amp;amp;lt;CONJ&amp;amp;gt; | &amp;amp;lt;DISJ&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;TERM&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;TERM&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;TERM&amp;amp;gt; ::= ( &amp;amp;lt;EXPR&amp;amp;gt; ) | &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ges.: Eine Belegung der &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, so dass der gegebene Ausdruck &amp;quot;True&amp;quot; wird&lt;br /&gt;
&lt;br /&gt;
=== Naive Lösung ===&lt;br /&gt;
Probiere alle Bedingungen aus &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; Komplexität &amp;lt;math&amp;gt;\mathcal{O}(2^{n}) \!&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Im Allgemeinen ist das der effizienteste bekannte Algorithmus'''&lt;br /&gt;
&lt;br /&gt;
== '''Normalformen''' von logischen Ausdrücken ==&lt;br /&gt;
&lt;br /&gt;
=== k-Konjunktionen-Normalform(k-CNF) ===&lt;br /&gt;
&lt;br /&gt;
* ein &amp;quot;Literal&amp;quot; ist eine Variable &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt; oder deren Negation&lt;br /&gt;
* jeweils ''k'' Literale werden mit &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; in einer '''Disjunktion''' verknüpft&lt;br /&gt;
* Disjunktionen werden mit &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; in einer '''Konjunktion''' verbunden&lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in k-CNF(wieder in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;DISJ&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;DISJ&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;DISJ&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; ... &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; ) &amp;amp;lt;!-- k Literale --&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* 3-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2 \vee x_4) \wedge (x_2 \vee x_3 \vee \neg x_4) \wedge (\neg x_1 \vee x_4 \vee \neg x_5)&amp;lt;/math&amp;gt;&lt;br /&gt;
* 2-CNF: &amp;lt;math&amp;gt;(x_1 \vee \neg x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* Jeder logische Ausdruck kann in polynomieller Zeit in 3-CNF umgewandelt werden&lt;br /&gt;
* Im Allgemeinen kann ein logischer Ausdruck nicht in 2-CNF umgeschrieben werden&lt;br /&gt;
&lt;br /&gt;
=== Implikationen-Normalform(INF) ===&lt;br /&gt;
&lt;br /&gt;
Konjunktionen von Implikationen:&lt;br /&gt;
* zB &amp;lt;math&amp;gt;(x_1 \to x_2) \wedge (x_2 \to \neg x_3) \wedge (x_4 \to x_3)&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
    Grammatik eines Ausdrucks in INF(you know the drill ;)):&lt;br /&gt;
    &amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;CONJ&amp;amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    &amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;IMPL&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;IMPL&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;IMPL&amp;amp;gt; ::= ( &amp;amp;lt;LIT&amp;amp;gt; &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; &amp;amp;lt;LIT&amp;amp;gt; )&lt;br /&gt;
    &amp;amp;lt;LIT&amp;amp;gt;  ::=  &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt; &lt;br /&gt;
    &amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;border-bottom: 1px solid #000;&amp;quot;&amp;gt;Satz&amp;lt;/span&amp;gt;:&lt;br /&gt;
* jeder Ausdruck in 2-CNF kann in INF umgewandelt werden: &lt;br /&gt;
*: &amp;lt;math&amp;gt; (x_i \wedge x_j) \Leftrightarrow (\neg x_i \to x_j) \vee (\neg x_j \to x_i) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;\rightarrow \; x_i == \neg x_i&amp;lt;/math&amp;gt; unmöglich ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit: teste die Eigenschaft&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2167</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2167"/>
				<updated>2008-07-08T17:57:55Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* Erfüllbarkeitsproblem */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Erfüllbarkeitsproblem =&lt;br /&gt;
&lt;br /&gt;
geg.: &lt;br /&gt;
* n Boolsche Variablen &amp;lt;math&amp;gt;x_i \in \{True,False\}&amp;lt;/math&amp;gt; und deren Negation &amp;lt;math&amp;gt;\neg x_i (i=1..n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* Logischer Ausdruck in &amp;lt;math&amp;gt;x_i,\neg x_i&amp;lt;/math&amp;gt;&lt;br /&gt;
*: zB &amp;lt;math&amp;gt;(x_1 \vee x_2) \wedge (x_3 \vee x_4)&amp;lt;/math&amp;gt; ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
Grammatik(in [http://de.wikipedia.org/wiki/Backus-Naur-Form BNF]):&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;lt;EXP&amp;amp;gt;  ::= &amp;amp;lt;DISJ&amp;amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;lt;DISJ&amp;amp;gt; ::= &amp;amp;lt;CONJ&amp;amp;gt; | &amp;amp;lt;DISJ&amp;amp;gt; &amp;lt;math&amp;gt;\vee&amp;lt;/math&amp;gt; &amp;amp;lt;CONJ&amp;amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;lt;CONJ&amp;amp;gt; ::= &amp;amp;lt;TERM&amp;amp;gt; | &amp;amp;lt;CONJ&amp;amp;gt; &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; &amp;amp;lt;TERM&amp;amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;lt;TERM&amp;amp;gt; ::= ( &amp;amp;lt;EXPR&amp;amp;gt; ) | &amp;amp;lt;VAR&amp;amp;gt; | &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&amp;amp;lt;VAR&amp;amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;lt;VAR&amp;amp;gt;  ::= &amp;lt;math&amp;gt;x_1&amp;lt;/math&amp;gt; | ... | &amp;lt;math&amp;gt;x_n&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ges.: Eine Belegung der &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;, so dass der gegebene Ausdruck &amp;quot;True&amp;quot; wird&lt;br /&gt;
&lt;br /&gt;
=== Naive Lösung ===&lt;br /&gt;
Probiere alle Bedingungen aus &amp;lt;math&amp;gt;\to&amp;lt;/math&amp;gt; Komplexität &amp;lt;math&amp;gt;\mathcal{O}(2^{n}) \!&amp;lt;/math&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Im Allgemeinen ist das der effizienteste bekannte Algorithmus'''&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;\rightarrow \; x_i == \neg x_i&amp;lt;/math&amp;gt; unmöglich ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit: teste die Eigenschaft&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2166</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2166"/>
				<updated>2008-07-08T17:18:09Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* Erfüllbarkeitsproblem */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Erfüllbarkeitsproblem =&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;\rightarrow \; x_i == \neg x_i&amp;lt;/math&amp;gt; unmöglich ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit: teste die Eigenschaft&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2165</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2165"/>
				<updated>2008-07-08T17:17:49Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: /* Erfüllbarkeitsproblem */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Erfüllbarkeitsproblem ===&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;\rightarrow \; x_i == \neg x_i&amp;lt;/math&amp;gt; unmöglich ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit: teste die Eigenschaft&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	<entry>
		<id>https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2164</id>
		<title>Graphen und Graphenalgorithmen</title>
		<link rel="alternate" type="text/html" href="https://alda.iwr.uni-heidelberg.de/index.php?title=Graphen_und_Graphenalgorithmen&amp;diff=2164"/>
				<updated>2008-07-08T17:17:24Z</updated>
		
		<summary type="html">&lt;p&gt;LDream: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung zu Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation -- Königsberger Brückenproblem ===&lt;br /&gt;
Leonhard Euler [http://de.wikipedia.org/wiki/Leonhard_Euler] erfand den Graphen-Formalismus 1736, um eine scheinbar banale Frage zu beantworten: Ist es möglich, in Königsberg (siehe Abbildung) einen Spaziergang zu unternehmen, bei dem jede der 7 Brücken genau einmal überquert wird?&lt;br /&gt;
&lt;br /&gt;
[[Image:Koenigsberg.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein Graph abstrahiert von der Geometrie des Problems und repräsentiert nur die Topologie. Jeder Stadtteil von Königsberg ist ein Knoten des Graphen, jede Brücke eine Kante. Der zum Brückenproblem gehörende Graph sieht also so aus:&lt;br /&gt;
&lt;br /&gt;
     O&lt;br /&gt;
    /| \&lt;br /&gt;
    \|  \&lt;br /&gt;
     O---O&lt;br /&gt;
    /|  /&lt;br /&gt;
    \| /&lt;br /&gt;
     O&lt;br /&gt;
&lt;br /&gt;
Der gesuchte Spaziergang würde existieren, wenn es maximal 2 Knoten gäbe, an denen sich eine ungerade Zahl von Kanten trifft. Die Frage muss für Königsberg also verneint werden, denn hier gibt es vier solche Knoten. &lt;br /&gt;
&lt;br /&gt;
Inzwischen haben Graphen ein riesige Zahl weiterer Anwendungen gefunden. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Landkarten:&lt;br /&gt;
** Knoten: Länder&lt;br /&gt;
** Kanten: gemeinsame Grenzen&lt;br /&gt;
&lt;br /&gt;
* Logische Schaltkreise:&lt;br /&gt;
** Knoten: Gatter&lt;br /&gt;
** Kanten: Verbindungen&lt;br /&gt;
&lt;br /&gt;
* Chemie (Summenformeln):&lt;br /&gt;
** Knoten: chemische Elemente&lt;br /&gt;
** Kanten: Bindungen &lt;br /&gt;
&lt;br /&gt;
* Soziologie (StudiVZ)&lt;br /&gt;
** Soziogramm&lt;br /&gt;
*** Knoten: Personen&lt;br /&gt;
*** Kanten: Freund von ...&lt;br /&gt;
&lt;br /&gt;
=== Definitionen ===&lt;br /&gt;
&lt;br /&gt;
;Ungerichteter Graph: Ein ungerichteter Graph G = ( V, E ) besteht aus&lt;br /&gt;
:* einer endliche Menge V von Knoten (vertices)&lt;br /&gt;
:* einer endlichen Menge &amp;lt;math&amp;gt;E \subset V \times V&amp;lt;/math&amp;gt; von Kanten (edges)&lt;br /&gt;
:Die Paare (u,v) und (v,u) gelten dabei als nur ''eine'' Kante (somit gilt die Symmetriebeziehung: (u,v) ∈ E =&amp;gt; (v,u) ∈ E ). Die Anzahl der Kanten, die sich an einem Knoten treffen, wird als ''Grad'' (engl. ''degree'') dieses Knotens bezeichnet:&lt;br /&gt;
:::degree(v) = |{v' ∈ V | (v,v') ∈ E}|&lt;br /&gt;
:(Die Syntax |{...}| bezeichnet dabei die Mächtigkeit der angegebenen Menge, also die Anzahl der Elemente in der Menge.)&lt;br /&gt;
&lt;br /&gt;
Der Graph des Königsberger Brückenproblems ist ungerichtet. Bezeichnet man die Knoten entsprechend des folgenden Bildes&lt;br /&gt;
    c&lt;br /&gt;
   /| \&lt;br /&gt;
   \|  \&lt;br /&gt;
    b---d &lt;br /&gt;
   /|  /&lt;br /&gt;
   \| /&lt;br /&gt;
    a&lt;br /&gt;
&lt;br /&gt;
gilt für die Knotengrade: &amp;lt;tt&amp;gt;degree(a) == degree(c) == degree(d) == 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;degree(b) == 5&amp;lt;/tt&amp;gt;. Genauer muss man bei diesem Graphen von einem ''Multigraphen'' sprechen, weil es zwischen einigen Knotenpaaren (nämlich (a, b) sowie (b, c)) mehrere Kanten (&amp;quot;Mehrfachkanten&amp;quot;) gibt. Wir werden in dieser Vorlesung nicht näher auf Multigraphen eingehen.&lt;br /&gt;
&lt;br /&gt;
;Gerichteter Graph: Ein Graph heißt ''gerichtet'', wenn die Kanten (u,v) und (v,u) unterschieden werden. Die Kante (u,v) ∈ E wird nun als Kante von u nach v (aber nicht umgekehrt) interpretiert. Entsprechend unterscheidet man jetzt den ''eingehenden'' und den ''ausgehenden Grad'' jedes Knotens:&lt;br /&gt;
:*out_degree(v) = |{v' ∈ V | (v,v') ∈ E}|&amp;lt;br/&amp;gt;&lt;br /&gt;
:*in_degree(v)  = |{v' ∈ V| (v',v) ∈ E}|&lt;br /&gt;
&lt;br /&gt;
Das folgende Bild zeigt einen gerichteten Graphen. Hier gilt &amp;lt;tt&amp;gt;out_degree(1) == out_degree(3) == in_degree(2) == in_degree(4) == 2&amp;lt;/tt&amp;gt; und &lt;br /&gt;
&amp;lt;tt&amp;gt;in_degree(1) == in_degree(3) == out_degree(2) == out_degree(4) == 0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Image:digraph.png|gerichteter Graph]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Vollständiger Graph: Ein vollständiger Graph ist ein ungerichteter Graph, bei dem jeder Knoten mit allen anderen Knoten verbunden ist.&lt;br /&gt;
:::&amp;lt;math&amp;gt;E = \{ (v,w) |  v \in V, w \in V, v \ne w \}&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein vollständiger Graph mit |V| Knoten hat &amp;lt;math&amp;gt;|E| = \frac{|V|(|V|-1)}{2}&amp;lt;/math&amp;gt; Kanten.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abbildungen zeigen die vollständigen Graphen mit einem bis fünf Knoten (auch als K&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; bis K&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot; style=&amp;quot;margin: 1em auto 1em auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:k1.png|frame|k1]]&lt;br /&gt;
| [[Image:k2.png|frame|k2]]&lt;br /&gt;
| [[Image:k3.png|frame|k3]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Image:k4.png|frame|k4]]&lt;br /&gt;
| [[Image:k5.png|frame|k5]]&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Rätsel''&amp;lt;br/&amp;gt;&lt;br /&gt;
Auf einer Party sind Leute. Alle stoßen miteinander an. Es hat 78 mal &amp;quot;Pling&amp;quot; gemacht.&lt;br /&gt;
Wieviele Leute waren da? Antwort: Jede Person ist ein Knoten des Graphen, jedes Antoßen eine Kante. &lt;br /&gt;
Da alle miteinander angestoßen haben, handelt es sich um einen vollständigen Graphen. Mit&lt;br /&gt;
|V|(|V|-1)/2 = 78 folgt, dass es 13 Personen waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Gewichteter Graph: Ein Graph heißt ''gewichtet'', wenn jeder Kante eine reelle Zahl zugeordnet ist. Bei vielen Anwendungen beschränkt man sich auch auf nichtnegative reelle Gewichte. In einem gerichteten Graphen können die Gewichte der Kanten (u,v) und (v,u) unterschiedlich sein.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte kodieren Eigenschaften der Kanten, die für die jeweilige Anwendung interessant sind. Bei der Berechnung des maximalen Flusses in einem Netzwerk sind die Gewichte z.B. die Durchflusskapazitäten jeder Kante, bei der Suche nach kürzesten Weges kodieren Sie den Abstand zwischen den Endknoten der Kante, bei Währungsnetzwerken (jeder Knoten ist eine Währung) geben sie die Wechselkurse an, usw..&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Teilgraphen: Ein Graph G' = (V',E') ist ein Teilgraph eines Graphen G, wenn gilt:&lt;br /&gt;
:* V' &amp;amp;sube; V &lt;br /&gt;
:* E' &amp;amp;sub; E &lt;br /&gt;
:Er heißt ''(auf)spannender Teilgraph'', wenn gilt:&lt;br /&gt;
:* V' = V&lt;br /&gt;
:Er heißt ''induzierter Teilgraph'', wenn gilt:&lt;br /&gt;
:* e = (u,v) ∈ E' &amp;amp;sub; E &amp;amp;hArr; u ∈ V' und v ∈ V'&lt;br /&gt;
:Den von V' induzierten Teilgraphen erhält man also, indem man aus G alle Knoten löscht, die nicht in V' sind, sowie alle Kanten (und nur diese Kanten), die einen der gelöschten Knoten als Endknoten haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wege, Pfade, Zyklen, Kreise, Erreichbarkeit: Sei G = (V,E) ein Graph (ungerichtet oder gerichteter) Graph. Dann gilt folgende rekursive Definition:&lt;br /&gt;
:* Für v ∈ V ist (v) ein Weg der Länge 0 in G&lt;br /&gt;
:* Falls &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ein Weg ist, und eine Kante &amp;lt;math&amp;gt;(v_{n-1}, v_n)\in E&amp;lt;/math&amp;gt; existiert, dann ist auch &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ein Weg, und er hat die Länge n. &lt;br /&gt;
: Ein Weg ist also eine nichtleere Folge von Knoten, so dass aufeinander folgende Knoten stets durch eine Kante verbunden sind. Die Länge des Weges entspricht der Anzahl der Kanten im Weg (= Anzahl der Knoten - 1).&lt;br /&gt;
:* Ein ''Pfad'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, bei dem alle Knoten v&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt; verschieden sind.&lt;br /&gt;
:* ''Ein Zyklus'' &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1}, v_n)&amp;lt;/math&amp;gt; ist ein Weg, der zum Ausgangspunkt zurückkehrt, wenn also v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; gilt.&lt;br /&gt;
:* Ein ''Kreis'' ist ein Zyklus ohne Überkreuzungen. Das heisst, es gilt v&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; = v&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt; und &amp;lt;math&amp;gt;(v_0, v_1, ..., v_{n-1})&amp;lt;/math&amp;gt; ist ein Pfad.&lt;br /&gt;
:* Ein Knoten w ∈ V ist von einem anderen Knoten v ∈ V aus ''erreichbar'' genau dann, wenn ein Weg (v, ..., w) existiert. Wir schreiben dann &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;.&lt;br /&gt;
In einem ungerichteten Graph ist die Erreichbarkeits-Relation stets symmetrisch, das heisst aus &amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; folgt &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. In einem gerichteten Graphen ist dies im allgemeinen nicht der Fall.&lt;br /&gt;
&lt;br /&gt;
Bestimmte Wege haben spezielle Namen&lt;br /&gt;
&lt;br /&gt;
;Eulerweg: Ein Eulerweg ist ein Weg, der alle '''Kanten''' genau einmal enthält.&lt;br /&gt;
&lt;br /&gt;
Die eingangs erwähnte Frage des Königsberger Brückenproblems ist equivalent zu der Frage, ob der dazugehörige Graph einen Eulerweg besitzt (daher der Name). Ein anderes bekanntes Beispiel ist das &amp;quot;Haus vom Nikolaus&amp;quot;: Wenn man diesen Graphen in üblicher Weise in einem Zug zeichnet, erhält man gerade den Eulerweg. &lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &amp;quot;Das Haus vom Nikolaus&amp;quot;: Alle ''Kanten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonweg: Ein Hamiltonweg ist ein Weg, der alle '''Knoten''' genau einmal enthält. Das &amp;quot;Haus vom Nikolaus&amp;quot; besitzt auch einen Hamiltonweg:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /   &lt;br /&gt;
  O----O&lt;br /&gt;
     /  &lt;br /&gt;
    /      Alle ''Knoten'' werden nur ''einmal'' passiert&lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Hamiltonkreis: Ein Hamiltonkreis ist ein Kreis, der alle '''Knoten''' genau einmal enthält. Auch ein solches Gebilde ist im Haus von Nilolaus enthalten:&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
  |    |   v0 = vn&lt;br /&gt;
  |    |   vi != vj   Für Alle i,j   i !=j; i,j &amp;gt;0; i,j &amp;lt; n&lt;br /&gt;
  O----O     &lt;br /&gt;
&lt;br /&gt;
Die folgende Skizze zeigt hingegen einen Zyklus: Der Knoten rechts unten sowie die untere Kante sind zweimal enthalten (die Kante einmal von links nach rechts und einmal von rechts nach links):&lt;br /&gt;
&lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O    O&lt;br /&gt;
    \  |&lt;br /&gt;
     \ |   Zyklus&lt;br /&gt;
  O====O&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Zusammenhang, Zusammenhangskomponenten: Ein ungerichteter Graph G heißt ''zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt;&lt;br /&gt;
:Ein gerichteter Graph G ist zusammenhängend, wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''oder''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Er ist ''stark zusammenhängend'', wenn für alle v,w ∈ V gilt:&lt;br /&gt;
:::&amp;lt;math&amp;gt;v \rightsquigarrow w&amp;lt;/math&amp;gt; '''und''' &amp;lt;math&amp;gt;w \rightsquigarrow v&amp;lt;/math&amp;gt;. &lt;br /&gt;
:Entsprechende Definitionen gelten für Teilgraphen G'. Ein Teilgraph G' heisst ''Zusammenhangskomponente'' von G, wenn er ein ''maximaler'' zusammenhängender Teilgraph ist, d.h. wenn G' zusammenhängend ist, und man keine Knoten und Kanten aus G mehr zu G' hinzufügen kann, so dass G' immer noch zusammenhängend bleibt. Entsprechend definiert man ''starke Zusammenhangskomponenten'' in einem gerichteten Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Planarer Graph, ebener Graph: Ein Graph heißt ''planar'', wenn er so in einer Ebene gezeichnet werden ''kann'', dass sich die Kanten nicht schneiden (außer an den Knoten). Ein Graph heißt ''eben'', wenn er tatsächlich so gezeichnet ''ist'', dass sich die Kanten nicht schneiden. Die Einbettung in die Ebene ist im allgemeinen nicht eindeutig.&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Der folgende Graph ist planar und eben:&lt;br /&gt;
 &lt;br /&gt;
      O&lt;br /&gt;
     /|\&lt;br /&gt;
    / O \&lt;br /&gt;
   / / \ \&lt;br /&gt;
   O     O&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Haus vom Nikolaus&amp;quot; ist ebenfalls planar, wird aber üblicherweise nicht als ebener Graph gezeichnet, weil sich die Diagonalen auf der Wand überkreuzen:&lt;br /&gt;
 &lt;br /&gt;
    O&lt;br /&gt;
   /  \&lt;br /&gt;
  O----O&lt;br /&gt;
  | \/ |&lt;br /&gt;
  | /\ |   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Eine ebene Einbettung dieses Graphen wird erreicht, wenn man eine der Diagonalen ausserhalb des Hauses zeichnet. Der Graph (also die Menge der Knoten und Kanten) ändert sich dadurch nicht.&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
 |--O----O&lt;br /&gt;
 |  |  / |&lt;br /&gt;
 |  | /  |   &lt;br /&gt;
 |  O----O      Das &amp;quot;Haus vom Nikolaus&amp;quot; als ebener Graph gezeichnet.&lt;br /&gt;
 |       |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
Eine alternative Einbettung erhalten wir, wenn wir die andere Diagonale außerhalb des Hauses zeichnen:&lt;br /&gt;
 &lt;br /&gt;
      O  &lt;br /&gt;
     /  \&lt;br /&gt;
    O----O--|&lt;br /&gt;
    | \  |  |&lt;br /&gt;
    |  \ |  | &lt;br /&gt;
    O----O  |     Alternative Einbettung des &amp;quot;Haus vom Nikolaus&amp;quot;.&lt;br /&gt;
    |       |&lt;br /&gt;
    |-------|&lt;br /&gt;
&lt;br /&gt;
Jede Einbettung eines planaren Graphen (also jeder ebene Graph) definiert eine eindeutige Menge von ''Regionen'':&lt;br /&gt;
&lt;br /&gt;
 |----O   @&lt;br /&gt;
 |   /@ \&lt;br /&gt;
 |  O----O&lt;br /&gt;
 |  |@ / |&lt;br /&gt;
 |  | / @|   &lt;br /&gt;
 |  O----O        @ entspricht jeweils einer ''Region''. Auch ausserhalb der Figur ist eine Region (die sogenannte ''unendliche'' Region).&lt;br /&gt;
 |@      |&lt;br /&gt;
 |-------|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der vollständige Graph K5 ist kein planarer Graph, da sich zwangsweise Kanten schneiden, wenn man diesen Graphen in der Ebene zeichnet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
;Dualer Graph: Jeder ebene Graph G = (V, E) hat einen ''dualen Graphen'' D = (V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;), dessen Knoten und Kanten wie folgt definiert sind:&lt;br /&gt;
:* V&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; enthält einen Knoten für jede Region des Graphen G&lt;br /&gt;
:* Für jede Kante e ∈ E gibt es eine duale Kante e&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt; ∈ E&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;, die die an e angrenzenden Regionen (genauer: die entsprechenden Knoten in D) verbindet.&lt;br /&gt;
&lt;br /&gt;
DIe folgende Abbildung zeigt einen Graphen (grau) und seinen dualen Graphen (schwarz). Die Knoten des dualen Graphen sind mit Zahlen gekennzeichnet und entsprechen den Regionen des Originalgraphen. Jeder (grauen) Kante des Originalgraphen entspricht eine (schwarze) Kante des dualen Graphen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Image:dual-graphs.png]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für duale Graphen gilt: Jede Region des dualen Graphen enthält genau einen Knoten des Originalgraphen. Deshalb ist der duale Graph des dualen Graphen wieder der Originalgraph.&lt;br /&gt;
&lt;br /&gt;
=== Repräsentation von Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei G = ( V, E ) gegeben und liege V in einer linearen Sortierung vor.&amp;lt;br/&amp;gt; &lt;br /&gt;
:::&amp;lt;math&amp;gt;V = \{ v_1, ...., v_n \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Adjazenzmatrix: Ein Graph kann durch eine Adjazenzmatrix repräsentiert werden, die soviele Zeilen und Spalten enthält, wie der Graph Knoten hat. Die Elemente der Adjazenzmatrix sind &amp;quot;1&amp;quot;, falls eine Kante zwischen den zugehörigen Knoten existiert:&lt;br /&gt;
:::&amp;lt;math&amp;gt;\mathrm{\bold A} = a_{ij} = &lt;br /&gt;
\begin{cases}&lt;br /&gt;
1 &amp;amp; \mathrm{falls}\quad (v_i, v_j) \in E \\&lt;br /&gt;
0 &amp;amp; \mathrm{sonst}&lt;br /&gt;
\end{cases} &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:Die Indizes der Matrix entsprechen also den Indizes der Knoten gemäß der gegebenen Sortierung. Im Falle eines ungerichteten Graphen ist die Adjazenzmatrix stets symmetrisch (d.h. es gilt &amp;lt;math&amp;gt;a_{ij}=a_{ji}&amp;lt;/math&amp;gt;), bei einem gerichteten Graphen ist sie im allgemeinen unsymmetrisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel für einen ungerichteten Graphen:&lt;br /&gt;
&lt;br /&gt;
 v = { a,b,c,d }     b      d&lt;br /&gt;
                     | \  / |&lt;br /&gt;
                     |  \/  |&lt;br /&gt;
                     |  /\  |&lt;br /&gt;
                     | /  \ |&lt;br /&gt;
                     a      c&lt;br /&gt;
 &lt;br /&gt;
       a b c d&lt;br /&gt;
      -----------&lt;br /&gt;
      (0 1 0 1) |a &lt;br /&gt;
  A = (1 0 1 0) |b&lt;br /&gt;
      (0 1 0 1) |c&lt;br /&gt;
      (1 0 1 0) |d&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzmatrixdarstellung eignet sich besonders für dichte Graphen (d.h. wenn die Zahl der Kanten in O(|V|&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;) ist.&lt;br /&gt;
&lt;br /&gt;
;Adjazenzlisten: In der Adjazenzlistendarstellung wird der Graph als Liste von Knoten repräsentiert, die für jeden Knoten einen Eintrag enthält. Der Eintrag für jeden Knoten ist wiederum eine Liste, die die Nachbarknoten dieses Knotens enthält:&lt;br /&gt;
:* graph = {adjazencyList(v) | v ∈ V}&lt;br /&gt;
:* adjazencyList(v) = {v' ∈ V | (v, v') ∈ E}&lt;br /&gt;
&lt;br /&gt;
In Python implementieren wir Adjazenzlisten zweckmäßig als Array von Arrays:&lt;br /&gt;
&lt;br /&gt;
                   graph = [[...],[...],...,[...]]&lt;br /&gt;
 Adjazenzliste für Knoten =&amp;gt;  0     1         n&lt;br /&gt;
&lt;br /&gt;
Wenn wir bei dem Graphen oben die Knoten wie bei der Adjazenzmatrix indizieren (also &amp;lt;tt&amp;gt;a =&amp;gt; 0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;b =&amp;gt; 1&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;c =&amp;gt; 2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;d =&amp;gt; 3&amp;lt;/tt&amp;gt;), erhalten wir die Adjazenzlistendarstellung:&lt;br /&gt;
&lt;br /&gt;
 graph = [[b, d], [a, c],[b, d], [a, c]]&lt;br /&gt;
&lt;br /&gt;
Auf die Nachbarknoten eines durch seinen Index &amp;lt;tt&amp;gt;node&amp;lt;/tt&amp;gt; gegebenen Knotens können wir also wie folgt zugreifen:&lt;br /&gt;
&lt;br /&gt;
      for neighbors in graph[node]:&lt;br /&gt;
          ... # do something with neighbor&lt;br /&gt;
&lt;br /&gt;
Die Adjazenzlistendarstellung ist effizienter, wenn der Graph nicht dicht ist, so dass viele Einträge der Adjazenzmatrix Null wären.&lt;br /&gt;
&lt;br /&gt;
;Transponierter Graph: Den ''transponierten Graphen'' G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; eines gerichteten Graphen G erhält man, wenn man alle Kantenrichtungen umkehrt.&lt;br /&gt;
&lt;br /&gt;
Bei ungerichteten Graphen hat die Transposition offensichtlich keinen Effekt, weil alle Kanten bereits in beiden Richtungen vorhanden sind, so dass G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = G gilt. Bei gerichteten Graphen ist die Transposition dann einfach, wenn der Graph als Adjazenzmatrix implementiert ist, weil man einfach die transponierte Adjazenzmatrix verwenden muss (beachte, dass sich die Reihenfolge der Indizes umkehrt):&lt;br /&gt;
:::A&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; = a&amp;lt;sub&amp;gt;ji&amp;lt;/sub&amp;gt;&lt;br /&gt;
Ist der Graph hingegen durch eine Adjazenzliste repräsentiert, muss etwas mehr Aufwand getrieben werden:&lt;br /&gt;
&lt;br /&gt;
 def transpose(graph):&lt;br /&gt;
      gt = [[] for k in graph]   # zunächst leere Adjazenzlisten von G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt;&lt;br /&gt;
      for node in range(len(graph)):&lt;br /&gt;
           for neighbor in graph[node]:&lt;br /&gt;
               gt[neighbor].append(node)  # füge die umgekehrte Kante in G&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; ein&lt;br /&gt;
      return gt&lt;br /&gt;
&lt;br /&gt;
== Bäume und Wälder ==&lt;br /&gt;
&lt;br /&gt;
;Baum: Ein ''Baum'' ist ein zusammenhängender, kreisfreier Graph.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Binärer Suchbaum&lt;br /&gt;
&lt;br /&gt;
;Spannbaum: Ein ''Spannbaum'' eines zusammenhängenden Graphen G ist ein zusammenhängender, kreisfreier Teilgraph von G, der alle Knoten von G enthält&lt;br /&gt;
&lt;br /&gt;
Beispiel: Spannbaum für das &amp;quot;Haus des Nikolaus&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    O   &lt;br /&gt;
   /       &lt;br /&gt;
  O    O&lt;br /&gt;
  |  /  &lt;br /&gt;
  | /   &lt;br /&gt;
  O----O&lt;br /&gt;
&lt;br /&gt;
Der Spannbaum eines Graphen mit |V| Knoten hat stets |V| - 1 Kanten.&lt;br /&gt;
&lt;br /&gt;
;Wald: Ein ''Wald'' ist ein unzusammenhängender, kreisfreier Graph.&lt;br /&gt;
: Jede Zusammenhangskomponente eines Waldes ist ein Baum.&lt;br /&gt;
&lt;br /&gt;
== Durchlaufen von Graphen ==&lt;br /&gt;
&lt;br /&gt;
=== Tiefensuche in Graphen ===&lt;br /&gt;
&lt;br /&gt;
Sei der Graph gegeben als Liste von Listen = g&lt;br /&gt;
&lt;br /&gt;
 def dfs (g,node,v=0):&lt;br /&gt;
   if v == 0:&lt;br /&gt;
     v = [0]*len(g) #visited-Liste&lt;br /&gt;
   v[node] = 1 #besuche node&lt;br /&gt;
   for t in g[node]: #gehe zu allen Nachbarn&lt;br /&gt;
     if v[t] == 0: #falls diese noch nicht besucht&lt;br /&gt;
       dfs(g,t,v) #Rekursion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Tiefens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Aufruf dfs(g,1)&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,4,3,6,7,5&lt;br /&gt;
&lt;br /&gt;
=== Breitensuche ===&lt;br /&gt;
&lt;br /&gt;
 from Queue import *&lt;br /&gt;
 def bfs(g,startnode)&lt;br /&gt;
   v = [0]*len(g)&lt;br /&gt;
   q = Queue()&lt;br /&gt;
   v = [startnode] = 1 #besuche&lt;br /&gt;
   q.put(startnode) #in Schlange&lt;br /&gt;
   while not q.get()&lt;br /&gt;
     node = q.get()&lt;br /&gt;
     for t in q[node]&lt;br /&gt;
       if v[t] == 0:&lt;br /&gt;
         v[t] = 1&lt;br /&gt;
         q.put(t)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Breitens.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=&amp;gt;Folge 1,2,3,4,5,6,7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Damenproblem ==&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
 |   | X |   |   |&lt;br /&gt;
 |---|---|---|---| &lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 | X |   |   |   |&lt;br /&gt;
 |---|---|---|---|&lt;br /&gt;
 |   |   |   | X |&lt;br /&gt;
  ---------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4 Damen auf einem vereinfachten Schachbrett so Positionieren, dass sich keine bedroht.&lt;br /&gt;
&lt;br /&gt;
erster Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zweiter Durchlauf:&lt;br /&gt;
&lt;br /&gt;
[[Image:Suche2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weitere Anwendungen (18.06.08) ==&lt;br /&gt;
&lt;br /&gt;
 def dfs(graph):&lt;br /&gt;
        '''&lt;br /&gt;
        Diese Tiefensuche tut so noch nichts weiter als zu traversieren&lt;br /&gt;
        + graph ist Array,&lt;br /&gt;
            i-ter Eintrag enthaelt Adjazenzliste (auch Array) des i-ten Knotens,&lt;br /&gt;
            wobei Knoten nummeriert von 0 ... v-i&lt;br /&gt;
        '''&lt;br /&gt;
        def visit(graph, node, visited):&lt;br /&gt;
                '''&lt;br /&gt;
                visited ist Array mit Flags fuer besuchte Knoten&lt;br /&gt;
                '''&lt;br /&gt;
                if visited[node]: return&lt;br /&gt;
                visited[node] = True&lt;br /&gt;
                for neighbor in graph[node]:&lt;br /&gt;
                        visit(graph, neighbor, visited)&lt;br /&gt;
&lt;br /&gt;
        visited = [False]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, visited)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Finden von Zusammenhangskomponenten ===&lt;br /&gt;
&lt;br /&gt;
Ein moeglicher Einsatz des Verfahrens ist das Finden von Zusammenhangskomponenten (connected components).&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Definition: CC_i = {u_k, u_l e V: es gibt einen Pfad von u_k nach u_l (&amp;quot;u_l ist von u_k aus erreichbar&amp;quot;)&lt;br /&gt;
* fuer ungerichtete Graphen gilt zusaetzlich: es gibt einen Pfad von u_l nach u_k}&lt;br /&gt;
&lt;br /&gt;
Die Relation CC_i, also die Zusammenhangskomponenten (ZK) bilden eine Aequivalenzrelation,&lt;br /&gt;
also kann fuer jede ZK ein Repraesentant bestimmt werden (der sog. &amp;quot;Anker&amp;quot;). Kennt jeder&lt;br /&gt;
Knoten seinen Anker, so ist das ZK-Problem geloest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tiefensuchen-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Unser erster Ansatz ist, den Anker mit Hilfe der Tiefensuche zu finden, wobei statt&lt;br /&gt;
Knotenbesuche Knotennummern fuer die schon gefundenen Anker gesetzt werden. Ein moeglicher&lt;br /&gt;
Algorithmus lautet damit wie folgt:&lt;br /&gt;
&lt;br /&gt;
 def connectedComponents(graph):&lt;br /&gt;
        def visit(graph, node, anchors, anchor):&lt;br /&gt;
                '''&lt;br /&gt;
                anchor ist Anker der aktuellen ZK&lt;br /&gt;
                '''&lt;br /&gt;
                if anchors[node] is not None: return # Anker von &amp;lt;node&amp;gt; schon bekannt&lt;br /&gt;
                anchors[node] = anchor&lt;br /&gt;
                for neighbor in graph[node]&lt;br /&gt;
                        visit(graph, neighbor, anchors, anchor)&lt;br /&gt;
&lt;br /&gt;
        anchors = [None]*len(graph)&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                visit(graph, node, anchors, node) # node: Anker der naechste ZK = erster Knoten der ZK&lt;br /&gt;
        return anchors&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Union-Find-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
Eine Alternative (ohne Tiefensuche) waere z.B. ein Union-Find-Algorithmus. Idee dabei ist, dass eingangs jeder Knoten eine eigene ZK bildet, wobei in einer anschliessenden Rekursion Kanten gesucht werden, die zwischen den ZK bestehen.&lt;br /&gt;
&lt;br /&gt;
Initialisierung: jeder Knoten wird als 1 ZK behandelt&lt;br /&gt;
Rekursion: fasse ZK zusammen (Union) falls Kante zwischen ihnen existiert&lt;br /&gt;
Ergebnis: Array mit dem Anker jedes Knotens&lt;br /&gt;
&lt;br /&gt;
 def unionFindCC(graph):&lt;br /&gt;
        def findAnchor(anchors, k):&lt;br /&gt;
                '''&lt;br /&gt;
                Prueft auf anchors[k]==k&lt;br /&gt;
                '''&lt;br /&gt;
                while anchors[k] != k:&lt;br /&gt;
                        k = anchors[k]&lt;br /&gt;
                return k&lt;br /&gt;
&lt;br /&gt;
        def edges(graph):&lt;br /&gt;
                e = []&lt;br /&gt;
                for node in range(len(graph)):&lt;br /&gt;
                        for n in graph[node]:&lt;br /&gt;
                                if node &amp;lt; n:&lt;br /&gt;
                                        e.append((node, n))&lt;br /&gt;
                return e&lt;br /&gt;
&lt;br /&gt;
        anchors = range(len(graph)) # jeder Knoten ist sein eigener Anker&lt;br /&gt;
        for edge in edges(graph):&lt;br /&gt;
                # diese Schleife ordnet die Anker so, dass&lt;br /&gt;
                #   der 1. Anker immer der kleinste ist&lt;br /&gt;
                a1, a2 = findAnchor(anchors, edge[0]), findAnchor(anchors, edge[1])&lt;br /&gt;
                if a2 &amp;lt; a1: a2,a1 = a1,a2&lt;br /&gt;
                if a1 != a2: anchors[a2] = a1&lt;br /&gt;
        for node in range(len(graph)):&lt;br /&gt;
                # diese Schleife raeumt mit Indirektionen auf (s. Bsp. (#))&lt;br /&gt;
                anchors[node] = findAnchor(anchors, node)&lt;br /&gt;
&lt;br /&gt;
* Beispiel (#): ...&lt;br /&gt;
&lt;br /&gt;
Eine verbreitete Anwendung fuer dieses Verfahren gibt es in der Bildverarbeitung:&lt;br /&gt;
&lt;br /&gt;
* Beispiel: ...&lt;br /&gt;
&lt;br /&gt;
== Variationen der Tiefensuche (19.06.2008) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Algorithmen, die in der Vorlesung nicht behandelt werden ===&lt;br /&gt;
&lt;br /&gt;
* Max Flow (zur Bestimmung des maximalen Flusses durch ein Netzwerk, z.B. bei Ölpipelines)&lt;br /&gt;
* Matching (auch ''Paarung'' genannt): Teilmenge der Kanten eines Graphen, wobei keine zwei Kanten einen gleichen Knoten besitzen&lt;br /&gt;
*:Anwendungsbereiche: Zuordnung von Gruppen, z.B. Arbeitsamt (Zuordnung Arbeitssuchender - Stellenangebot), Universität (Zuordnung Studenten - Übungsgruppen) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachte Lösung für den ''acyclic''-Algorithmus ===&lt;br /&gt;
Zum Finden von Zyklen, bzw. der Feststellung, ob ein Graph azyklisch ist, verwenden wir&lt;br /&gt;
wieder eine modifizierte Version der Tiefensuche: Die Knoten werden wieder nach dem System der Tiefensuche besucht, und alle besuchten Knoten in einem Array visited abgespeichert. Es gibt einen Zyklus genau dann, wenn man zu&lt;br /&gt;
einem früheren Knoten (außer zum direkten Vorgaenger) zurückkommt.&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;code python&amp;gt;&lt;br /&gt;
     def acyclic(graph):&lt;br /&gt;
         def visit(graph, node, fromNode, visited):&lt;br /&gt;
             if visited[node]:			# Zyklus entdeckt&lt;br /&gt;
                 return False&lt;br /&gt;
             visited[node] = True&lt;br /&gt;
             for neighbor in graph[node]:&lt;br /&gt;
                 if neighbor == fromNode:	# überspringe Nachbar, von dem du gekommen bist&lt;br /&gt;
                     continue&lt;br /&gt;
                 if not visit(graph, neighbor, node, visited):&lt;br /&gt;
                     return False		# der Graph ist zyklisch&lt;br /&gt;
             return True			# kein Zyklus&lt;br /&gt;
         visited = [False]*len(graph)&lt;br /&gt;
         for node in range(len(graph)):&lt;br /&gt;
             if visited[node]:	# schließt aus, dass Knoten besucht wird, der schon besucht war&lt;br /&gt;
                 continue&lt;br /&gt;
             if not visit(graph, node, None, visited):&lt;br /&gt;
                 return False&lt;br /&gt;
         return True&lt;br /&gt;
   &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
&lt;br /&gt;
* Wenn ein Knoten bereits besucht ist, dann gehört er zur gleichen Zusammenhangskomponente - dies hat allerdings nichts mit einem Zyklus zu tun.&lt;br /&gt;
* Ein Graph der einmal zyklisch war wird nie wieder azyklisch.&lt;br /&gt;
* Der obige Algorithmus weist Ähnlichkeiten mit den bereits behandelten Algorithmen auf: '''ein guter Algorithmus zeichnet sich dadurch aus, dass mit kleinen Code-Variationen ganz andere Probleme gelöst werden können'''.&lt;br /&gt;
&lt;br /&gt;
=== Kürzeste Wege (Pfade) ===&lt;br /&gt;
&lt;br /&gt;
* Definition: gewichteter Graph&lt;br /&gt;
&lt;br /&gt;
 Jeder Kante e ist eine reelle oder natürliche Zahl w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; zugeordnet (wird auch als&lt;br /&gt;
 ''Kantengewicht'' bezeichnet).&lt;br /&gt;
&lt;br /&gt;
z.B. &lt;br /&gt;
* Abstand der Anfangs- und Endknoten&lt;br /&gt;
&lt;br /&gt;
* Durchflusskapazität eines Rohres (für max-Flussprobleme)&lt;br /&gt;
&lt;br /&gt;
* Wechselkurse (Darstellung in einem gerichteten Graph, da jede Kante auch eine Richtung hat. Die Knoten sind die Währungen, die Kanten sind die Wechselkurse. Auf diese Weise lassen sich unterschiedliche Wechselkurse + Bankgebühren darstellen.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Definition''': Problem des kürzesten Weges&lt;br /&gt;
&lt;br /&gt;
Sei P die Menge aller Wege von u nach v&lt;br /&gt;
&lt;br /&gt;
 P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt; = {u_v}&lt;br /&gt;
&lt;br /&gt;
und der Weg gegeben durch&lt;br /&gt;
&lt;br /&gt;
 u &amp;amp;rarr; x&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &amp;amp;rarr; x&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;amp;rarr; ... &amp;amp;rarr; v&lt;br /&gt;
&lt;br /&gt;
dann sind die Kosten eines Weges definiert durch&lt;br /&gt;
&lt;br /&gt;
 Kosten (P&amp;lt;sub&amp;gt;uv&amp;lt;/sub&amp;gt;) = &amp;lt;math&amp;gt;\sum\limits_{l \in Pv}&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* gesucht: Pfad u_v, so dass Kosten (u_v) minimal sind&lt;br /&gt;
&lt;br /&gt;
* Lösung: Algorithmus von Dijkstra&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Algorithmus von Dijkstra ===&lt;br /&gt;
&lt;br /&gt;
==== Edsger Wybe Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
geb. 11. Mai 1930 in Rotterdam&lt;br /&gt;
&lt;br /&gt;
ges. 06. August 2002&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dijkstra war ein niederländischer Informatiker und Wegbereiter der strukturierten Programmierung. 1972 erhielt er für seine Leistung in der Technik und Kunst der Programmiersprachen den Turing Award, der jährlich von der Association for Computing Machinery (ACM) an Personen verliehen wird, die sich besonders um die Entwicklung der Informatik verdient gemacht haben. Zu seinen Beiträgen zur Informatik gehören unter anderem der Dijkstra-Algorithmus zur Berechnung des kürzesten Weges in einem Graphen sowie eine Abhandlung über den go-to-Befehl und warum er nicht benutzt werden sollte. Der go-to-Befehl war in den 60er und 70er Jahren weit verbreitet, führte aber zu Spaghetti-Code. In seinem berühmten Paper &amp;quot;A Case against the GO TO Statement&amp;quot;[http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF], das als Brief mit dem Titel &amp;quot;Go-to statement considered harmful&amp;quot; veröffentlicht wurde, argumentiert Dijkstra, dass es umso schwieriger ist, dem Quellcode eines Programmes zu folgen, je mehr go-to-Befehle darin enthalten sind und zeigt, dass man auch ohne diesen Befehl gute Programme schreiben kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;code python&amp;gt;&lt;br /&gt;
    import heapq	# heapq ist ein Modul von Python&lt;br /&gt;
    def dijkstra(graph, start, ziel):	# graph: gewichtete Adjazenzliste&lt;br /&gt;
        heap = []&lt;br /&gt;
        visited = [None]*len(graph)&lt;br /&gt;
        visited[start] = start&lt;br /&gt;
        for neighbor in graph[start]:&lt;br /&gt;
            heapq.heappush(heap, (neighbor[1], start, neighbor[0])) # neighbor[1]:Kantengewicht,neighbor[0]:Endpunkt d. K.&lt;br /&gt;
        while len(heap) &amp;gt; 0:	# solange der heap nicht leer ist&lt;br /&gt;
            w, fromNode, node = heapq.heappop(heap)&lt;br /&gt;
            if visited[node] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                continue&lt;br /&gt;
            visited[node] = fromNode    # baue Vorgänger-Baum&lt;br /&gt;
            if node == ziel:	# da der heap noch nicht leer ist, wird an dieser Stelle ein break benötigt&lt;br /&gt;
                break&lt;br /&gt;
            for neighbor in graph[node]:&lt;br /&gt;
                if visited[neighbor[0]] is not None:	# wenn der kürzeste Pfad bereits bekannt ist, überspringe ihn&lt;br /&gt;
                    continue&lt;br /&gt;
                heapq.heappush(heap, (neighbor[1]+w, node, neighbor[0]))&lt;br /&gt;
        bestPath = []&lt;br /&gt;
        t = ziel&lt;br /&gt;
        while t != visited[t]:		# Array wird durchlaufen bis der Anker des Pfades gefunden ist, vgl. Union-Search&lt;br /&gt;
            bestPath.append(t)&lt;br /&gt;
            t=visited[t]&lt;br /&gt;
        bestPath.append(start)&lt;br /&gt;
        return bestPath			# bestPath.reverse()&lt;br /&gt;
  &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Anmerkungen zum Code:'''&lt;br /&gt;
* der graph ist eine gewichtete Adjazenzliste&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | Endknoten || (Nr. der Nachbarn des Knoten 0)&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||  || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || || style=&amp;quot;background:silver; color:white&amp;quot; | Gewicht || (Gewicht der jeweiligen Kante)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
* Eingabe z.B.:&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
| Knoten || style=&amp;quot;background:silver; color:white&amp;quot; | 0 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | (1, 0.3) || style=&amp;quot;background:silver; color:white&amp;quot; | (3, 0.1) || style=&amp;quot;background:silver; color:white&amp;quot; | (5, 1.2) ||&lt;br /&gt;
|- &lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 1 || &amp;amp;rarr; || style=&amp;quot;background:silver; color:white&amp;quot; | || style=&amp;quot;background:silver; color:white&amp;quot; |  || style=&amp;quot;background:silver; color:white&amp;quot; |  ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 3 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 5 ||&lt;br /&gt;
|-&lt;br /&gt;
| || style=&amp;quot;background:silver; color:white&amp;quot; | 6 ||&lt;br /&gt;
|}&lt;br /&gt;
* heapq() verwendet den 1. Eintrag des Tupels zum sortieren des heap&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Prinzip des Dijkstra-Algorithmus ====&lt;br /&gt;
&lt;br /&gt;
* Algorithmus ist Tiefensuche mit Prioritätswarteschlange (Heap) statt eines Stapelspeichers (Stack) &amp;amp;rarr; vgl. Übung 8&lt;br /&gt;
&lt;br /&gt;
* Die Prioritätswarteschlange speichert die kürzesten Wege, die bereits gefunden worden sind.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch eine Warteschlange (Queue) ersetzt, erhält man Breitensuche.&lt;br /&gt;
&lt;br /&gt;
* Wenn man die Prioritätswarteschlange (Heap) durch einen Stapelspeicher (Stack) ersetzt, erhält man Tiefensuche.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Beispiel ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Bsp.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* An der Stelle &amp;quot;neighbor[1]&amp;quot; wird eine Zählvariable ''count'' eingefügt, die hoch (Breitensuche) oder runter (Tiefensuche) zählt.&lt;br /&gt;
&lt;br /&gt;
* Die Gewichte werden hoch- oder runtergezählt, so wie die Kanten gesehen wurden.&lt;br /&gt;
&lt;br /&gt;
* Wenn man rückwärts zählt (von 0 abziehen), werden die zuletzt hinzugefügten Kanten expandiert.&lt;br /&gt;
&lt;br /&gt;
* '''Algorithmus von Dijkstra funktioniert &amp;lt;u&amp;gt;nur&amp;lt;/u&amp;gt; für &amp;lt;u&amp;gt;positive&amp;lt;/u&amp;gt; Kantengewichte&lt;br /&gt;
*:&amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt; w&amp;lt;sub&amp;gt;e&amp;lt;/sub&amp;gt; &amp;gt; 0'''&lt;br /&gt;
&lt;br /&gt;
* Bei negativen Kantengewichten könnte es Zyklen geben, die negative Kosten für den ganzen Zyklus haben:&lt;br /&gt;
&lt;br /&gt;
     /\		1. Durchlauf: Kosten -1&lt;br /&gt;
  1 /  \ 4	2. Durchlauf: Kosten -2&lt;br /&gt;
   /____\	etc.&lt;br /&gt;
      2&lt;br /&gt;
&lt;br /&gt;
* Verwendung bei arbitragen Geschäften (Börsengeschäfte, die die Preis-, Kurs- und Zinsunterschiede auf verschiedenen Märkten ausnutzen):&lt;br /&gt;
*:EURO wurden in YEN, YEN in DOLLAR gewechselt und das Geld hat sich dadurch vermehrt&lt;br /&gt;
* Für negative Kantengewichte verwendet man den Bellman-Ford-Allgorithmus, der allerdings langsamer ist, als der Dijkstra-Algorithmus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Komplexität von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten wird höchstens 1x expandiert (Iteration über die Nachbarn des Knotens).&lt;br /&gt;
&lt;br /&gt;
* Jeder Knoten kann mehrmals im Heap enthalten sein.&lt;br /&gt;
&lt;br /&gt;
* Es sind aber höchstens E (Anzahl der Kanten) Heap-Einträge möglich, da jede Kante höchstens 1 Heap-Eintrag generiert (ein Knoten ist nur dann im Heap, wenn man ihn über eine Kante erreicht hat, die man vorher noch nicht besucht hatte). Deshalb können nie mehr Einträge im Heap sein, als es Kanten gibt. Die Komplexität von heappush(), heappop() ist&lt;br /&gt;
 O(log E) = O(2 log v) = O(log v) &lt;br /&gt;
wenn alle Kanten einen Heap-Eintrag generiert haben.&lt;br /&gt;
* Die while-Schleife wird im schlimmsten Fall E mal durchlaufen, deshalb ist die Komplexität von Dijkstra O(E log v).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Korrektheit von Dijkstra ====&lt;br /&gt;
&lt;br /&gt;
* Falls &lt;br /&gt;
 visited[node] (Schleifen-Invariante von while) != None &lt;br /&gt;
ist, dann liefert Zurückverfolgen des Pfades von node nach start den kürzesten Pfad von start nach node (gilt für alle Knoten, für die das visited-Feld gesetzt ist).&lt;br /&gt;
* Induktionsanfang: visited[start] ist einziger not-None-Fall &amp;amp;rarr; Bedingung erfüllt&lt;br /&gt;
* Induktionsschritt: wenn visited[node] gesetzt wird, ist es ein kürzester Pfad&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Indirekter Beweis ====&lt;br /&gt;
&lt;br /&gt;
Set S = {node | visited[node] != None} (alle Knoten, von denen wir den kürzesten Pfad schon kennen)&lt;br /&gt;
&lt;br /&gt;
* u ist der Knoten an der Spitze des Heaps&lt;br /&gt;
* fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S (ein Nachbar von node kommt erst dann in den Heap, wenn visited[node] vorher gesetzt wurde)&lt;br /&gt;
* falls u &amp;amp;rarr; fromNode &amp;amp;rarr start kein kürzester Pfad wäre, müsste u's Vorgänger in V\S sein&lt;br /&gt;
* sei dieser Vorgänger x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S, x &amp;lt;math&amp;gt;\not=&amp;lt;/math&amp;gt; u&lt;br /&gt;
* sei w&amp;lt;sub&amp;gt;x&amp;lt;/sub&amp;gt; das Gewicht der Kante x &amp;amp;rarr; u, dann sind die Kosten für start nach u gleich&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_u) = Kosten(start_x) + wx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Annahme des indirekten Beweises:&lt;br /&gt;
&lt;br /&gt;
  Kosten(start_fromNode) + w&amp;lt;sub&amp;gt;fromNode&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Behauptung des indirekten Beweises:&lt;br /&gt;
 Es gibt einen anderen Pfad x, so dass die Kosten von start nach x geringer sind&lt;br /&gt;
&lt;br /&gt;
* Da aber gilt:&lt;br /&gt;
 fromNode &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S und x &amp;lt;math&amp;gt;\notin&amp;lt;/math&amp;gt; S&lt;br /&gt;
&lt;br /&gt;
* gilt (Induktionsvoraussetzung):&lt;br /&gt;
  Kosten(start_fromNode) &amp;lt; Kosten(start_x)&lt;br /&gt;
&lt;br /&gt;
* Falls Kosten(start_x) &amp;lt; Kosten(start_u) müsste x im Heap vor u kommen; daraus folgt, dass u nicht an der Spitze des Heaps sein kann&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Widerspruch!&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Die Behauptung, der Weg über x ist besser, kann nicht stimmen.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;rarr; Korrektheit von Dijkstra ist somit bewiesen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wie kann man Dijkstra noch verbessern? ====&lt;br /&gt;
&lt;br /&gt;
===== A*-Algorithmus =====&lt;br /&gt;
&lt;br /&gt;
* Verbesserung von Dijkstra im typischen Fall, aber die Komplexität ist immer noch =(Elog v) im schlechtesten Fall (die Komplexität kann man nicht verbessern, aber die Laufzeit im typischen Fall).&lt;br /&gt;
* &amp;lt;u&amp;gt;Schätzung&amp;lt;/u&amp;gt; für jeden Knoten für den restlichen Weg: &lt;br /&gt;
geschätzte Gesamtkosten: Kosten(start_node) + Restschätzung(node_ziel)&lt;br /&gt;
(exakte Kosten werden durch Dijkstra ermittelt)&lt;br /&gt;
&lt;br /&gt;
'''Idee:'''&lt;br /&gt;
* Sortiere den Heap nach geschätzten Gesamtkosten.&lt;br /&gt;
* Satz: &lt;br /&gt;
 Falls jede Schätzung den exakten Weg &amp;lt;u&amp;gt;unterschätzt&amp;lt;/u&amp;gt;, werden die gleichen Pfade gefunden, wie &lt;br /&gt;
 bei Dijkstra (also die korrekten kürzesten Pfade).&lt;br /&gt;
(Die Schätzung für den restlichen Weg muss man immer so einrichten, dass der tatsächliche Weg unterschätzt wird. Da keine Straße kürzer sein kann als die Luftlinie, ist die Luftlinie eine geeignete Annahme für A*.)&lt;br /&gt;
* Falls der falsche Pfad im Heap eher an die Spitze kommt als der richtige Pfad, findet der A*-Algorithmus den falschen Pfad.&lt;br /&gt;
* Wenn der Pfad zum Ziel an der Spitze des Heap ist, dann wird keine Restschätzung mehr benötigt, denn wenn der Zielknoten aus dem Heap herrauskommt, dann hat man die exakte Berechnung. Die Restschätzung ist in diesem Fall 0. Wenn die Schätzung zu klein ist, wird der exakte Weg immer größer sein und zuerst aus dem Heap herauskommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Minimum_spanning_tree.png‎ |thumb|200px|right|Ein minimal aufspannender Baum verbindet alle Punkte eines Graphen bei minimaler Kantenlänge ([http://de.wikipedia.org/wiki/Spannbaum Quelle])]]&lt;br /&gt;
=='''Minimaler Spannbaum'''==&lt;br /&gt;
'''(engl.: minimum spanning tree; abgekürzt: MST)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: gewichteter Graph, zusammenhängend&amp;lt;br/&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: Untermenge &amp;lt;math&amp;gt;E'\subseteq E&amp;lt;/math&amp;gt;, so dass &amp;lt;math&amp;gt;\sum_{e\in E} w_e&amp;lt;/math&amp;gt; minimal und G' zusammenhängend ist. &amp;lt;br/&amp;gt;&lt;br /&gt;
* G'definiert dann einen Baum, denn andernfalls könnte man &amp;lt;math&amp;gt;\sum_{E'}&amp;lt;/math&amp;gt;verringern (eine Kante weglassen) ohne die Zusammenhangskomponente zu verletzen. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Wenn der Graph nicht zusammenhängend ist, würde man den Spannbaum für jede Zusammenhangskomponente getrennt ausrechnen. &lt;br /&gt;
* Der MST ist ähnlich wie der Dijkstra-Algorithmus: Dort ist ein Pfad gesucht bei dem die Summe der Gewicht über den Pfad minimal ist. &lt;br /&gt;
* Beim MST suchen wir eine Lösung bei der die Summe der Gewichte über den ganzen Graphen minimal ist. &lt;br /&gt;
&lt;br /&gt;
* Das Problem des MST ist nahe verwandt mit der Bestimmung der Zusammenhangskomponente, z.B. über den Tiefensuchbaum, wobei ein beliebiger Baum für die Zusammenhangskomponente und beim MST ein minimaler Baum gesucht ist.&lt;br /&gt;
&lt;br /&gt;
;Anwendungen&lt;br /&gt;
* '''Wie verbindet man ''n'' Punkte mit möglichst wenigen (kurzen) Straßen (Eisenbahnen, Drähten (bei Schaltungen) usw.)?'''&lt;br /&gt;
&lt;br /&gt;
[[Image:mst.png|thumb|250px|none|MST minimale Verbindung (Abb.1)]] &lt;br /&gt;
[[Image:Gleichseitigesdreieck.png|thumb|250px|none|MST = 2 (Länge = Kantengewicht)(Abb.2)]]&lt;br /&gt;
&lt;br /&gt;
*In der Praxis: Die Festlegung, dass man nur die gegebenen Punkte verwenden darf, ist eine ziemliche starke Einschränkung. &lt;br /&gt;
&lt;br /&gt;
* Wenn man sich vorstellt, es sind drei Punkte gegeben, die als gleichseitiges Dreieck angeordnet sind, dann ist der MST (siehe Abb.2, schwarz gezeichnet) und hat die Länge 2. Man kann hier die Länge als Kantengewicht verwenden. &lt;br /&gt;
&lt;br /&gt;
* Wenn es erlaubt ist zusätzliche Punkte einzufügen, dann kann man in der Mitte einen neuen Punkt setzen &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; neuer MST (siehe Abb.2, orange gezeichnet).&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Höhe = &amp;lt;math&amp;gt;\frac{1}{2}\sqrt{3}&amp;lt;/math&amp;gt;, Schwerpunkt: teilt die Höhe des Dreiecks im Verhältnis 2:1; der Abstand von obersten Punkt bis zum neu eingeführten Punkt: &amp;lt;math&amp;gt;\frac{2}{3}h = \frac{\sqrt{3}}{3}&amp;lt;/math&amp;gt;, davon insgesamt 3 Stück, damit (gilt für den MST in orange eingezeichnet): MST = &amp;lt;math&amp;gt;3\left(\frac{1}{3}\right) \sqrt{3} = \sqrt{3} \approx 1,7&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Damit ist der MST in orange kürzer als der schwarz gezeichnete MST. &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;\Rightarrow&amp;lt;/math&amp;gt;Folgerung: MST kann kürzer werden, wenn man einen Punkt dazu nimmt. &lt;br /&gt;
* Umgekehrt kann der MST auch kürzer werden, wenn man einen Punkt aus dem Graphen entfernt, aber wie das Beipiel des gleichseitigen Dreiecks zeigt, ist dies nicht immer der Fall.&lt;br /&gt;
&lt;br /&gt;
[[Image: bahn.png|thumb|250px|none|Bahnstrecke Verbindung (Abb.3)]]&lt;br /&gt;
&lt;br /&gt;
* Methode der zusätzlichen Punkteinfügung hat man früher beim Bahnstreckenbau verwendet. Durch Einführung eines Knotenpunktes kann die Streckenlänge verkürzt werden (Dreiecksungleichung).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Bestimmung von Datenclustern'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:cluster.png|none|350px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Daten (in der Abb.: Punkte) bilden Gruppen. &lt;br /&gt;
&lt;br /&gt;
* In der Abbildung hat man 2 verschiedene Messungen gemacht (als x- und y-Achse aufgetragen), bspw. Größe und Gewicht von Personen. Für jede Person i wird ein Punkt an der Koordinate (Größe&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;, Gewicht&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;) gezeichnet (siehe Bild a). Dies bezeichnet man als ''Scatter Plot''. Wenn bestimmte Wertkombinationen häufiger auftreten als andere, bilden sich mitunter Gruppen aus, bspw. eine Gruppe für &amp;quot;klein und schwer&amp;quot; etc.&lt;br /&gt;
&lt;br /&gt;
* Durch Verbinden der Punkte mittels eines MST (siehe Abbildung (b)) sieht man, dass es kurze (innerhalb der Gruppen) und lange Kanten (zwischen den Gruppen) gibt. &lt;br /&gt;
&lt;br /&gt;
* Wenn man geschickt eine Schwelle einführt und alle Kanten löscht, die länger sind als die Schwelle, dann bekommt man als Zusammenhangskomponente die einzelnen Gruppen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei Algorithmen für dieses Problem &lt;br /&gt;
(im Vergleich zu Algorithmen für die Zusammenhangskomponente nur leicht verbesserte Algorithmen)&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Prim====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Prim#Hashing_mit_offener_Adressierung Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
:Idee: starte an der Wurzel (willkürlich gewählter Knoten) und füge jeweils die günstigste Kante hinzu (&amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt; genau wie beim Dijsktra-Algorithmus, aber die Definitionen, welche Kante die günstigste ist, unterscheiden sich.)&lt;br /&gt;
&lt;br /&gt;
 import heapq&lt;br /&gt;
 def prim(graph):  #Graphdatenstruktur ist wie bei Dijsktra  &lt;br /&gt;
 	heap = []                                       &lt;br /&gt;
 	visited = [False]*len(graph) &lt;br /&gt;
 	sum = 0  #wird später das Gewicht des Spannbaums sein&lt;br /&gt;
 	r = []  #r ist die Lösung&lt;br /&gt;
 	visited[0] = True #fixed&lt;br /&gt;
 	for neighbor in graph[0]:  #willkürlich 0 als Wurzel gewählt&lt;br /&gt;
 		heapq.heappush(heap, (neighbor[1], 0, neighbor[0]))  #Heap wird gefüllt &lt;br /&gt;
 	while len(heap):&lt;br /&gt;
 		wn, start, ziel = heapq.heappop(heap) &lt;br /&gt;
 		if visited[ziel]: continue&lt;br /&gt;
 		visited[ziel] = True  #wenn visited noch nicht besetzt&lt;br /&gt;
 		sum += wn  #Addition des Gewichts der aktuellen Kante&lt;br /&gt;
 		r.append([start, ziel])  #Kante wird an die Lsg. angehängt&lt;br /&gt;
 		for neighbor in graph[ziel]:&lt;br /&gt;
 			if visited[neighbor[0]]: continue&lt;br /&gt;
 			heapq.heappush(heap, (neighbor[1], ziel, neighbor[0]))&lt;br /&gt;
 	return sum, r&lt;br /&gt;
&lt;br /&gt;
====Algorithmus von Kruskal====&lt;br /&gt;
[http://de.wikipedia.org/wiki/Algorithmus_von_Kruskal Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Kruskal%27s_algorithm (en)]&lt;br /&gt;
&lt;br /&gt;
Eine andere Vorgehensweise zur Bestimmung des minimalen Spannbaums besteht darin, einfach Kanten nacheinander hinzuzufügen und hierbei bei jedem Schritt die kürzeste Kante zu verwenden, die keinen Zyklus bildet. Anders ausgedrückt: Der Algorithmus beginnt mit ''N'' Bäumen; in ''N'' Schritten kombiniert er jeweils zwei Bäume (unter Verwendung der kürzesten möglichen Kante), bis nur noch ein Baum übrig bleibt. &lt;br /&gt;
Der Algorithmus von J.Kruskal ist seit 1956 bekannt. &lt;br /&gt;
&lt;br /&gt;
* Idee: wie beim Union-Find-Algorithmus für Zusammenhangskomponenten&lt;br /&gt;
&lt;br /&gt;
# Behandle jeden Knoten als Baum für sich&lt;br /&gt;
# Fasse zwei Bäume zu einem neuen Baum zusammen&lt;br /&gt;
&lt;br /&gt;
* für MST (im Unterschied zu Union-Find): betrachte dazu die Kanten in aufsteigender Reihenfolge der Gewichte&lt;br /&gt;
(priority queue; ignoriere Kanten zwischen Knoten in gleichem Baum)&lt;br /&gt;
&lt;br /&gt;
* Algorithmus eignet sich besser für das Clusteringproblem, da der Schwellwert von vornerein über die Kantenlänge an den Algorithmus übergeben werden kann. Man hört mit dem Vereinigen auf, wenn die Kantenlänge den Schwellwert überschreitet. &lt;br /&gt;
*Es kann keine kürzere Kante als der Schwellwert mehr kommen, da die Kanten vorher sortiert worden sind. &lt;br /&gt;
&lt;br /&gt;
''Komplexität:'' gleich wie beim Dijkstra-Algorithmus, weil jede Kante kommt höchstens einmal in den Heap.&lt;br /&gt;
* Aufwand für Heap ist max. &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; Einträge, da jede Kante nur einmal im Heap sein kann, d.h. Heap hat den Aufwand: &amp;lt;math&amp;gt;O\left(E\log E\right)&amp;lt;/math&amp;gt;, falls keine Mehrfachkanten vorhanden: &amp;lt;math&amp;gt;v^2 &amp;gt; E&amp;lt;/math&amp;gt; und deshalb: log E &amp;lt; 2 log v. &lt;br /&gt;
* Daraus folgt, dass das dasselbe ist wie &amp;lt;math&amp;gt;O \left(E\log v\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=&amp;gt; geeignet für Übungsaufgabe&lt;br /&gt;
&lt;br /&gt;
== Problem des Handlungsreisenden ==&lt;br /&gt;
'''(engl.: Traveling Salesman Problem; abgekürzt: TSP)'''&amp;lt;br\&amp;gt;&lt;br /&gt;
[http://de.wikipedia.org/wiki/Problem_des_Handlungsreisenden Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Prim%27s_algorithm (en)]&lt;br /&gt;
[[Image:TSP_Deutschland_3.PNG|thumb|200px|right|Optimaler Reiseweg eines Handlungsreisenden([http://de.wikipedia.org/w/index.php?title=Bild:TSP_Deutschland_3.PNG&amp;amp;filetimestamp=20070110124506 Quelle])]]&lt;br /&gt;
&lt;br /&gt;
*Eine der wohl bekanntesten Aufgabenstellungen im Bereich der Graphentheorie ist das Problem des Handlungsreisenden. &lt;br /&gt;
*Hierbei soll ein Handlungsreisender nacheinander ''n'' Städte besuchen und am Ende wieder an seinem Ausgangspunkt ankommen. Dabei soll jede Stadt nur einmal besucht werden und der Weg mit den minimalen Kosten gewählt werden. &lt;br /&gt;
*Alternativ kann auch ein Weg ermittelt werden, dessen Kosten unter einer vorgegebenen Schranke liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zusammenhängender, gewichteter Graph (oft vollständiger Graph)&lt;br /&gt;
:&amp;lt;u&amp;gt;''gesucht''&amp;lt;/u&amp;gt;: kürzester Weg, der alle Knoten genau einmal (falls ein solcher Pfad vorhanden) besucht (und zum Ausgangsknoten zurückkehrt)&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:auch genannt: kürzester Hamiltonpfad (Pfad der alle Knoten einmal besucht): &lt;br /&gt;
::- durch psychologische Experimente wurde herausgefunden, dass der Menschen (in 2D) &amp;lt;math&amp;gt;\approx&amp;lt;/math&amp;gt; proportionale Zeit zur Anzahl der Knoten braucht, um den Pfad zu finden, &amp;lt;math&amp;gt;O(V)&amp;lt;/math&amp;gt; (linear) (&amp;lt;math&amp;gt;\lesssim 5%&amp;lt;/math&amp;gt; länger als optimaler Pfad ist typisch) &amp;lt;br\&amp;gt;&lt;br /&gt;
:&amp;lt;u&amp;gt;''vorgegeben''&amp;lt;/u&amp;gt;: Startknoten (kann willkürlich gewählt werden), vollständiger Graph&lt;br /&gt;
&lt;br /&gt;
::::: =&amp;gt; v-1 Möglichkeiten für den ersten Nachfolgerknoten =&amp;gt; je v-2 Möglichkeiten für dessen Nachfolger...&lt;br /&gt;
:::::also &amp;lt;math&amp;gt;\frac{(v-1)!}{2}&amp;lt;/math&amp;gt; mögliche Wege in einem vollständigen Graphen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Ein naiver Ansatz zur Lösung des TSP Problems ist das erschöpfende Durchsuchen des Graphen, auch &amp;quot;brute force&amp;quot; Algorithmus (&amp;quot;mit roher Gewalt&amp;quot;), indem alle möglichen Rundreisen betrachtet werden und schließlich die mit den geringsten Kosten ausgewählt wird. &lt;br /&gt;
*Dieses Verfahren versagt allerdings bei größeren Graphen, aufgrund der hohen Komplexität.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
'''Systematisches Erzeugen aller Permutationen'''&amp;lt;br\&amp;gt;&lt;br /&gt;
*Allgemeines Verfahren, wie man von einer gegebenen Menge verschiedene Schlüssel - in diesem Fall: Knotennummern - sämtliche Permutationen systematisch erzeugen kann. &amp;lt;br\&amp;gt;&lt;br /&gt;
*'''Trick''': erzeuge jede Permutation als Wort und betrachte dann deren lexikographische (&amp;quot;wie im Lexikon&amp;quot;) Ordnung.&amp;lt;br\&amp;gt;&lt;br /&gt;
*Der erste unterschiedliche Buchstabe unterscheidet. Wenn die Buchstaben gleich sind, dann kommt das kürzere Wort zuerst. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;''gegeben''&amp;lt;/u&amp;gt;: zwei Wörter a,b &amp;lt;br\&amp;gt;&lt;br /&gt;
mathematisch formuliert, wie die Wörter im Wörterbuch sortiert sind: &amp;lt;br\&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;a&amp;lt;b \Leftrightarrow i&amp;lt;min(len(a), len(b))&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[i] == b[i] i&amp;lt;j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
:::::::&amp;lt;math&amp;gt;a[j] &amp;lt; b[j] i == j&amp;lt;/math&amp;gt;&amp;lt;br\&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder falls &amp;lt;math&amp;gt;a[i] == b[i]  \forall i &amp;lt; n &amp;lt;/math&amp;gt;&lt;br /&gt;
:::&amp;lt;math&amp;gt;len(a) &amp;lt; len(b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# beginne mit dem kleinsten Wort =&amp;gt; ist der Fall, wenn a aufsteigend sortiert&lt;br /&gt;
# definiere Funktion &amp;quot;next_permutation&amp;quot;, die den Nachfolger in lexikographischer Ordnung erzeugt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel: Die folgenden Permutationen der Zahlen 1,2,3 sind lexikographisch geordnet&lt;br /&gt;
&lt;br /&gt;
 1 2 3    6 Permutationen, da 3! = 6&lt;br /&gt;
 1 3 2&lt;br /&gt;
 2 1 3&lt;br /&gt;
 2 3 1&lt;br /&gt;
 3 1 2&lt;br /&gt;
 3 2 1&lt;br /&gt;
 -----&lt;br /&gt;
 0 1 2 Position&lt;br /&gt;
&lt;br /&gt;
Die lexikographische Ordnung wird deutlicher, wenn wir statt dessen die Buchstaben a,b,c verwenden:&lt;br /&gt;
&lt;br /&gt;
 abc&lt;br /&gt;
 acb&lt;br /&gt;
 bac&lt;br /&gt;
 bca&lt;br /&gt;
 cab&lt;br /&gt;
 cba&lt;br /&gt;
&lt;br /&gt;
Eine Funktion, die aus einer gegebenen Permutation die in lexikographischer Ordnung nächst folgende erzeugt, kann wie folgt implementiert werden:&lt;br /&gt;
&lt;br /&gt;
 def next_permutation(a):&lt;br /&gt;
 	i = len(a) -1  #letztes Element; man arbeitet sich von hinten nach vorne durch&lt;br /&gt;
 	while True:  # keine Endlosschleife, da i dekrementiert wird und damit irgendwann 0 wird&lt;br /&gt;
 		if i &amp;lt;= 0: return False  # a ist letzte Permutation&lt;br /&gt;
 		i -= 1&lt;br /&gt;
 		if a[i]&amp;lt;a[i+1]: break&lt;br /&gt;
 	#lexikogr. Nachfolger hat größeres a[i]&lt;br /&gt;
 	j = len(a)&lt;br /&gt;
 	while True:&lt;br /&gt;
 		j -= 1&lt;br /&gt;
 		if a[i] &amp;lt; a[j]: break&lt;br /&gt;
 	a[i], a[j] = a[j], a[i] #swap a[i], a[j]&lt;br /&gt;
 	#sortiere aufsteigend zwischen a[i] und Ende&lt;br /&gt;
 	#zur Zeit absteigend sortiert =&amp;gt; invertieren&lt;br /&gt;
 	i += 1&lt;br /&gt;
 	j = len(a) -1&lt;br /&gt;
 	while i &amp;lt; j:&lt;br /&gt;
 		a[i], a[j] = a[j], a[i]&lt;br /&gt;
 		i += 1&lt;br /&gt;
 		j-= 1&lt;br /&gt;
 	return True  # eine weitere Permutation gefunden&lt;br /&gt;
  	&lt;br /&gt;
  def naiveTSP(graph):&lt;br /&gt;
 	start = 0&lt;br /&gt;
 	result = range(len(graph))+[start]&lt;br /&gt;
 	rest = range(1,len(graph))&lt;br /&gt;
 	c = pathCost(result, graph)&lt;br /&gt;
 	while next_permutation(rest):&lt;br /&gt;
 		r = [start]+rest+[start]&lt;br /&gt;
 		cc = pathCost(r, graph)&lt;br /&gt;
 		if cc &amp;lt; c:&lt;br /&gt;
 			c = cc&lt;br /&gt;
 			result = r&lt;br /&gt;
 		return c, result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Komplexität''': &amp;lt;math&amp;gt;(v-1)!&amp;lt;/math&amp;gt; Schleifendurchläufe (=Anzahl der Permutationen, da die Schleife abgebrochen wird, sobald es keine weiteren Permutationen mehr gibt), also &lt;br /&gt;
	&amp;lt;math&amp;gt;O(v!) = O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Beispiel:&lt;br /&gt;
{| &lt;br /&gt;
|- &lt;br /&gt;
| | i = 0 || |  |||  ||| j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|| &amp;amp;darr; || || || &amp;amp;darr; ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 2 || #input für next_permutation&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  || i = 2 || ||  j = 3 ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || &amp;amp;darr;|| || &amp;amp;darr; ||&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1|| # vertauschen der beiden Elemente &lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
||  ||  ||i = 2 ||   ||&lt;br /&gt;
|-&lt;br /&gt;
||  ||  ||j = 2 ||   ||&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
||  || || &amp;amp;darr;|| ||&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;background:silver; color:white&amp;quot; | 2 ||style=&amp;quot;background:silver; color:white&amp;quot; | 1 ||style=&amp;quot;background:silver; color:white&amp;quot;| 3 ||style=&amp;quot;background:silver; color:white&amp;quot; | 4|| #absteigend sortiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Stirling'sche Formel: &lt;br /&gt;
[http://de.wikipedia.org/wiki/Stirling-Formel Wikipedia (de)]&lt;br /&gt;
[http://en.wikipedia.org/wiki/Stirling%27s_approximation (en)]&lt;br /&gt;
&lt;br /&gt;
Die Stirling-Formel ist eine mathematische Formel, mit der man für große Fakultäten Näherungswerte berechnen kann. Die Stirling-Formel findet überall dort Verwendung, wo die exakten Werte einer Fakultät nicht von Bedeutung sind. Damit lassen sich durch die Sterling Formel z.T. starke Vereinfachungen erzielen. &lt;br /&gt;
&amp;lt;math&amp;gt;v! \approx \sqrt{2 \pi v} \left(\frac{v}{e}\right)^v&amp;lt;/math&amp;gt;&lt;br /&gt;
: &amp;lt;math&amp;gt;O(v!) = O\left(\sqrt{v}\left(\frac{v}{e}\right)^v\right) \approx O(v^v)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Erfüllbarkeitsproblem ==&lt;br /&gt;
&lt;br /&gt;
== Stark zusammenhängende Komponenten ==&lt;br /&gt;
&lt;br /&gt;
geg.: gerichteter Graph&lt;br /&gt;
&lt;br /&gt;
    1. Bestimme die post Order Time (mit Tiefensuche)&lt;br /&gt;
    2. Transponieren des Graphen &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt;&lt;br /&gt;
    3. Bestimme ConnComp &amp;lt;math&amp;gt;G^T&amp;lt;/math&amp;gt; mit bekannten CC Algorithmen, aber so, dass Knoten in absteigender post Order behandelt werden&lt;br /&gt;
&lt;br /&gt;
[[Image:Curva.png|thumb|250px|none]]    Beweis: 1.Bilde Komponentengraphen:&lt;br /&gt;
    '''Knoten:''' jede SCC &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; ist ein Knoten&lt;br /&gt;
    '''Kanten:''' &amp;lt;math&amp;gt;C_i \rightarrow C_j \Leftrightarrow U_k \rightarrow U_l&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;U_k \in C_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_l \in C_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    '''*Eigenschaft 1:''' der Komponentengraph ist :&amp;lt;u&amp;gt;''azyklisch''&amp;lt;/u&amp;gt;:&lt;br /&gt;
     &amp;lt;math&amp;gt;pot \left(C_i\right) = max_{U_k \in C_i}  pot\left(U_k\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 2:''' falls &amp;lt;math&amp;gt;C_i \rightsquigarrow C_j&amp;lt;/math&amp;gt; dann &amp;lt;math&amp;gt;pot \left(C_i\right) &amp;gt; pot \left(C_j\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     (ausserdem gilt: es gibt keinen Weg &amp;lt;math&amp;gt;C_j \rightsquigarrow C_i&amp;lt;/math&amp;gt; )&lt;br /&gt;
     aber: in transponierten Graphen sind alle Kanten umgedreht&lt;br /&gt;
    &lt;br /&gt;
    '''*Eigenschaft 3:''' falls &amp;lt;math&amp;gt;{C_j}^T \rightsquigarrow {C_i}^T&amp;lt;/math&amp;gt; , dann gilt &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigenschaft 2-3 &amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; im transponierten Graphen gibt es nie einen Pfad &amp;lt;math&amp;gt;{C_i}^T \rightsquigarrow {C_j}^T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;pot \left({C_i}^T\right) &amp;gt; pot \left({C_j}^T\right)&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Schritt 3 des Algorithmus kann von einem geg. Startknoten &amp;lt;u&amp;gt;''nur''&amp;lt;/u&amp;gt; die Knoten derselben SCC erreichen&lt;br /&gt;
 &lt;br /&gt;
q.e.d.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Anwendung auf 2-SAT Problem ==&lt;br /&gt;
&lt;br /&gt;
geg.: Implikationen-Normalform, dargestellt als gerichteter Graph.&lt;br /&gt;
&lt;br /&gt;
Eigenschaft: alle Variablen in derselben SCC müssen den gleichen Wert haben, weil &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\underbrace{x_i \rightsquigarrow x_j \stackrel{\wedge}{=} x_i \rightarrow x_j; \;\;\;     x_j \rightsquigarrow x_i \stackrel{\wedge}{=} x_j \rightarrow x_i}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:::::&amp;lt;math&amp;gt;\;\;\;x_i == x_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\rightarrow \; x_i  \; und \; \neg x_i&amp;lt;/math&amp;gt; dürfen nie in derselben SCC sein, weil &amp;lt;math&amp;gt;\rightarrow \; x_i == \neg x_i&amp;lt;/math&amp;gt; unmöglich ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Longrightarrow&amp;lt;/math&amp;gt; Algorithmus für Erfüllbarkeit: teste die Eigenschaft&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Das funktioniert leider nicht für k-SAT mit &amp;lt;math&amp;gt;k&amp;gt;2&amp;lt;/math&amp;gt;'''&lt;/div&gt;</summary>
		<author><name>LDream</name></author>	</entry>

	</feed>