<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://wiki.flbk-hamm.de/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Flbkwikiadmin</id>
	<title>FLBK-Wiki - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.flbk-hamm.de/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Flbkwikiadmin"/>
	<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/Spezial:Beitr%C3%A4ge/Flbkwikiadmin"/>
	<updated>2026-05-06T18:52:18Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.45.3</generator>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Quicksort&amp;diff=2849</id>
		<title>Quicksort</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Quicksort&amp;diff=2849"/>
		<updated>2026-05-05T07:28:30Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: /* Laufzeitanalyse */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Rekursive Baumstruktur.jpg|mini|Rekursive Aufteilung beim Quicksort]]&lt;br /&gt;
Ein in der Praxis oft eingesetzter und sehr effizienter [[Sortieren|Sortieralgorithmus]] ist der &#039;&#039;&#039;Quicksort&#039;&#039;&#039; (englisch für „schnelles Sortieren“). Er funktioniert nach dem Prinzip &#039;&#039;&#039;„Teile und herrsche“&#039;&#039;&#039; (Divide and Conquer), welches in der Programmierung meist mithilfe von [[Rekursion]] realisiert wird. &lt;br /&gt;
&lt;br /&gt;
Die Grundidee ist einfach: Die zu sortierende Datenmenge wird in zwei kleinere Teillisten zerlegt (&amp;quot;Teile&amp;quot;) und diese werden anschließend separat sortiert (&amp;quot;Herrsche&amp;quot;). Diese Teillisten werden wiederum in noch kleinere Teillisten zerlegt, bis eine Liste nur noch aus einem einzigen Element besteht. Eine Liste mit nur einem Element ist naturgemäß immer sortiert – an diesem Punkt endet die [[Rekursion]] [http://openbook.galileocomputing.de/c_von_a_bis_z/022_c_algorithmen_003.htm (vgl. Galileo Computing)].&lt;br /&gt;
&lt;br /&gt;
=== Wie funktioniert die Zerlegung (Partitionierung)? ===&lt;br /&gt;
Die Zerlegung der Liste erfolgt anhand eines sogenannten &#039;&#039;&#039;Pivot-Elements&#039;&#039;&#039; (Dreh- und Angelpunkt). &lt;br /&gt;
# Ein beliebiges Element der Liste wird als Pivot ausgewählt.&lt;br /&gt;
# Alle anderen Elemente der Liste werden mit diesem Pivot verglichen.&lt;br /&gt;
# Die Liste wird in zwei Bereiche unterteilt:&lt;br /&gt;
#* &#039;&#039;&#039;Linke Teilliste:&#039;&#039;&#039; Alle Elemente, die kleiner (oder gleich) dem Pivot sind.&lt;br /&gt;
#* &#039;&#039;&#039;Rechte Teilliste:&#039;&#039;&#039; Alle Elemente, die größer als das Pivot sind.&lt;br /&gt;
&lt;br /&gt;
Nach diesem Schritt steht das Pivot-Element bereits an seiner endgültigen, korrekten Position in der Liste. Die Elemente in der linken und rechten Teilliste sind zwar untereinander noch unsortiert, aber es ist garantiert, dass alle Elemente links vom Pivot kleiner sind als alle Elemente rechts davon.&lt;br /&gt;
&lt;br /&gt;
Anschließend ruft sich der Quicksort-Algorithmus selbst auf (Rekursion), um die linke und die rechte Teilliste nach exakt demselben Muster weiter zu sortieren. Ein großer Vorteil dieses Verfahrens: Die Teillisten sind immer Abschnitte der ursprünglichen Liste. Es wird also (abgesehen vom Speicher für die Rekursionsaufrufe) kein zusätzlicher Speicherplatz für neue Listen benötigt (In-Place-Sortierung).&lt;br /&gt;
&lt;br /&gt;
== Beispiel ==&lt;br /&gt;
Als Beispiel betrachten wir eine unsortierte Liste mit Zahlen. Der Einfachheit halber wählen wir in diesem Beispiel immer das &#039;&#039;&#039;erste Element&#039;&#039;&#039; als Pivot-Element. Zu Beginn ist die Zahl 7 also unser Pivot.&lt;br /&gt;
&lt;br /&gt;
7 &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;10 8&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;11&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;9&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;3 1 5 2&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Zahlen, die kleiner als das Pivot-Element 7 sind, wurden blau markiert. Die Zahlen, die größer als 7 sind, wurden rot markiert. Nach dem ersten Partitionierungsschritt hat QuickSort die Liste folgendermaßen umgeordnet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;3 2 5 6 1 4&amp;lt;/span&amp;gt; &#039;&#039;&#039;7&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun stehen alle Zahlen, die kleiner sind als 7, links von ihr. Alle Zahlen, die größer sind, stehen rechts. Die Reihenfolge der Zahlen innerhalb der blauen und roten Teillisten ist hier noch willkürlich und hängt von der genauen Programmierung ab. Entscheidend ist: &#039;&#039;&#039;Die 7 hat nun ihre endgültige, korrekte Position gefunden.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Nächstes wird die linke (blaue) Teilliste per QuickSort sortiert. Als neues Pivot wählen wir wieder die erste Zahl des Bereichs, die 3. Die Zahlen, die kleiner sind als 3, werden wieder blau markiert, die größeren rot. Die bereits einsortierte 7 und die rechte Teilliste ignorieren wir für den Moment (grau hinterlegt):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;5 6&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:gray&amp;quot;&amp;gt;7 9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Algorithmus sortiert die Elemente relativ zur 3 um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;1 2&amp;lt;/span&amp;gt; &#039;&#039;&#039;3&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;6 5 4&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:gray&amp;quot;&amp;gt;7 9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Prozess wird fortgesetzt (erst die 1 und 2, dann die 6, 5 und 4), bis der gesamte linke Teil des Arrays vollständig sortiert ist. Das Ergebnis sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;1 2 3 4 5 6&#039;&#039; &#039;&#039;&#039;7&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:gray&amp;quot;&amp;gt;9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird die Teilliste rechts von der 7 nach demselben Prinzip sortiert. Wieder wird ein Pivot gewählt (hier die 9), und die kleineren und größeren Zahlen werden auf die jeweils richtige Seite gebracht, bis das gesamte Array sortiert ist.&lt;br /&gt;
&lt;br /&gt;
== Pseudocode ==&lt;br /&gt;
Der folgende Pseudocode illustriert die Arbeitsweise des [[Algorithmus]]. Bei jedem Aufruf von &amp;lt;code&amp;gt;quicksort(...)&amp;lt;/code&amp;gt; gibt &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt; den Index des ersten Elements in der Teilliste an und &amp;lt;code&amp;gt;rechts&amp;lt;/code&amp;gt; den des letzten. Beim ersten Aufruf (oberste Rekursionsebene) ist &amp;lt;code&amp;gt;links = 0&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;rechts = n-1&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
funktion quicksort(links, rechts) &lt;br /&gt;
    falls links &amp;lt; rechts dann &lt;br /&gt;
        // teile() ordnet das Array um und liefert die finale Position des Pivots&lt;br /&gt;
        teiler := teile(links, rechts) &lt;br /&gt;
        &lt;br /&gt;
        // Rekursiver Aufruf für die linke Teilliste&lt;br /&gt;
        quicksort(links, teiler - 1) &lt;br /&gt;
        &lt;br /&gt;
        // Rekursiver Aufruf für die rechte Teilliste&lt;br /&gt;
        quicksort(teiler + 1, rechts) &lt;br /&gt;
    ende&lt;br /&gt;
ende&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wahl des Pivot-Elements ==&lt;br /&gt;
Die Effizienz von Quicksort steht und fällt mit der Wahl des Pivot-Elements. &lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schlechteste Wahl:&#039;&#039;&#039; Das Pivot-Element ist zufällig immer das kleinste oder größte Element der Teilliste. Dann wird die Liste extrem ungleichmäßig geteilt (eine Liste mit 0 Elementen, eine mit &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Elementen).&lt;br /&gt;
* &#039;&#039;&#039;Beste Wahl:&#039;&#039;&#039; Das Pivot-Element ist genau der [[Median]] (der Wert, der exakt in der Mitte der sortierten Liste stehen würde). Die Liste wird in zwei exakt gleich große Hälften geteilt. Da der Median in unsortierten Daten aber aufwendig zu suchen ist, behilft man sich mit Tricks.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Warum das erste Element oft eine schlechte Wahl ist:&#039;&#039;&#039;&lt;br /&gt;
Wenn man immer stur das erste Element als Pivot wählt (wie in unserem Beispiel) und die Eingabeliste ist bereits (oder fast) fertig sortiert, trifft automatisch immer der oben beschriebene schlechteste Fall ein. Der Algorithmus wird dadurch extrem langsam.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Lösungsansätze für die Praxis:&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;Randomisierung:&#039;&#039;&#039; Man wählt ein zufälliges Element aus dem Intervall als Pivot.&lt;br /&gt;
# &#039;&#039;&#039;Median-of-Three:&#039;&#039;&#039; Man betrachtet das erste, das mittlere und das letzte Element der Teilliste und wählt von diesen dreien den mittleren Wert als Pivot. Dies schützt sehr gut vor bereits vorsortierten Listen.&lt;br /&gt;
&lt;br /&gt;
== Java Implementierung ==&lt;br /&gt;
Bisher wurde die allgemeine Funktionsweise beschrieben. Am besten lassen sich komplexe [[Algorithmus|Algorithmen]] nachvollziehen, wenn man sie anhand einer Implementierung mit Hilfe eines [[Debugger|Debuggers]] analysiert. In der folgenden [[Java]]-Implementierung wird ein [[Array]] aus Ganzzahlen (&amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;) sortiert. &lt;br /&gt;
&lt;br /&gt;
Zur Abwechslung wird in dieser Implementierung immer das &#039;&#039;&#039;letzte&#039;&#039;&#039; Element des aktuellen Bereichs (&amp;lt;code&amp;gt;array[rechts]&amp;lt;/code&amp;gt;) als Pivot gewählt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void quickSort(int array[], int links, int rechts) {&lt;br /&gt;
      if (links &amp;lt; rechts) {&lt;br /&gt;
         // Teile das Array und finde die Position des Pivot-Elements&lt;br /&gt;
         int i = teile(array, links, rechts);&lt;br /&gt;
         &lt;br /&gt;
         // Sortiere den linken Teil&lt;br /&gt;
         quickSort(array, links, i - 1);&lt;br /&gt;
         // Sortiere den rechten Teil&lt;br /&gt;
         quickSort(array, i + 1, rechts);&lt;br /&gt;
      }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public int teile(int array[], int links, int rechts) {&lt;br /&gt;
      int pivot, i, j, help;&lt;br /&gt;
      pivot = array[rechts]; // Wir wählen das rechte Randelement als Pivot               &lt;br /&gt;
      i = links;&lt;br /&gt;
      j = rechts - 1;&lt;br /&gt;
      &lt;br /&gt;
      // Von links und rechts zur Mitte laufen, bis sich i und j kreuzen&lt;br /&gt;
      while (i &amp;lt;= j) {&lt;br /&gt;
         // Suche von links ein Element, das größer als das Pivot ist&lt;br /&gt;
         if (array[i] &amp;gt; pivot) {     &lt;br /&gt;
            // Tausche array[i] mit array[j], um es in den rechten Bereich zu bringen&lt;br /&gt;
            help = array[i]; &lt;br /&gt;
            array[i] = array[j]; &lt;br /&gt;
            array[j] = help;                             &lt;br /&gt;
            j--; // j einen Schritt weiter nach links schieben&lt;br /&gt;
         } else {&lt;br /&gt;
            i++; // i war kleiner/gleich Pivot, ist also korrekt positioniert -&amp;gt; weiter nach rechts&lt;br /&gt;
         }          &lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      // Das Pivot-Element an seine endgültige Position (i) tauschen&lt;br /&gt;
      help = array[i];&lt;br /&gt;
      array[i] = array[rechts];&lt;br /&gt;
      array[rechts] = help;&lt;br /&gt;
      &lt;br /&gt;
      // Rückgabe der finalen Position des Pivot-Elements&lt;br /&gt;
      return i;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vorbereitung: Indikatorvariablen und die Harmonische Reihe ==&lt;br /&gt;
Bei komplexeren Algorithmen wie [[Quicksort]] reicht einfaches Zählen der Vergleiche oft nicht mehr aus. Hier arbeiten wir in der Laufzeitanalyse mit einem stochastischen Werkzeug: &#039;&#039;&#039;Indikatorvariablen&#039;&#039;&#039; und der &#039;&#039;&#039;Harmonischen Reihe&#039;&#039;&#039;. Um das Prinzip zu verinnerlichen, analysieren wir vorab einen simplen Code zur Maximumsuche:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public int findMax(int[] A) {&lt;br /&gt;
    int max = A[0];&lt;br /&gt;
    for (int i = 1; i &amp;lt; A.length; i++) {&lt;br /&gt;
        if (A[i] &amp;gt; max) {&lt;br /&gt;
            max = A[i]; // Update-Operation&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return max;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Vergleiche in der &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung werden immer exakt &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Mal ausgeführt. Aber wie oft wird die Variable &amp;lt;code&amp;gt;max&amp;lt;/code&amp;gt; im Durchschnitt tatsächlich überschrieben (Update-Operation)?&lt;br /&gt;
&lt;br /&gt;
Wir stellen uns vor, das Array enthält völlig zufällig gemischte Zahlen.&lt;br /&gt;
* &#039;&#039;&#039;Intuitiver Ansatz:&#039;&#039;&#039; &lt;br /&gt;
Das 1. Element ist automatisch das bisherige Maximum (Wahrscheinlichkeit: 1). &lt;br /&gt;
Damit das 2. Element ein neues Maximum ist, muss es größer als das erste sein. Die Chance dafür ist bei zufälliger Verteilung exakt 50% (&amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt;). &lt;br /&gt;
Damit das 3. Element ein neues Maximum ist, muss es das Größte der ersten drei Elemente sein. Die Chance dafür ist &amp;lt;math&amp;gt;\frac{1}{3}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Mathematischer Ansatz (Indikatorvariablen):&#039;&#039;&#039;&lt;br /&gt;
Wir definieren einen „Schalter“ (die Indikatorvariable) &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt;. Er ist &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;, wenn ein neues Maximum an Stelle &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; gefunden wird, sonst &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Die Wahrscheinlichkeit dafür ist &amp;lt;math&amp;gt;P(X_i = 1) = \frac{1}{i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Um die durchschnittliche Gesamtzahl der Updates zu finden, addieren wir einfach diese Wahrscheinlichkeiten auf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E[X] = \sum_{i=1}^{n} \frac{1}{i} = 1 + \frac{1}{2} + \frac{1}{3} + \frac{1}{4} + \dots + \frac{1}{n}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese spezielle Summe nennt man in der Mathematik die &#039;&#039;&#039;Harmonische Reihe&#039;&#039;&#039; (&amp;lt;math&amp;gt;H_n&amp;lt;/math&amp;gt;). Sie wächst extrem langsam. Für große Zahlen &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; entspricht sie in etwa dem &#039;&#039;&#039;natürlichen Logarithmus&#039;&#039;&#039; (&amp;lt;math&amp;gt;\ln(n)&amp;lt;/math&amp;gt;). &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Erkenntnis für Quicksort:&#039;&#039;&#039; Obwohl das Array &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elemente hat, wird die Update-Operation im Durchschnitt nur logarithmisch oft (&amp;lt;math&amp;gt;\mathcal{O}(\log n)&amp;lt;/math&amp;gt;) ausgeführt. Genau dieses stochastische Prinzip (das Aufsummieren von Einzelwahrscheinlichkeiten, die zu einem Logarithmus führen) ist der Schlüssel, um im nächsten Abschnitt zu beweisen, dass Quicksort im Average-Case bei &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt; liegt.&lt;br /&gt;
&lt;br /&gt;
== Laufzeitanalyse ==&lt;br /&gt;
&lt;br /&gt;
=== Best-Case (Bester Fall) ===&lt;br /&gt;
[[Datei:Verzweigungsbaum Quicksort symetrisch.png|mini|Best-Case: Symmetrischer Baum]]&lt;br /&gt;
Der beste Fall liegt vor, wenn das Pivot-Element die Liste in jedem Rekursionsschritt exakt in der Mitte teilt. Die entstehenden Teillisten sind dann immer gleich groß.&lt;br /&gt;
[[Datei:SubArrays symetrisch Quicksort.png|mini|Halbierung der Arraygrößen]]&lt;br /&gt;
&lt;br /&gt;
Wird die Anzahl der zu sortierenden Elemente &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; verdoppelt, kommt durch die fortlaufende Halbierung lediglich &#039;&#039;&#039;eine einzige neue Rekursionsebene&#039;&#039;&#039; (ein Baum-Level) hinzu. Dieses Wachstumsverhalten (Wie oft kann ich &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; durch 2 teilen, bis 1 übrig bleibt?) wird mathematisch durch den Logarithmus zur Basis 2 ausgedrückt: &amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Zu sortierende Elemente &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 4&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 8&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 16&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 32&lt;br /&gt;
|-&lt;br /&gt;
! Als Zweierpotenz&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^3}&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^4}&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^5}&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Anzahl Rekursionsebenen &amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 2&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 3&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 4&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wir haben also insgesamt &amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt; Ebenen. Nun müssen wir noch den Aufwand pro Ebene bestimmen: Obwohl die einzelnen Teillisten nach unten hin immer kürzer werden, verdoppelt sich gleichzeitig ihre Anzahl. Auf &#039;&#039;&#039;jeder einzelnen Ebene&#039;&#039;&#039; müssen daher in der Summe über alle Teillisten hinweg wieder (nahezu) &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elemente betrachtet und mit den jeweiligen Pivots verglichen werden.&lt;br /&gt;
&lt;br /&gt;
Wir multiplizieren also die Arbeit pro Ebene (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Vergleiche) mit der Anzahl der Ebenen (&amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&#039;&#039;&#039;Fazit Best-Case:&#039;&#039;&#039; Die Laufzeit beträgt &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Average-Case (Durchschnittlicher Fall) ===&lt;br /&gt;
In der Praxis haben wir fast nie den perfekten Best-Case, aber zum Glück auch extrem selten den absoluten Worst-Case. Meistens teilt das Pivot die Liste in ungleiche, aber moderate Verhältnisse (z. B. 30:70 oder 60:40). Auch bei solchen ungleichen Teilungen wächst die Tiefe des Baumes weiterhin nur logarithmisch.&lt;br /&gt;
&lt;br /&gt;
Um den Average-Case mathematisch exakt zu beweisen, greifen wir auf Stochastik und unser Vorwissen zur Harmonischen Reihe zurück. Wir fragen uns: &#039;&#039;Wie hoch ist die Wahrscheinlichkeit, dass zwei beliebige Elemente (nennen wir sie &amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt;, wobei &amp;lt;math&amp;gt;z_i &amp;lt; z_j&amp;lt;/math&amp;gt;) im Laufe des gesamten Algorithmus direkt miteinander verglichen werden?&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Zwei Elemente werden genau dann miteinander verglichen, wenn eines der beiden als Pivot-Element ausgewählt wird, &#039;&#039;&#039;bevor&#039;&#039;&#039; irgendein anderes Element, dessen Wert zwischen &amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt; liegt, als Pivot gewählt wird.&lt;br /&gt;
* Zwischen &amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt; (inklusive der beiden Randelemente) liegen exakt &amp;lt;math&amp;gt;k = j - i + 1&amp;lt;/math&amp;gt; Elemente. &lt;br /&gt;
* Gehen wir von einer zufälligen Pivot-Wahl aus, hat jedes dieser &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; Elemente die gleiche Chance, zuerst als Pivot gezogen zu werden. Damit &amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt; verglichen werden, muss exakt &amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; oder &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt; der &amp;quot;Gewinner&amp;quot; dieser Ziehung sein. Da es &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; günstige Fälle bei &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; möglichen Fällen gibt, beträgt die Wahrscheinlichkeit exakt &amp;lt;math&amp;gt;\frac{2}{k}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Summieren wir diese Wahrscheinlichkeiten (den sogenannten Erwartungswert) für alle möglichen Elementpaare im gesamten Array auf, erhalten wir folgende Doppel-Summe:&lt;br /&gt;
&amp;lt;math&amp;gt;E(X) = \sum_{i=1}^{n-1} \sum_{j=i+1}^{n} \frac{2}{j - i + 1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ersetzt man den Abstand &amp;lt;math&amp;gt;(j - i + 1)&amp;lt;/math&amp;gt; formal durch die Laufvariable &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, löst sich die innere Summe auf zu:&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_{k=2}^{n} \frac{2}{k} = 2 \cdot \left( \frac{1}{2} + \frac{1}{3} + \dots + \frac{1}{n} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Klammer erkennen wir exakt die &#039;&#039;&#039;Harmonische Reihe&#039;&#039;&#039; wieder! Aus der Mathematik wissen wir, dass die Harmonische Reihe für große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; gegen den natürlichen Logarithmus &amp;lt;math&amp;gt;\ln(n)&amp;lt;/math&amp;gt; konvergiert. Die innere Summe ergibt also maximal ungefähr &amp;lt;math&amp;gt;2 \cdot \ln(n)&amp;lt;/math&amp;gt;. Da die äußere Summe (die Variable &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;) insgesamt &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-mal durchlaufen wird, multiplizieren wir diesen Wert mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; und erhalten insgesamt rund &amp;lt;math&amp;gt;2n \cdot \ln(n)&amp;lt;/math&amp;gt; zu erwartende Vergleiche. &lt;br /&gt;
&lt;br /&gt;
Da konstante Vorfaktoren (wie die &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;) und die Basis des Logarithmus in der asymptotischen Notation ignoriert werden, ist der Beweis erbracht: &lt;br /&gt;
&#039;&#039;&#039;Fazit Average-Case:&#039;&#039;&#039; Auch bei zufälliger Verteilung konvergiert Quicksort dank des logarithmischen Wachstums sicher gegen die Komplexitätsklasse &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Worst-Case (Schlechtester Fall) ===&lt;br /&gt;
[[Datei:Baum N-Ebenen.png|mini|Worst-Case: Entarteter Baum]]&lt;br /&gt;
Angenommen, wir haben extremes Pech und die Partitionierung ist maximal unausgeglichen. Das gewählte Pivot ist immer das absolut kleinste oder größte Element der aktuellen Teilliste. Dann enthält eine der Partitionen 0 Elemente und die andere Partition &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Elemente. Der Baum &amp;quot;entartet&amp;quot; zu einer linearen Kette. &lt;br /&gt;
&#039;&#039;Hinweis für die Praxis: Wird bei einer naiven Implementierung immer stur das letzte Element als Pivot gewählt, tritt dieser Worst-Case ironischerweise genau dann auf, wenn die Liste bereits aufsteigend oder absteigend sortiert ist!&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Aufwand für das Vergleichen (Partitionieren) auf der ersten Ebene mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen ist proportional zu &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Nennen wir diesen Aufwand &amp;lt;math&amp;gt;c \cdot n&amp;lt;/math&amp;gt; (wobei &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; eine Konstante für die Dauer eines einzelnen Vergleichs ist).&lt;br /&gt;
Auf der nächsten Ebene müssen wir &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Elemente vergleichen, der Aufwand ist also &amp;lt;math&amp;gt;c \cdot (n-1)&amp;lt;/math&amp;gt;. Danach &amp;lt;math&amp;gt;c \cdot (n-2)&amp;lt;/math&amp;gt; und so weiter.&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Partitionierungszeiten für alle Ebenen aufsummieren, erhalten wir folgende Reihe:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot n + c \cdot (n - 1) + c \cdot (n - 2) + \dots + c \cdot 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir klammern &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; aus und erkennen die bekannte [[Arithmetische-reihe|Gaußsche Summenformel]]:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot (n + (n-1) + (n-2) + \dots + 2) \approx c \cdot \frac{n(n+1)}{2} = \frac{c}{2}n^2 + \frac{c}{2}n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Da in der asymptotischen Laufzeitanalyse konstante Faktoren (&amp;lt;math&amp;gt;\frac{c}{2}&amp;lt;/math&amp;gt;) und niederwertige Terme (wie &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) ignoriert werden, bestimmt allein der quadratische Term das Wachstumsverhalten. &lt;br /&gt;
&#039;&#039;&#039;Fazit Worst-Case:&#039;&#039;&#039; Quicksort hat im schlechtesten Fall eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;br /&gt;
[[Kategorie:FI_I_TP2]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Angebotsvergleich&amp;diff=2848</id>
		<title>Angebotsvergleich</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Angebotsvergleich&amp;diff=2848"/>
		<updated>2026-04-30T08:12:41Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: /* Beispiele */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein Angebotsvergleich wird durchgeführt, um das bestmögliche Angebot für ein Produkt oder eine Dienstleistung zu bestimmen.&lt;br /&gt;
&lt;br /&gt;
==Äquivalenzprinzip der Mathematik==&lt;br /&gt;
Unterschiedliche Geldbeträge dürfen nur verglichen, addiert oder subtrahiert werden, wenn diese auf den gleichen Zeitpunkt auf- oder abgezinst wurden.&lt;br /&gt;
In der Regel beziehen wir Geldbeträge auf den jetzigen Zeitpunkt (&amp;lt;math&amp;gt;t=0&amp;lt;/math&amp;gt;) und nennen den jeweiligen Geldbetrag &#039;&#039;&#039;Barwert&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==Definition==&lt;br /&gt;
Bei einem &#039;&#039;&#039;Angebotsvergleich&#039;&#039;&#039; liegen mehrere Angebote für ein Produkt oder eine Dienstleistung mit unterschiedlichen Zahlungsbedingungen vor. Die Zahlungsforderungen in einem Angebot werden auf den Zeitpunkt &amp;lt;math&amp;gt;t=0&amp;lt;/math&amp;gt; abgezinst und anschließend miteinander addiert. Den resultierenden Wert nennen wir &#039;&#039;&#039;Barwert&#039;&#039;&#039; des Angebots. Abschließend werden die Barwerte miteinander verglichen, um das beste Angebot zu ermitteln. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;iframe width=&amp;quot;280&amp;quot; height=&amp;quot;157.5&amp;quot; src=&amp;quot;https://www.youtube.com/embed/mPwGFaFQ5LU?si=rTdru8wzdgwR8gFU&amp;quot; title=&amp;quot;YouTube video player&amp;quot; frameborder=&amp;quot;0&amp;quot; allow=&amp;quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&amp;quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Beispiele==&lt;br /&gt;
Wir betrachten im Folgenden zwei Beispiele für einen Angebotsvergleich.&lt;br /&gt;
&lt;br /&gt;
===Angebotsvergleich mit Barwerten durchführen===&lt;br /&gt;
Wir haben uns ein Fahrrad gekauft. Der Zinssatz beträgt 4 % pro Jahr. Es besteht die Möglichkeit, in einem Jahr 450 € oder in zwei Jahren 480 € zu zahlen.&lt;br /&gt;
&lt;br /&gt;
Wir verwenden die [[Zinseszinsrechnung#Zinseszinsformel|Zinseszinsformel]] und berechnen jeweils das [[Zinseszinsrechnung|Anfangskapital]]:&lt;br /&gt;
&lt;br /&gt;
#Möglichkeit mit &amp;lt;math&amp;gt;q=1,04,~n=1 \text{ und } K(1)=450&amp;lt;/math&amp;gt;: &amp;lt;br&amp;gt;&amp;lt;math&amp;gt;K(n)=K(0)\cdot q^n&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&amp;lt;math&amp;gt;450=K(0)\cdot 1,04^1~|~:1,04&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&amp;lt;math&amp;gt;\frac{450}{{1,04}}=K(0)&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&amp;lt;math&amp;gt;432,69\approx K(0)&amp;lt;/math&amp;gt;&lt;br /&gt;
#Möglichkeit mit &amp;lt;math&amp;gt;q=1,04,~n=2 \text{ und } K(2)=480&amp;lt;/math&amp;gt;: &amp;lt;br&amp;gt;&amp;lt;math&amp;gt;K(n)=K(0)\cdot q^n&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&amp;lt;math&amp;gt;480=K(0)\cdot 1,04^2~|~:1,04^2&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&amp;lt;math&amp;gt;\frac{480}{{1,04^2}}=K(0)&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&amp;lt;math&amp;gt;443,79\approx K(0)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir wählen Möglichkeit 1, weil das Anfangskapital mit ca. 432,69 € geringer ist als das Anfangskapital von 443,79 € bei Möglichkeit 2.&lt;br /&gt;
&lt;br /&gt;
===Angebotsvergleich ohne Barwerte durchführen===&lt;br /&gt;
Wir haben uns ein Fahrrad gekauft. Der Zinssatz beträgt 4 % pro Jahr. Es besteht die Möglichkeit, in einem Jahr 450 € oder in zwei Jahren 480 € zu zahlen.&lt;br /&gt;
&lt;br /&gt;
Alternativ zur vorherigen Rechnung können wir die Zahlungen aufzinsen und anschließend vergleichen:&lt;br /&gt;
&lt;br /&gt;
#Möglichkeit mit &amp;lt;math&amp;gt;q=1,04,~n=1 \text{ und } K(1)=450&amp;lt;/math&amp;gt;: &amp;lt;br&amp;gt;&amp;lt;math&amp;gt;K\left(2\right)=450\cdot1,04=468&amp;lt;/math&amp;gt;&lt;br /&gt;
#Möglichkeit mit &amp;lt;math&amp;gt;q=1,04,~n=2 \text{ und } K(2)=480&amp;lt;/math&amp;gt;:&amp;lt;br&amp;gt;&amp;lt;math&amp;gt;K\left(2\right)=480&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Gemäß dem Äquivalenzprinzip ist Möglichkeit 1 wieder besser.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Finanzmathematik]]&lt;br /&gt;
[[Kategorie:FHR_WuV_Mathe]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Insertion-sort&amp;diff=2847</id>
		<title>Insertion-sort</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Insertion-sort&amp;diff=2847"/>
		<updated>2026-04-28T08:52:09Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: Flbkwikiadmin verschob die Seite Insertion-sort nach Insertionsort&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#WEITERLEITUNG [[Insertionsort]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Insertionsort&amp;diff=2846</id>
		<title>Insertionsort</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Insertionsort&amp;diff=2846"/>
		<updated>2026-04-28T08:52:09Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: Flbkwikiadmin verschob die Seite Insertion-sort nach Insertionsort&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
Der [[Algorithmus]] dieses [[Sortieren|Sortierverfahrens]] ist relativ simpel. Das Prinzip von Insertion Sort ist folgendes: Die einzelnen Elemente werden von vorne nach hinten durchlaufen. Von der aktuellen Position aus wird jedes Element von rechts nach links verschoben – und zwar so lange, bis das einzufügende Element größer oder gleich dem Element ist, das an der aktuell betrachteten Position steht.&lt;br /&gt;
&lt;br /&gt;
Der Platz für das Element, das verschoben wird, ist währenddessen frei. Diese Lücke wird anschließend mit dem entsprechenden Wert an der richtigen Stelle gefüllt.&lt;br /&gt;
&lt;br /&gt;
== Beispiel ==&lt;br /&gt;
[[Datei:Animation Insertio-Sort.gif|mini]]&lt;br /&gt;
Die folgende Tabelle zeigt die Sortierschritte zum Sortieren der Folge 5 7 0 3 4 2 6 1. Auf der linken Seite (grün dargestellt) befindet sich jeweils der bereits sortierte Teil der Folge. Die blauen Ziffern repräsentieren den unsortierten Teil der Zahlenfolge. Ganz rechts steht in Klammern die Anzahl der Positionen, um die das eingefügte Element nach links verschoben wurde. Das aktuell eingefügte Element ist fett markiert.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center; font-family:monospace;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;5&amp;lt;/span&amp;gt;&#039;&#039; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;7&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;3&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; || (0)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;5&amp;lt;/span&amp;gt;&#039;&#039;&#039; || &#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;7&amp;lt;/span&amp;gt;&#039;&#039;&#039; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;3&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; || (0)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt;&#039;&#039;&#039;&#039;&#039; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;5&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;7&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;3&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; || (2)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt; || &#039;&#039;&#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;3&amp;lt;/span&amp;gt;&#039;&#039;&#039;&#039;&#039; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;5&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;7&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; || (2)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;3&amp;lt;/span&amp;gt; || &#039;&#039;&#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt;&#039;&#039;&#039;&#039;&#039; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;5&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;7&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; || (2)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt; || &#039;&#039;&#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt;&#039;&#039;&#039;&#039;&#039; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;3&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;5&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;7&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; || (4)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;3&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;5&amp;lt;/span&amp;gt; || &#039;&#039;&#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt;&#039;&#039;&#039;&#039;&#039; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;7&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; || (1)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt; || &#039;&#039;&#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt;&#039;&#039;&#039;&#039;&#039; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;3&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;5&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;7&amp;lt;/span&amp;gt; || (6)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Pseudocode ==&lt;br /&gt;
Der [[Algorithmus]] sieht im Pseudocode so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
prozedur insertionSort(A ist Liste sortierbarer Elemente)&lt;br /&gt;
    n = Länge von A&lt;br /&gt;
    für i von 1 bis n - 1 wiederhole&lt;br /&gt;
        einzusortierenderWert = A[i]&lt;br /&gt;
        j = i - 1&lt;br /&gt;
        solange j ≥ 0 und A[j] &amp;gt; einzusortierenderWert wiederhole&lt;br /&gt;
            A[j + 1] = A[j]&lt;br /&gt;
            j = j - 1&lt;br /&gt;
        ende solange&lt;br /&gt;
        A[j + 1] = einzusortierenderWert&lt;br /&gt;
    ende für&lt;br /&gt;
ende prozedur&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Komplexität ==&lt;br /&gt;
&lt;br /&gt;
=== Best Case ===&lt;br /&gt;
Die optimale Eingabe ist ein bereits sortiertes [[Array]]. In diesem Fall wird pro Iteration lediglich ein Vergleich durchgeführt, und die innere Schleife wird nie betreten. Die Gesamtanzahl der Vergleiche ist somit proportional zur Anzahl der Elemente &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Insertion Sort besitzt im Best Case daher eine lineare Laufzeit von &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Average Case ===&lt;br /&gt;
Das Average-Case-Szenario geht von einer zufälligen Verteilung der Werte in der Liste aus.&lt;br /&gt;
&lt;br /&gt;
Im Durchschnitt ist das aktuell betrachtete Element kleiner als die Hälfte der bereits sortierten Elemente. Die innere Schleife muss das Element also im Mittel nur bis in die Mitte des bisher sortierten Teilbereichs verschieben. Die Anzahl der Vergleiche und Verschiebungen halbiert sich im Vergleich zum Worst Case dadurch in etwa.&lt;br /&gt;
&lt;br /&gt;
Da in der asymptotischen Landau-Notation konstante Faktoren (wie diese Halbierung) ignoriert werden, dominiert weiterhin die höchste Potenz das Wachstum. Die Zeitkomplexität liegt im Average Case demnach bei &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Worst Case ===&lt;br /&gt;
Eine Liste in umgekehrter (absteigender) Reihenfolge stellt das Worst-Case-Szenario für Insertion Sort dar, beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;[11, 7, 5, 3, 2, 0]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir betrachten nur die Vergleichsoperationen, die mit der Variablen &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; gezählt werden. Die Anzahl der zu sortierenden Elemente beträgt &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Beim ersten äußeren Schleifendurchlauf ist &amp;lt;math&amp;gt;c = 1&amp;lt;/math&amp;gt;, da nur ein Vergleich durchgeführt wird. Beim zweiten Durchlauf sind es 2 Vergleiche, beim dritten 3 usw., bis zum letzten Durchlauf mit &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Vergleichen.&lt;br /&gt;
&lt;br /&gt;
Die Gesamtanzahl der Vergleiche ergibt sich zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;1 + 2 + 3 + \dots + (n - 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist eine [[Arithmetische-reihe|arithmetische Reihe]]. Mit der gaußschen Summenformel erhält man:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{n(n-1)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; dominiert der quadratische Term, sodass Insertion Sort im Worst Case in der Komplexitätsklasse &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; liegt.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;br /&gt;
[[Kategorie:FI_I_TP2]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Selection-sort&amp;diff=2845</id>
		<title>Selection-sort</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Selection-sort&amp;diff=2845"/>
		<updated>2026-04-28T08:51:59Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: Flbkwikiadmin verschob die Seite Selection-sort nach Selectionsort&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#WEITERLEITUNG [[Selectionsort]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Selectionsort&amp;diff=2844</id>
		<title>Selectionsort</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Selectionsort&amp;diff=2844"/>
		<updated>2026-04-28T08:51:59Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: Flbkwikiadmin verschob die Seite Selection-sort nach Selectionsort&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
Ein einfacher [[Sortieren|Sortieralgorithmus]] ist der Selection Sort (Sortieren durch Auswählen). Dieser Algorithmus sucht zunächst das kleinste Element in der Liste, merkt es sich und tauscht es mit dem Element am Anfang aus, sodass sich das kleinste Element an der ersten Stelle befindet. &lt;br /&gt;
&lt;br /&gt;
Anschließend wird das zweitkleinste Element im verbleibenden, noch unsortierten Teil der Liste gesucht und mit dem Element an der zweiten Stelle vertauscht, und so weiter. Auf diese Weise erhalten die Elemente auf der linken Seite der aktuellen Position schrittweise ihren festen Platz und werden nicht mehr verändert.&lt;br /&gt;
&lt;br /&gt;
== Beispiel ==&lt;br /&gt;
[[Datei:Array Selectionsort.gif|mini]]&lt;br /&gt;
Es soll eine Liste mit dem Inhalt &amp;lt;code&amp;gt;[4, 3, 1, 5, 2]&amp;lt;/code&amp;gt; sortiert werden.&lt;br /&gt;
&lt;br /&gt;
Im ersten Durchlauf wird die komplette Liste durchsucht, um das kleinste Element auszuwählen (Select) und an den Beginn der Liste zu stellen. In diesem Beispiel ist das der Wert 1. Der ursprüngliche Wert der Startposition (hier die 4) wird mit dem Wert an der Position des kleinsten Elements (hier die 1 am Index 2) vertauscht.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Animation Selectionsort.gif|mini]]&lt;br /&gt;
Im zweiten Durchlauf beginnt die Suche nach dem kleinsten Element nun bei Index 1. An Index 0 befindet sich bereits das kleinste Element der gesamten Liste, daher ist dort keine Suche mehr erforderlich. Der restliche Teil der Liste wird durchsucht. Es wird die 2 als kleinstes Element ausgewählt und mit der neuen Startposition vertauscht.&lt;br /&gt;
&lt;br /&gt;
Die Suche wiederholt sich nach diesem Prinzip, bis alle Elemente sortiert sind. Der Suchbereich verkleinert sich dabei Schritt für Schritt um ein Element. Die folgende Animation visualisiert den Algorithmus.&lt;br /&gt;
&lt;br /&gt;
== Pseudocode ==&lt;br /&gt;
Der [[Algorithmus]] sieht im Pseudocode so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
prozedur selectionSort(A ist Liste sortierbarer Elemente)&lt;br /&gt;
    n = Länge von A&lt;br /&gt;
    für einfuegeIndex von 0 bis n - 2 wiederhole&lt;br /&gt;
        minPosition = einfuegeIndex&lt;br /&gt;
        für idx von einfuegeIndex + 1 bis n - 1 wiederhole&lt;br /&gt;
            falls A[idx] &amp;lt; A[minPosition] dann&lt;br /&gt;
                minPosition = idx&lt;br /&gt;
            ende falls&lt;br /&gt;
        ende für&lt;br /&gt;
        falls minPosition ≠ einfuegeIndex dann&lt;br /&gt;
            vertausche A[minPosition] und A[einfuegeIndex]&lt;br /&gt;
        ende falls&lt;br /&gt;
    ende für&lt;br /&gt;
ende prozedur&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Java-Implementierung ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public ArrayList&amp;lt;Mitarbeiter&amp;gt; sortierenNachNachnamen() {&lt;br /&gt;
    ArrayList&amp;lt;Mitarbeiter&amp;gt; sortierteListe = new ArrayList&amp;lt;&amp;gt;(mitarbeiterListe);&lt;br /&gt;
    int stelle = 0;&lt;br /&gt;
&lt;br /&gt;
    while (stelle &amp;lt; sortierteListe.size() - 1) {&lt;br /&gt;
        int kleinstesElement = stelle;&lt;br /&gt;
&lt;br /&gt;
        for (int index = stelle + 1; index &amp;lt; sortierteListe.size(); index++) {&lt;br /&gt;
            if (sortierteListe.get(index).getNachname()&lt;br /&gt;
                    .compareTo(sortierteListe.get(kleinstesElement).getNachname()) &amp;lt; 0) {&lt;br /&gt;
                kleinstesElement = index;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (kleinstesElement != stelle) {&lt;br /&gt;
            Mitarbeiter puffer = sortierteListe.get(kleinstesElement);&lt;br /&gt;
            sortierteListe.set(kleinstesElement, sortierteListe.get(stelle));&lt;br /&gt;
            sortierteListe.set(stelle, puffer);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        stelle++;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return sortierteListe;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Komplexität ==&lt;br /&gt;
Mit Hilfe der [[Laufzeitanalyse]] betrachten wir nun die Zeitkomplexität des Algorithmus Selection Sort. &lt;br /&gt;
&lt;br /&gt;
Ein besonderes Merkmal dieses Algorithmus ist, dass seine Laufzeit **unabhängig** von der anfänglichen Sortierung der Elemente ist. Um eine Datenstruktur (z. B. ein [[Array]]) mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Einträgen zu sortieren, muss &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Mal das Minimum durch Vergleichen bestimmt werden. &lt;br /&gt;
&lt;br /&gt;
Bei der ersten Bestimmung des Minimums sind &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Vergleiche notwendig, bei der zweiten &amp;lt;math&amp;gt;n - 2&amp;lt;/math&amp;gt; Vergleiche, und so weiter bis zum letzten Durchlauf mit exakt 1 Vergleich. Mit der [[Arithmetische-reihe|gaußschen Summenformel]] erhält man die Gesamtanzahl der immer notwendigen Vergleiche:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;(n - 1) + (n - 2) + \dots + 1 = \frac{n(n - 1)}{2} = \frac{1}{2}n^2 - \frac{1}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Best Case ===&lt;br /&gt;
Das Best-Case-Szenario tritt ein, wenn das Array bereits vollständig aufsteigend sortiert ist. &lt;br /&gt;
Auch wenn alle Elemente bereits am richtigen Platz stehen, &amp;quot;weiß&amp;quot; der Selection Sort dies nicht. Er muss trotzdem in jedem Durchlauf den kompletten unsortierten Rest der Liste überprüfen, um sicherzustellen, dass nicht doch ein noch kleineres Element existiert. Es finden zwar keine Vertauschungen (Swaps) statt, die Anzahl der Vergleiche bleibt jedoch bei &amp;lt;math&amp;gt;\frac{n(n - 1)}{2}&amp;lt;/math&amp;gt;. Da die Vergleiche asymptotisch dominieren, liegt die Zeitkomplexität im Best Case bei &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Average Case ===&lt;br /&gt;
Im durchschnittlichen Fall (Average Case) liegt eine zufällig durchmischte Liste vor. &lt;br /&gt;
Genau wie im Best Case muss der Selection Sort die gesamte arithmetische Reihe an Vergleichen durchführen, um das Minimum in jedem Schritt zu finden. Die Anzahl der durchgeführten Vertauschungen liegt im Schnitt zwischen 0 und &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt;. Auch hier ändert dies nichts an der dominanten quadratischen Anzahl der Vergleiche. Die Zeitkomplexität im Average Case ist somit ebenfalls &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Worst Case ===&lt;br /&gt;
Das Worst-Case-Szenario liegt vor, wenn die Liste in umgekehrter (absteigender) Reihenfolge sortiert ist.&lt;br /&gt;
Der Algorithmus führt wieder exakt &amp;lt;math&amp;gt;\frac{n(n - 1)}{2}&amp;lt;/math&amp;gt; Vergleiche aus. In diesem Fall muss bei fast jedem Durchlauf (maximal &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Mal) das kleinste Element mit dem aktuellen Start-Element vertauscht werden. Da die maximale Anzahl der Vertauschungen linear (&amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;) wächst, die Anzahl der Vergleiche jedoch quadratisch, bestimmt der quadratische Term das Laufzeitverhalten. Die Komplexitätsklasse im Worst Case liegt demnach bei &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;br /&gt;
[[Kategorie:FI_I_TP2]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Bubble-sort&amp;diff=2843</id>
		<title>Bubble-sort</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Bubble-sort&amp;diff=2843"/>
		<updated>2026-04-28T08:51:48Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: Flbkwikiadmin verschob die Seite Bubble-sort nach Bubblesort&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#WEITERLEITUNG [[Bubblesort]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Bubblesort&amp;diff=2842</id>
		<title>Bubblesort</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Bubblesort&amp;diff=2842"/>
		<updated>2026-04-28T08:51:48Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: Flbkwikiadmin verschob die Seite Bubble-sort nach Bubblesort&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
Der Bubble Sort (Sortieren durch Aufsteigen) ist ein verhältnismäßig einfacher [[Sortieren|Sortieralgorithmus]]. In der sogenannten Bubble-Phase wird die Eingabeliste von links nach rechts durchlaufen. Dabei wird in jedem Schritt das aktuell betrachtete Element mit seinem rechten Nachbarn verglichen. Falls die beiden Elemente das vorgegebene Sortierkriterium verletzen (z. B. das linke größer als das rechte ist), werden sie vertauscht. Am Ende einer solchen Phase steht bei auf- bzw. absteigender Sortierung das größte bzw. kleinste Element der Eingabe fest am Ende der Liste.&lt;br /&gt;
&lt;br /&gt;
Die Bubble-Phase wird so lange wiederholt, bis die Eingabeliste vollständig sortiert ist. Dabei muss das jeweils letzte Element des vorherigen Durchlaufs nicht mehr betrachtet werden, da es bereits seine finale Position erreicht hat und die restliche zu sortierende Eingabe keine größeren bzw. kleineren Elemente mehr enthält.&lt;br /&gt;
&lt;br /&gt;
Je nachdem, ob auf- oder absteigend sortiert wird, steigen die größeren oder kleineren Elemente wie Luftblasen im Wasser (daher der Name &#039;&#039;Bubble&#039;&#039;) Schritt für Schritt immer weiter nach oben an das Ende der Liste. Es werden stets zwei benachbarte Zahlen miteinander in „Bubbles“ verglichen und bei Bedarf vertauscht.&lt;br /&gt;
&lt;br /&gt;
== Beispiel ==&lt;br /&gt;
[[Datei:Animation Bubble Sort.gif|mini]]&lt;br /&gt;
Eine Reihe von fünf Zahlen soll aufsteigend sortiert werden. Die fett und kursiv markierten Zahlen werden jeweils miteinander verglichen. Ist die linke Zahl größer als die rechte, so werden beide vertauscht. Im ersten Durchlauf wandert somit die größte Zahl ganz nach rechts an die letzte Position. Im zweiten Durchlauf muss diese letzte Position folglich nicht mehr verglichen werden. Im dritten Durchlauf entfallen die letzten zwei Positionen, und so weiter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1. Durchlauf&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;55 07&#039;&#039;&#039;&#039;&#039; 78 12 42 (55 ist größer als 07 -&amp;gt; Tausch)&lt;br /&gt;
* 07 &#039;&#039;&#039;&#039;&#039;55 78&#039;&#039;&#039;&#039;&#039; 12 42 (55 ist nicht größer als 78 -&amp;gt; kein Tausch)&lt;br /&gt;
* 07 55 &#039;&#039;&#039;&#039;&#039;78 12&#039;&#039;&#039;&#039;&#039; 42 (78 ist größer als 12 -&amp;gt; Tausch)&lt;br /&gt;
* 07 55 12 &#039;&#039;&#039;&#039;&#039;78 42&#039;&#039;&#039;&#039;&#039; (78 ist größer als 42 -&amp;gt; Tausch, letzter Vergleich)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2. Durchlauf&#039;&#039;&#039; (78 ist bereits fest positioniert)&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;07 55&#039;&#039;&#039;&#039;&#039; 12 42 78&lt;br /&gt;
* 07 &#039;&#039;&#039;&#039;&#039;55 12&#039;&#039;&#039;&#039;&#039; 42 78 (Tausch)&lt;br /&gt;
* 07 12 &#039;&#039;&#039;&#039;&#039;55 42&#039;&#039;&#039;&#039;&#039; 78 (Tausch, letzter Vergleich)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3. Durchlauf&#039;&#039;&#039; (55 und 78 sind fest positioniert)&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;07 12&#039;&#039;&#039;&#039;&#039; 42 55 78&lt;br /&gt;
* 07 &#039;&#039;&#039;&#039;&#039;12 42&#039;&#039;&#039;&#039;&#039; 55 78 (Letzter Vergleich)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;4. Durchlauf&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;07 12&#039;&#039;&#039;&#039;&#039; 42 55 78 (Letzter Vergleich)&lt;br /&gt;
&lt;br /&gt;
07 12 42 55 78 – Die Liste ist fertig sortiert.&lt;br /&gt;
&lt;br /&gt;
== Pseudocode ==&lt;br /&gt;
Der [[Algorithmus]] sieht im Pseudocode der unoptimierten Basisvariante so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
prozedur bubbleSort(A ist Liste sortierbarer Elemente)&lt;br /&gt;
    n = Länge von A&lt;br /&gt;
    wiederhole solange n &amp;gt; 1&lt;br /&gt;
        n = n - 1&lt;br /&gt;
        i = 0&lt;br /&gt;
        wiederhole solange i &amp;lt; n&lt;br /&gt;
            falls A[i] &amp;gt; A[i + 1] dann&lt;br /&gt;
                tausche A[i] und A[i + 1]&lt;br /&gt;
            ende falls&lt;br /&gt;
            i = i + 1&lt;br /&gt;
        ende wiederhole&lt;br /&gt;
    ende wiederhole&lt;br /&gt;
ende prozedur&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Komplexität ==&lt;br /&gt;
Mit Hilfe der [[Laufzeitanalyse]] betrachten wir nun die Zeitkomplexität des Algorithmus Bubble Sort für ein [[Array]] der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Best Case ===&lt;br /&gt;
Das Best-Case-Szenario tritt ein, wenn die Liste bereits von Beginn an vollständig aufsteigend sortiert ist. &lt;br /&gt;
* &#039;&#039;&#039;Unoptimierte Variante (siehe Pseudocode):&#039;&#039;&#039; Auch wenn keine Vertauschungen notwendig sind, durchläuft der oben notierte Algorithmus die Liste strikt weiter. Er führt &amp;lt;math&amp;gt;\frac{n(n - 1)}{2}&amp;lt;/math&amp;gt; Vergleiche aus. Die Zeitkomplexität liegt hier also bei &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Optimierte Variante (Praxis-Standard):&#039;&#039;&#039; In der Praxis wird Bubble Sort fast immer mit einer Abbruchbedingung (z. B. einem Boolean-Flag `wurdeGetauscht`) implementiert. Findet in einem kompletten Durchlauf kein einziger Tausch statt, weiß der Algorithmus, dass die Liste sortiert ist, und bricht vorzeitig ab. In dieser optimierten Form benötigt er im Best Case nur &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Vergleiche. Die Zeitkomplexität sinkt dadurch auf eine lineare Laufzeit von &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Average Case ===&lt;br /&gt;
Im durchschnittlichen Fall (Average Case) liegt eine zufällig durchmischte Liste vor.&lt;br /&gt;
Etwa die Hälfte der maximal möglichen Vertauschungen muss durchgeführt werden. Sowohl die Anzahl der Vergleiche als auch die der Vertauschungen wachsen quadratisch zur Eingabemenge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Konstante Faktoren (wie die Halbierung der Tauschoperationen) werden in der Landau-Notation ignoriert. Die Zeitkomplexität im Average Case liegt somit bei &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Worst Case ===&lt;br /&gt;
Das Worst-Case-Szenario liegt vor, wenn die Liste in umgekehrter (absteigender) Reihenfolge sortiert ist.&lt;br /&gt;
In diesem Fall ist das aktuell betrachtete Element immer größer als sein rechter Nachbar. Der Algorithmus muss in jedem einzelnen Schritt eine Vertauschung durchführen, da jedes Element an das andere Ende der Liste &amp;quot;blubbern&amp;quot; muss. Er führt die maximale Anzahl an Vergleichen und Vertauschungen durch: &amp;lt;math&amp;gt;\frac{n(n - 1)}{2}&amp;lt;/math&amp;gt;. Da der Term mit der höchsten Potenz das Wachstum dominiert, liegt die Komplexitätsklasse des Bubble Sorts im Worst Case bei &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;br /&gt;
[[Kategorie:FI_I_TP2]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Quicksort&amp;diff=2841</id>
		<title>Quicksort</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Quicksort&amp;diff=2841"/>
		<updated>2026-04-28T08:51:30Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: /* Laufzeitanalyse */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Rekursive Baumstruktur.jpg|mini|Rekursive Aufteilung beim Quicksort]]&lt;br /&gt;
Ein in der Praxis oft eingesetzter und sehr effizienter [[Sortieren|Sortieralgorithmus]] ist der &#039;&#039;&#039;Quicksort&#039;&#039;&#039; (englisch für „schnelles Sortieren“). Er funktioniert nach dem Prinzip &#039;&#039;&#039;„Teile und herrsche“&#039;&#039;&#039; (Divide and Conquer), welches in der Programmierung meist mithilfe von [[Rekursion]] realisiert wird. &lt;br /&gt;
&lt;br /&gt;
Die Grundidee ist einfach: Die zu sortierende Datenmenge wird in zwei kleinere Teillisten zerlegt (&amp;quot;Teile&amp;quot;) und diese werden anschließend separat sortiert (&amp;quot;Herrsche&amp;quot;). Diese Teillisten werden wiederum in noch kleinere Teillisten zerlegt, bis eine Liste nur noch aus einem einzigen Element besteht. Eine Liste mit nur einem Element ist naturgemäß immer sortiert – an diesem Punkt endet die [[Rekursion]] [http://openbook.galileocomputing.de/c_von_a_bis_z/022_c_algorithmen_003.htm (vgl. Galileo Computing)].&lt;br /&gt;
&lt;br /&gt;
=== Wie funktioniert die Zerlegung (Partitionierung)? ===&lt;br /&gt;
Die Zerlegung der Liste erfolgt anhand eines sogenannten &#039;&#039;&#039;Pivot-Elements&#039;&#039;&#039; (Dreh- und Angelpunkt). &lt;br /&gt;
# Ein beliebiges Element der Liste wird als Pivot ausgewählt.&lt;br /&gt;
# Alle anderen Elemente der Liste werden mit diesem Pivot verglichen.&lt;br /&gt;
# Die Liste wird in zwei Bereiche unterteilt:&lt;br /&gt;
#* &#039;&#039;&#039;Linke Teilliste:&#039;&#039;&#039; Alle Elemente, die kleiner (oder gleich) dem Pivot sind.&lt;br /&gt;
#* &#039;&#039;&#039;Rechte Teilliste:&#039;&#039;&#039; Alle Elemente, die größer als das Pivot sind.&lt;br /&gt;
&lt;br /&gt;
Nach diesem Schritt steht das Pivot-Element bereits an seiner endgültigen, korrekten Position in der Liste. Die Elemente in der linken und rechten Teilliste sind zwar untereinander noch unsortiert, aber es ist garantiert, dass alle Elemente links vom Pivot kleiner sind als alle Elemente rechts davon.&lt;br /&gt;
&lt;br /&gt;
Anschließend ruft sich der Quicksort-Algorithmus selbst auf (Rekursion), um die linke und die rechte Teilliste nach exakt demselben Muster weiter zu sortieren. Ein großer Vorteil dieses Verfahrens: Die Teillisten sind immer Abschnitte der ursprünglichen Liste. Es wird also (abgesehen vom Speicher für die Rekursionsaufrufe) kein zusätzlicher Speicherplatz für neue Listen benötigt (In-Place-Sortierung).&lt;br /&gt;
&lt;br /&gt;
== Beispiel ==&lt;br /&gt;
Als Beispiel betrachten wir eine unsortierte Liste mit Zahlen. Der Einfachheit halber wählen wir in diesem Beispiel immer das &#039;&#039;&#039;erste Element&#039;&#039;&#039; als Pivot-Element. Zu Beginn ist die Zahl 7 also unser Pivot.&lt;br /&gt;
&lt;br /&gt;
7 &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;10 8&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;11&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;9&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;3 1 5 2&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Zahlen, die kleiner als das Pivot-Element 7 sind, wurden blau markiert. Die Zahlen, die größer als 7 sind, wurden rot markiert. Nach dem ersten Partitionierungsschritt hat QuickSort die Liste folgendermaßen umgeordnet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;3 2 5 6 1 4&amp;lt;/span&amp;gt; &#039;&#039;&#039;7&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun stehen alle Zahlen, die kleiner sind als 7, links von ihr. Alle Zahlen, die größer sind, stehen rechts. Die Reihenfolge der Zahlen innerhalb der blauen und roten Teillisten ist hier noch willkürlich und hängt von der genauen Programmierung ab. Entscheidend ist: &#039;&#039;&#039;Die 7 hat nun ihre endgültige, korrekte Position gefunden.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Nächstes wird die linke (blaue) Teilliste per QuickSort sortiert. Als neues Pivot wählen wir wieder die erste Zahl des Bereichs, die 3. Die Zahlen, die kleiner sind als 3, werden wieder blau markiert, die größeren rot. Die bereits einsortierte 7 und die rechte Teilliste ignorieren wir für den Moment (grau hinterlegt):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;5 6&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:gray&amp;quot;&amp;gt;7 9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Algorithmus sortiert die Elemente relativ zur 3 um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;1 2&amp;lt;/span&amp;gt; &#039;&#039;&#039;3&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;6 5 4&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:gray&amp;quot;&amp;gt;7 9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Prozess wird fortgesetzt (erst die 1 und 2, dann die 6, 5 und 4), bis der gesamte linke Teil des Arrays vollständig sortiert ist. Das Ergebnis sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;1 2 3 4 5 6&#039;&#039; &#039;&#039;&#039;7&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:gray&amp;quot;&amp;gt;9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird die Teilliste rechts von der 7 nach demselben Prinzip sortiert. Wieder wird ein Pivot gewählt (hier die 9), und die kleineren und größeren Zahlen werden auf die jeweils richtige Seite gebracht, bis das gesamte Array sortiert ist.&lt;br /&gt;
&lt;br /&gt;
== Pseudocode ==&lt;br /&gt;
Der folgende Pseudocode illustriert die Arbeitsweise des [[Algorithmus]]. Bei jedem Aufruf von &amp;lt;code&amp;gt;quicksort(...)&amp;lt;/code&amp;gt; gibt &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt; den Index des ersten Elements in der Teilliste an und &amp;lt;code&amp;gt;rechts&amp;lt;/code&amp;gt; den des letzten. Beim ersten Aufruf (oberste Rekursionsebene) ist &amp;lt;code&amp;gt;links = 0&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;rechts = n-1&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
funktion quicksort(links, rechts) &lt;br /&gt;
    falls links &amp;lt; rechts dann &lt;br /&gt;
        // teile() ordnet das Array um und liefert die finale Position des Pivots&lt;br /&gt;
        teiler := teile(links, rechts) &lt;br /&gt;
        &lt;br /&gt;
        // Rekursiver Aufruf für die linke Teilliste&lt;br /&gt;
        quicksort(links, teiler - 1) &lt;br /&gt;
        &lt;br /&gt;
        // Rekursiver Aufruf für die rechte Teilliste&lt;br /&gt;
        quicksort(teiler + 1, rechts) &lt;br /&gt;
    ende&lt;br /&gt;
ende&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wahl des Pivot-Elements ==&lt;br /&gt;
Die Effizienz von Quicksort steht und fällt mit der Wahl des Pivot-Elements. &lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schlechteste Wahl:&#039;&#039;&#039; Das Pivot-Element ist zufällig immer das kleinste oder größte Element der Teilliste. Dann wird die Liste extrem ungleichmäßig geteilt (eine Liste mit 0 Elementen, eine mit &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Elementen).&lt;br /&gt;
* &#039;&#039;&#039;Beste Wahl:&#039;&#039;&#039; Das Pivot-Element ist genau der [[Median]] (der Wert, der exakt in der Mitte der sortierten Liste stehen würde). Die Liste wird in zwei exakt gleich große Hälften geteilt. Da der Median in unsortierten Daten aber aufwendig zu suchen ist, behilft man sich mit Tricks.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Warum das erste Element oft eine schlechte Wahl ist:&#039;&#039;&#039;&lt;br /&gt;
Wenn man immer stur das erste Element als Pivot wählt (wie in unserem Beispiel) und die Eingabeliste ist bereits (oder fast) fertig sortiert, trifft automatisch immer der oben beschriebene schlechteste Fall ein. Der Algorithmus wird dadurch extrem langsam.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Lösungsansätze für die Praxis:&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;Randomisierung:&#039;&#039;&#039; Man wählt ein zufälliges Element aus dem Intervall als Pivot.&lt;br /&gt;
# &#039;&#039;&#039;Median-of-Three:&#039;&#039;&#039; Man betrachtet das erste, das mittlere und das letzte Element der Teilliste und wählt von diesen dreien den mittleren Wert als Pivot. Dies schützt sehr gut vor bereits vorsortierten Listen.&lt;br /&gt;
&lt;br /&gt;
== Java Implementierung ==&lt;br /&gt;
Bisher wurde die allgemeine Funktionsweise beschrieben. Am besten lassen sich komplexe [[Algorithmus|Algorithmen]] nachvollziehen, wenn man sie anhand einer Implementierung mit Hilfe eines [[Debugger|Debuggers]] analysiert. In der folgenden [[Java]]-Implementierung wird ein [[Array]] aus Ganzzahlen (&amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;) sortiert. &lt;br /&gt;
&lt;br /&gt;
Zur Abwechslung wird in dieser Implementierung immer das &#039;&#039;&#039;letzte&#039;&#039;&#039; Element des aktuellen Bereichs (&amp;lt;code&amp;gt;array[rechts]&amp;lt;/code&amp;gt;) als Pivot gewählt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void quickSort(int array[], int links, int rechts) {&lt;br /&gt;
      if (links &amp;lt; rechts) {&lt;br /&gt;
         // Teile das Array und finde die Position des Pivot-Elements&lt;br /&gt;
         int i = teile(array, links, rechts);&lt;br /&gt;
         &lt;br /&gt;
         // Sortiere den linken Teil&lt;br /&gt;
         quickSort(array, links, i - 1);&lt;br /&gt;
         // Sortiere den rechten Teil&lt;br /&gt;
         quickSort(array, i + 1, rechts);&lt;br /&gt;
      }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public int teile(int array[], int links, int rechts) {&lt;br /&gt;
      int pivot, i, j, help;&lt;br /&gt;
      pivot = array[rechts]; // Wir wählen das rechte Randelement als Pivot               &lt;br /&gt;
      i = links;&lt;br /&gt;
      j = rechts - 1;&lt;br /&gt;
      &lt;br /&gt;
      // Von links und rechts zur Mitte laufen, bis sich i und j kreuzen&lt;br /&gt;
      while (i &amp;lt;= j) {&lt;br /&gt;
         // Suche von links ein Element, das größer als das Pivot ist&lt;br /&gt;
         if (array[i] &amp;gt; pivot) {     &lt;br /&gt;
            // Tausche array[i] mit array[j], um es in den rechten Bereich zu bringen&lt;br /&gt;
            help = array[i]; &lt;br /&gt;
            array[i] = array[j]; &lt;br /&gt;
            array[j] = help;                             &lt;br /&gt;
            j--; // j einen Schritt weiter nach links schieben&lt;br /&gt;
         } else {&lt;br /&gt;
            i++; // i war kleiner/gleich Pivot, ist also korrekt positioniert -&amp;gt; weiter nach rechts&lt;br /&gt;
         }          &lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      // Das Pivot-Element an seine endgültige Position (i) tauschen&lt;br /&gt;
      help = array[i];&lt;br /&gt;
      array[i] = array[rechts];&lt;br /&gt;
      array[rechts] = help;&lt;br /&gt;
      &lt;br /&gt;
      // Rückgabe der finalen Position des Pivot-Elements&lt;br /&gt;
      return i;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vorbereitung: Indikatorvariablen und die Harmonische Reihe ==&lt;br /&gt;
Bei komplexeren Algorithmen wie [[Quicksort]] reicht einfaches Zählen der Vergleiche oft nicht mehr aus. Hier arbeiten wir in der Laufzeitanalyse mit einem stochastischen Werkzeug: &#039;&#039;&#039;Indikatorvariablen&#039;&#039;&#039; und der &#039;&#039;&#039;Harmonischen Reihe&#039;&#039;&#039;. Um das Prinzip zu verinnerlichen, analysieren wir vorab einen simplen Code zur Maximumsuche:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public int findMax(int[] A) {&lt;br /&gt;
    int max = A[0];&lt;br /&gt;
    for (int i = 1; i &amp;lt; A.length; i++) {&lt;br /&gt;
        if (A[i] &amp;gt; max) {&lt;br /&gt;
            max = A[i]; // Update-Operation&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return max;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Vergleiche in der &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung werden immer exakt &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Mal ausgeführt. Aber wie oft wird die Variable &amp;lt;code&amp;gt;max&amp;lt;/code&amp;gt; im Durchschnitt tatsächlich überschrieben (Update-Operation)?&lt;br /&gt;
&lt;br /&gt;
Wir stellen uns vor, das Array enthält völlig zufällig gemischte Zahlen.&lt;br /&gt;
* &#039;&#039;&#039;Intuitiver Ansatz:&#039;&#039;&#039; &lt;br /&gt;
Das 1. Element ist automatisch das bisherige Maximum (Wahrscheinlichkeit: 1). &lt;br /&gt;
Damit das 2. Element ein neues Maximum ist, muss es größer als das erste sein. Die Chance dafür ist bei zufälliger Verteilung exakt 50% (&amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt;). &lt;br /&gt;
Damit das 3. Element ein neues Maximum ist, muss es das Größte der ersten drei Elemente sein. Die Chance dafür ist &amp;lt;math&amp;gt;\frac{1}{3}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Mathematischer Ansatz (Indikatorvariablen):&#039;&#039;&#039;&lt;br /&gt;
Wir definieren einen „Schalter“ (die Indikatorvariable) &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt;. Er ist &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;, wenn ein neues Maximum an Stelle &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; gefunden wird, sonst &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Die Wahrscheinlichkeit dafür ist &amp;lt;math&amp;gt;P(X_i = 1) = \frac{1}{i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Um die durchschnittliche Gesamtzahl der Updates zu finden, addieren wir einfach diese Wahrscheinlichkeiten auf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E[X] = \sum_{i=1}^{n} \frac{1}{i} = 1 + \frac{1}{2} + \frac{1}{3} + \frac{1}{4} + \dots + \frac{1}{n}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese spezielle Summe nennt man in der Mathematik die &#039;&#039;&#039;Harmonische Reihe&#039;&#039;&#039; (&amp;lt;math&amp;gt;H_n&amp;lt;/math&amp;gt;). Sie wächst extrem langsam. Für große Zahlen &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; entspricht sie in etwa dem &#039;&#039;&#039;natürlichen Logarithmus&#039;&#039;&#039; (&amp;lt;math&amp;gt;\ln(n)&amp;lt;/math&amp;gt;). &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Erkenntnis für Quicksort:&#039;&#039;&#039; Obwohl das Array &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elemente hat, wird die Update-Operation im Durchschnitt nur logarithmisch oft (&amp;lt;math&amp;gt;\mathcal{O}(\log n)&amp;lt;/math&amp;gt;) ausgeführt. Genau dieses stochastische Prinzip (das Aufsummieren von Einzelwahrscheinlichkeiten, die zu einem Logarithmus führen) ist der Schlüssel, um im nächsten Abschnitt zu beweisen, dass Quicksort im Average-Case bei &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt; liegt.&lt;br /&gt;
&lt;br /&gt;
== Laufzeitanalyse ==&lt;br /&gt;
&lt;br /&gt;
=== Best-Case (Bester Fall) ===&lt;br /&gt;
[[Datei:Verzweigungsbaum Quicksort symetrisch.png|mini|Best-Case: Symmetrischer Baum]]&lt;br /&gt;
Der beste Fall liegt vor, wenn das Pivot-Element die Liste immer exakt in der Mitte teilt. Die Teillisten sind dann immer gleich groß.&lt;br /&gt;
[[Datei:SubArrays symetrisch Quicksort.png|mini|Halbierung der Arraygrößen]]&lt;br /&gt;
&lt;br /&gt;
Wird die Anzahl der zu sortierenden Elemente &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; verdoppelt, kommt durch die fortlaufende Halbierung lediglich &#039;&#039;&#039;eine einzige neue Rekursionsebene&#039;&#039;&#039; (ein Baum-Level) hinzu. Dieses Wachstumsverhalten (wie oft kann ich &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; durch 2 teilen, bis 1 übrig bleibt?) wird mathematisch durch den Logarithmus zur Basis 2 ausgedrückt: &amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Zu sortierende Elemente &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 4&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 8&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 16&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 32&lt;br /&gt;
|-&lt;br /&gt;
! Als Zweierpotenz&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^3}&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^4}&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^5}&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Anzahl Rekursionsebenen &amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 2&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 3&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 4&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wir haben also insgesamt &amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt; Ebenen. Auf &#039;&#039;&#039;jeder einzelnen Ebene&#039;&#039;&#039; müssen in der Summe &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elemente betrachtet und mit dem jeweiligen Pivot verglichen werden. &lt;br /&gt;
Wir multiplizieren also die Arbeit pro Ebene (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) mit der Anzahl der Ebenen (&amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&#039;&#039;&#039;Fazit Best-Case:&#039;&#039;&#039; Die Laufzeit beträgt &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Average-Case (Durchschnittlicher Fall) ===&lt;br /&gt;
In der Praxis haben wir selten den perfekten Best-Case, aber fast nie den absoluten Worst-Case. Meistens teilt das Pivot die Liste in ungleiche, aber moderate Verhältnisse (z. B. 30:70 oder 60:40). &lt;br /&gt;
&lt;br /&gt;
Um den Average-Case mathematisch exakt zu beweisen, greifen wir auf unser Vorwissen (Indikatorvariablen und Harmonische Reihe) zurück. Wir fragen uns: &#039;&#039;Wie hoch ist die Wahrscheinlichkeit, dass zwei beliebige Elemente (nennen wir sie &amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt;, wobei &amp;lt;math&amp;gt;z_i &amp;lt; z_j&amp;lt;/math&amp;gt;) im Laufe des Algorithmus direkt miteinander verglichen werden?&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Zwei Elemente werden genau dann miteinander verglichen, wenn eines der beiden als Pivot-Element ausgewählt wird, &#039;&#039;&#039;bevor&#039;&#039;&#039; irgendein anderes Element, dessen Wert zwischen &amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt; liegt, als Pivot gewählt wird.&lt;br /&gt;
* Zwischen &amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt; (inklusive der beiden Randelemente) liegen exakt &amp;lt;math&amp;gt;k = j - i + 1&amp;lt;/math&amp;gt; Elemente. &lt;br /&gt;
* Sind die Daten völlig zufällig verteilt, hat jedes dieser &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; Elemente die gleiche Chance, zuerst als Pivot gewählt zu werden. Damit &amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt; verglichen werden, muss exakt &amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; oder &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt; der &amp;quot;Gewinner&amp;quot; dieser Ziehung sein. Da es &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; günstige Fälle (&amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; oder &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt;) bei &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; möglichen Fällen gibt, beträgt die Wahrscheinlichkeit exakt &amp;lt;math&amp;gt;\frac{2}{k}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Summieren wir diese Wahrscheinlichkeiten (den Erwartungswert) für alle möglichen Elementpaare im gesamten Array auf, erhalten wir folgende Doppel-Summe:&lt;br /&gt;
&amp;lt;math&amp;gt;E(X) = \sum_{i=1}^{n-1} \sum_{j=i+1}^{n} \frac{2}{j - i + 1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ersetzt man den Abstand &amp;lt;math&amp;gt;(j - i + 1)&amp;lt;/math&amp;gt; formal durch die Laufvariable &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, löst sich die innere Summe auf zu:&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_{k=2}^{n} \frac{2}{k} = 2 \cdot \left( \frac{1}{2} + \frac{1}{3} + \dots + \frac{1}{n} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier erkennen wir exakt die &#039;&#039;&#039;Harmonische Reihe&#039;&#039;&#039; (&amp;lt;math&amp;gt;H_n&amp;lt;/math&amp;gt;) wieder! Da die Harmonische Reihe für große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; gegen den natürlichen Logarithmus &amp;lt;math&amp;gt;\ln(n)&amp;lt;/math&amp;gt; konvergiert, ergibt die innere Summe ungefähr &amp;lt;math&amp;gt;2 \cdot \ln(n)&amp;lt;/math&amp;gt;. Da die äußere Summe (stark vereinfacht) &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-mal läuft, erhalten wir insgesamt rund &amp;lt;math&amp;gt;2n \cdot \ln(n)&amp;lt;/math&amp;gt; Vergleiche. &lt;br /&gt;
&lt;br /&gt;
Da konstante Vorfaktoren wie die &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; beim Groß-O ignoriert werden, ist nun stochastisch bewiesen: &lt;br /&gt;
&#039;&#039;&#039;Fazit Average-Case:&#039;&#039;&#039; Auch bei zufälliger Verteilung konvergiert Quicksort dank des logarithmischen Wachstums der harmonischen Reihe sicher gegen die Komplexitätsklasse &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Worst-Case (Schlechtester Fall) ===&lt;br /&gt;
[[Datei:Baum N-Ebenen.png|mini|Worst-Case: Entarteter Baum]]&lt;br /&gt;
Betrachten wir zunächst die Worst-Case-[[Laufzeitanalyse]]. Angenommen, wir haben extremes Pech und die Partitionierung ist maximal unausgeglichen. Das gewählte Pivot ist immer das absolut kleinste oder größte Element. Dann enthält eine der Partitionen 0 Elemente und die andere Partition &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Elemente. &lt;br /&gt;
&lt;br /&gt;
Der Aufwand für das Vergleichen (Partitionieren) einer Ebene mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen ist proportional zu &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Nennen wir diesen Aufwand &amp;lt;math&amp;gt;c \cdot n&amp;lt;/math&amp;gt; (wobei &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; eine Konstante für die Dauer eines Vergleichs ist).&lt;br /&gt;
Auf der nächsten Ebene müssen wir &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Elemente vergleichen, der Aufwand ist &amp;lt;math&amp;gt;c \cdot (n-1)&amp;lt;/math&amp;gt;. Danach &amp;lt;math&amp;gt;c \cdot (n-2)&amp;lt;/math&amp;gt; und so weiter.&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Partitionierungszeiten für alle Ebenen aufsummieren, erhalten wir folgende Reihe:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot n + c \cdot (n - 1) + c \cdot (n - 2) + \dots + c \cdot 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir klammern &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; aus und erkennen die bekannte [[Arithmetische-reihe|Gaußsche Summenformel]]:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot (n + (n-1) + (n-2) + \dots + 2) \approx c \cdot \frac{n(n+1)}{2} = \frac{c}{2}n^2 + \frac{c}{2}n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Da in der asymptotischen Laufzeitanalyse konstante Faktoren und kleinere Terme (wie &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) ignoriert werden, wächst der Aufwand quadratisch. &lt;br /&gt;
&#039;&#039;&#039;Fazit Worst-Case:&#039;&#039;&#039; Quicksort hat im schlechtesten Fall eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;br /&gt;
[[Kategorie:FI_I_TP2]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Bubblesort&amp;diff=2840</id>
		<title>Bubblesort</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Bubblesort&amp;diff=2840"/>
		<updated>2026-04-28T08:50:55Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
Der Bubble Sort (Sortieren durch Aufsteigen) ist ein verhältnismäßig einfacher [[Sortieren|Sortieralgorithmus]]. In der sogenannten Bubble-Phase wird die Eingabeliste von links nach rechts durchlaufen. Dabei wird in jedem Schritt das aktuell betrachtete Element mit seinem rechten Nachbarn verglichen. Falls die beiden Elemente das vorgegebene Sortierkriterium verletzen (z. B. das linke größer als das rechte ist), werden sie vertauscht. Am Ende einer solchen Phase steht bei auf- bzw. absteigender Sortierung das größte bzw. kleinste Element der Eingabe fest am Ende der Liste.&lt;br /&gt;
&lt;br /&gt;
Die Bubble-Phase wird so lange wiederholt, bis die Eingabeliste vollständig sortiert ist. Dabei muss das jeweils letzte Element des vorherigen Durchlaufs nicht mehr betrachtet werden, da es bereits seine finale Position erreicht hat und die restliche zu sortierende Eingabe keine größeren bzw. kleineren Elemente mehr enthält.&lt;br /&gt;
&lt;br /&gt;
Je nachdem, ob auf- oder absteigend sortiert wird, steigen die größeren oder kleineren Elemente wie Luftblasen im Wasser (daher der Name &#039;&#039;Bubble&#039;&#039;) Schritt für Schritt immer weiter nach oben an das Ende der Liste. Es werden stets zwei benachbarte Zahlen miteinander in „Bubbles“ verglichen und bei Bedarf vertauscht.&lt;br /&gt;
&lt;br /&gt;
== Beispiel ==&lt;br /&gt;
[[Datei:Animation Bubble Sort.gif|mini]]&lt;br /&gt;
Eine Reihe von fünf Zahlen soll aufsteigend sortiert werden. Die fett und kursiv markierten Zahlen werden jeweils miteinander verglichen. Ist die linke Zahl größer als die rechte, so werden beide vertauscht. Im ersten Durchlauf wandert somit die größte Zahl ganz nach rechts an die letzte Position. Im zweiten Durchlauf muss diese letzte Position folglich nicht mehr verglichen werden. Im dritten Durchlauf entfallen die letzten zwei Positionen, und so weiter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1. Durchlauf&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;55 07&#039;&#039;&#039;&#039;&#039; 78 12 42 (55 ist größer als 07 -&amp;gt; Tausch)&lt;br /&gt;
* 07 &#039;&#039;&#039;&#039;&#039;55 78&#039;&#039;&#039;&#039;&#039; 12 42 (55 ist nicht größer als 78 -&amp;gt; kein Tausch)&lt;br /&gt;
* 07 55 &#039;&#039;&#039;&#039;&#039;78 12&#039;&#039;&#039;&#039;&#039; 42 (78 ist größer als 12 -&amp;gt; Tausch)&lt;br /&gt;
* 07 55 12 &#039;&#039;&#039;&#039;&#039;78 42&#039;&#039;&#039;&#039;&#039; (78 ist größer als 42 -&amp;gt; Tausch, letzter Vergleich)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2. Durchlauf&#039;&#039;&#039; (78 ist bereits fest positioniert)&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;07 55&#039;&#039;&#039;&#039;&#039; 12 42 78&lt;br /&gt;
* 07 &#039;&#039;&#039;&#039;&#039;55 12&#039;&#039;&#039;&#039;&#039; 42 78 (Tausch)&lt;br /&gt;
* 07 12 &#039;&#039;&#039;&#039;&#039;55 42&#039;&#039;&#039;&#039;&#039; 78 (Tausch, letzter Vergleich)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3. Durchlauf&#039;&#039;&#039; (55 und 78 sind fest positioniert)&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;07 12&#039;&#039;&#039;&#039;&#039; 42 55 78&lt;br /&gt;
* 07 &#039;&#039;&#039;&#039;&#039;12 42&#039;&#039;&#039;&#039;&#039; 55 78 (Letzter Vergleich)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;4. Durchlauf&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;07 12&#039;&#039;&#039;&#039;&#039; 42 55 78 (Letzter Vergleich)&lt;br /&gt;
&lt;br /&gt;
07 12 42 55 78 – Die Liste ist fertig sortiert.&lt;br /&gt;
&lt;br /&gt;
== Pseudocode ==&lt;br /&gt;
Der [[Algorithmus]] sieht im Pseudocode der unoptimierten Basisvariante so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
prozedur bubbleSort(A ist Liste sortierbarer Elemente)&lt;br /&gt;
    n = Länge von A&lt;br /&gt;
    wiederhole solange n &amp;gt; 1&lt;br /&gt;
        n = n - 1&lt;br /&gt;
        i = 0&lt;br /&gt;
        wiederhole solange i &amp;lt; n&lt;br /&gt;
            falls A[i] &amp;gt; A[i + 1] dann&lt;br /&gt;
                tausche A[i] und A[i + 1]&lt;br /&gt;
            ende falls&lt;br /&gt;
            i = i + 1&lt;br /&gt;
        ende wiederhole&lt;br /&gt;
    ende wiederhole&lt;br /&gt;
ende prozedur&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Komplexität ==&lt;br /&gt;
Mit Hilfe der [[Laufzeitanalyse]] betrachten wir nun die Zeitkomplexität des Algorithmus Bubble Sort für ein [[Array]] der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Best Case ===&lt;br /&gt;
Das Best-Case-Szenario tritt ein, wenn die Liste bereits von Beginn an vollständig aufsteigend sortiert ist. &lt;br /&gt;
* &#039;&#039;&#039;Unoptimierte Variante (siehe Pseudocode):&#039;&#039;&#039; Auch wenn keine Vertauschungen notwendig sind, durchläuft der oben notierte Algorithmus die Liste strikt weiter. Er führt &amp;lt;math&amp;gt;\frac{n(n - 1)}{2}&amp;lt;/math&amp;gt; Vergleiche aus. Die Zeitkomplexität liegt hier also bei &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Optimierte Variante (Praxis-Standard):&#039;&#039;&#039; In der Praxis wird Bubble Sort fast immer mit einer Abbruchbedingung (z. B. einem Boolean-Flag `wurdeGetauscht`) implementiert. Findet in einem kompletten Durchlauf kein einziger Tausch statt, weiß der Algorithmus, dass die Liste sortiert ist, und bricht vorzeitig ab. In dieser optimierten Form benötigt er im Best Case nur &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Vergleiche. Die Zeitkomplexität sinkt dadurch auf eine lineare Laufzeit von &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Average Case ===&lt;br /&gt;
Im durchschnittlichen Fall (Average Case) liegt eine zufällig durchmischte Liste vor.&lt;br /&gt;
Etwa die Hälfte der maximal möglichen Vertauschungen muss durchgeführt werden. Sowohl die Anzahl der Vergleiche als auch die der Vertauschungen wachsen quadratisch zur Eingabemenge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Konstante Faktoren (wie die Halbierung der Tauschoperationen) werden in der Landau-Notation ignoriert. Die Zeitkomplexität im Average Case liegt somit bei &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Worst Case ===&lt;br /&gt;
Das Worst-Case-Szenario liegt vor, wenn die Liste in umgekehrter (absteigender) Reihenfolge sortiert ist.&lt;br /&gt;
In diesem Fall ist das aktuell betrachtete Element immer größer als sein rechter Nachbar. Der Algorithmus muss in jedem einzelnen Schritt eine Vertauschung durchführen, da jedes Element an das andere Ende der Liste &amp;quot;blubbern&amp;quot; muss. Er führt die maximale Anzahl an Vergleichen und Vertauschungen durch: &amp;lt;math&amp;gt;\frac{n(n - 1)}{2}&amp;lt;/math&amp;gt;. Da der Term mit der höchsten Potenz das Wachstum dominiert, liegt die Komplexitätsklasse des Bubble Sorts im Worst Case bei &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;br /&gt;
[[Kategorie:FI_I_TP2]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Selectionsort&amp;diff=2839</id>
		<title>Selectionsort</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Selectionsort&amp;diff=2839"/>
		<updated>2026-04-28T08:49:46Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
Ein einfacher [[Sortieren|Sortieralgorithmus]] ist der Selection Sort (Sortieren durch Auswählen). Dieser Algorithmus sucht zunächst das kleinste Element in der Liste, merkt es sich und tauscht es mit dem Element am Anfang aus, sodass sich das kleinste Element an der ersten Stelle befindet. &lt;br /&gt;
&lt;br /&gt;
Anschließend wird das zweitkleinste Element im verbleibenden, noch unsortierten Teil der Liste gesucht und mit dem Element an der zweiten Stelle vertauscht, und so weiter. Auf diese Weise erhalten die Elemente auf der linken Seite der aktuellen Position schrittweise ihren festen Platz und werden nicht mehr verändert.&lt;br /&gt;
&lt;br /&gt;
== Beispiel ==&lt;br /&gt;
[[Datei:Array Selectionsort.gif|mini]]&lt;br /&gt;
Es soll eine Liste mit dem Inhalt &amp;lt;code&amp;gt;[4, 3, 1, 5, 2]&amp;lt;/code&amp;gt; sortiert werden.&lt;br /&gt;
&lt;br /&gt;
Im ersten Durchlauf wird die komplette Liste durchsucht, um das kleinste Element auszuwählen (Select) und an den Beginn der Liste zu stellen. In diesem Beispiel ist das der Wert 1. Der ursprüngliche Wert der Startposition (hier die 4) wird mit dem Wert an der Position des kleinsten Elements (hier die 1 am Index 2) vertauscht.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Animation Selectionsort.gif|mini]]&lt;br /&gt;
Im zweiten Durchlauf beginnt die Suche nach dem kleinsten Element nun bei Index 1. An Index 0 befindet sich bereits das kleinste Element der gesamten Liste, daher ist dort keine Suche mehr erforderlich. Der restliche Teil der Liste wird durchsucht. Es wird die 2 als kleinstes Element ausgewählt und mit der neuen Startposition vertauscht.&lt;br /&gt;
&lt;br /&gt;
Die Suche wiederholt sich nach diesem Prinzip, bis alle Elemente sortiert sind. Der Suchbereich verkleinert sich dabei Schritt für Schritt um ein Element. Die folgende Animation visualisiert den Algorithmus.&lt;br /&gt;
&lt;br /&gt;
== Pseudocode ==&lt;br /&gt;
Der [[Algorithmus]] sieht im Pseudocode so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
prozedur selectionSort(A ist Liste sortierbarer Elemente)&lt;br /&gt;
    n = Länge von A&lt;br /&gt;
    für einfuegeIndex von 0 bis n - 2 wiederhole&lt;br /&gt;
        minPosition = einfuegeIndex&lt;br /&gt;
        für idx von einfuegeIndex + 1 bis n - 1 wiederhole&lt;br /&gt;
            falls A[idx] &amp;lt; A[minPosition] dann&lt;br /&gt;
                minPosition = idx&lt;br /&gt;
            ende falls&lt;br /&gt;
        ende für&lt;br /&gt;
        falls minPosition ≠ einfuegeIndex dann&lt;br /&gt;
            vertausche A[minPosition] und A[einfuegeIndex]&lt;br /&gt;
        ende falls&lt;br /&gt;
    ende für&lt;br /&gt;
ende prozedur&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Java-Implementierung ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public ArrayList&amp;lt;Mitarbeiter&amp;gt; sortierenNachNachnamen() {&lt;br /&gt;
    ArrayList&amp;lt;Mitarbeiter&amp;gt; sortierteListe = new ArrayList&amp;lt;&amp;gt;(mitarbeiterListe);&lt;br /&gt;
    int stelle = 0;&lt;br /&gt;
&lt;br /&gt;
    while (stelle &amp;lt; sortierteListe.size() - 1) {&lt;br /&gt;
        int kleinstesElement = stelle;&lt;br /&gt;
&lt;br /&gt;
        for (int index = stelle + 1; index &amp;lt; sortierteListe.size(); index++) {&lt;br /&gt;
            if (sortierteListe.get(index).getNachname()&lt;br /&gt;
                    .compareTo(sortierteListe.get(kleinstesElement).getNachname()) &amp;lt; 0) {&lt;br /&gt;
                kleinstesElement = index;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (kleinstesElement != stelle) {&lt;br /&gt;
            Mitarbeiter puffer = sortierteListe.get(kleinstesElement);&lt;br /&gt;
            sortierteListe.set(kleinstesElement, sortierteListe.get(stelle));&lt;br /&gt;
            sortierteListe.set(stelle, puffer);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        stelle++;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return sortierteListe;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Komplexität ==&lt;br /&gt;
Mit Hilfe der [[Laufzeitanalyse]] betrachten wir nun die Zeitkomplexität des Algorithmus Selection Sort. &lt;br /&gt;
&lt;br /&gt;
Ein besonderes Merkmal dieses Algorithmus ist, dass seine Laufzeit **unabhängig** von der anfänglichen Sortierung der Elemente ist. Um eine Datenstruktur (z. B. ein [[Array]]) mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Einträgen zu sortieren, muss &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Mal das Minimum durch Vergleichen bestimmt werden. &lt;br /&gt;
&lt;br /&gt;
Bei der ersten Bestimmung des Minimums sind &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Vergleiche notwendig, bei der zweiten &amp;lt;math&amp;gt;n - 2&amp;lt;/math&amp;gt; Vergleiche, und so weiter bis zum letzten Durchlauf mit exakt 1 Vergleich. Mit der [[Arithmetische-reihe|gaußschen Summenformel]] erhält man die Gesamtanzahl der immer notwendigen Vergleiche:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;(n - 1) + (n - 2) + \dots + 1 = \frac{n(n - 1)}{2} = \frac{1}{2}n^2 - \frac{1}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Best Case ===&lt;br /&gt;
Das Best-Case-Szenario tritt ein, wenn das Array bereits vollständig aufsteigend sortiert ist. &lt;br /&gt;
Auch wenn alle Elemente bereits am richtigen Platz stehen, &amp;quot;weiß&amp;quot; der Selection Sort dies nicht. Er muss trotzdem in jedem Durchlauf den kompletten unsortierten Rest der Liste überprüfen, um sicherzustellen, dass nicht doch ein noch kleineres Element existiert. Es finden zwar keine Vertauschungen (Swaps) statt, die Anzahl der Vergleiche bleibt jedoch bei &amp;lt;math&amp;gt;\frac{n(n - 1)}{2}&amp;lt;/math&amp;gt;. Da die Vergleiche asymptotisch dominieren, liegt die Zeitkomplexität im Best Case bei &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Average Case ===&lt;br /&gt;
Im durchschnittlichen Fall (Average Case) liegt eine zufällig durchmischte Liste vor. &lt;br /&gt;
Genau wie im Best Case muss der Selection Sort die gesamte arithmetische Reihe an Vergleichen durchführen, um das Minimum in jedem Schritt zu finden. Die Anzahl der durchgeführten Vertauschungen liegt im Schnitt zwischen 0 und &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt;. Auch hier ändert dies nichts an der dominanten quadratischen Anzahl der Vergleiche. Die Zeitkomplexität im Average Case ist somit ebenfalls &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Worst Case ===&lt;br /&gt;
Das Worst-Case-Szenario liegt vor, wenn die Liste in umgekehrter (absteigender) Reihenfolge sortiert ist.&lt;br /&gt;
Der Algorithmus führt wieder exakt &amp;lt;math&amp;gt;\frac{n(n - 1)}{2}&amp;lt;/math&amp;gt; Vergleiche aus. In diesem Fall muss bei fast jedem Durchlauf (maximal &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Mal) das kleinste Element mit dem aktuellen Start-Element vertauscht werden. Da die maximale Anzahl der Vertauschungen linear (&amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;) wächst, die Anzahl der Vergleiche jedoch quadratisch, bestimmt der quadratische Term das Laufzeitverhalten. Die Komplexitätsklasse im Worst Case liegt demnach bei &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;br /&gt;
[[Kategorie:FI_I_TP2]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Insertionsort&amp;diff=2838</id>
		<title>Insertionsort</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Insertionsort&amp;diff=2838"/>
		<updated>2026-04-28T08:48:13Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
Der [[Algorithmus]] dieses [[Sortieren|Sortierverfahrens]] ist relativ simpel. Das Prinzip von Insertion Sort ist folgendes: Die einzelnen Elemente werden von vorne nach hinten durchlaufen. Von der aktuellen Position aus wird jedes Element von rechts nach links verschoben – und zwar so lange, bis das einzufügende Element größer oder gleich dem Element ist, das an der aktuell betrachteten Position steht.&lt;br /&gt;
&lt;br /&gt;
Der Platz für das Element, das verschoben wird, ist währenddessen frei. Diese Lücke wird anschließend mit dem entsprechenden Wert an der richtigen Stelle gefüllt.&lt;br /&gt;
&lt;br /&gt;
== Beispiel ==&lt;br /&gt;
[[Datei:Animation Insertio-Sort.gif|mini]]&lt;br /&gt;
Die folgende Tabelle zeigt die Sortierschritte zum Sortieren der Folge 5 7 0 3 4 2 6 1. Auf der linken Seite (grün dargestellt) befindet sich jeweils der bereits sortierte Teil der Folge. Die blauen Ziffern repräsentieren den unsortierten Teil der Zahlenfolge. Ganz rechts steht in Klammern die Anzahl der Positionen, um die das eingefügte Element nach links verschoben wurde. Das aktuell eingefügte Element ist fett markiert.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center; font-family:monospace;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;5&amp;lt;/span&amp;gt;&#039;&#039; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;7&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;3&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; || (0)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;5&amp;lt;/span&amp;gt;&#039;&#039;&#039; || &#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;7&amp;lt;/span&amp;gt;&#039;&#039;&#039; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;3&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; || (0)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt;&#039;&#039;&#039;&#039;&#039; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;5&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;7&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;3&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; || (2)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt; || &#039;&#039;&#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;3&amp;lt;/span&amp;gt;&#039;&#039;&#039;&#039;&#039; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;5&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;7&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; || (2)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;3&amp;lt;/span&amp;gt; || &#039;&#039;&#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt;&#039;&#039;&#039;&#039;&#039; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;5&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;7&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; || (2)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt; || &#039;&#039;&#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt;&#039;&#039;&#039;&#039;&#039; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;3&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;5&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;7&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; || (4)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;3&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;5&amp;lt;/span&amp;gt; || &#039;&#039;&#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt;&#039;&#039;&#039;&#039;&#039; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;7&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:blue;&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; || (1)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt; || &#039;&#039;&#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt;&#039;&#039;&#039;&#039;&#039; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;3&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;5&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; || &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;7&amp;lt;/span&amp;gt; || (6)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Pseudocode ==&lt;br /&gt;
Der [[Algorithmus]] sieht im Pseudocode so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
prozedur insertionSort(A ist Liste sortierbarer Elemente)&lt;br /&gt;
    n = Länge von A&lt;br /&gt;
    für i von 1 bis n - 1 wiederhole&lt;br /&gt;
        einzusortierenderWert = A[i]&lt;br /&gt;
        j = i - 1&lt;br /&gt;
        solange j ≥ 0 und A[j] &amp;gt; einzusortierenderWert wiederhole&lt;br /&gt;
            A[j + 1] = A[j]&lt;br /&gt;
            j = j - 1&lt;br /&gt;
        ende solange&lt;br /&gt;
        A[j + 1] = einzusortierenderWert&lt;br /&gt;
    ende für&lt;br /&gt;
ende prozedur&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Komplexität ==&lt;br /&gt;
&lt;br /&gt;
=== Best Case ===&lt;br /&gt;
Die optimale Eingabe ist ein bereits sortiertes [[Array]]. In diesem Fall wird pro Iteration lediglich ein Vergleich durchgeführt, und die innere Schleife wird nie betreten. Die Gesamtanzahl der Vergleiche ist somit proportional zur Anzahl der Elemente &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Insertion Sort besitzt im Best Case daher eine lineare Laufzeit von &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Average Case ===&lt;br /&gt;
Das Average-Case-Szenario geht von einer zufälligen Verteilung der Werte in der Liste aus.&lt;br /&gt;
&lt;br /&gt;
Im Durchschnitt ist das aktuell betrachtete Element kleiner als die Hälfte der bereits sortierten Elemente. Die innere Schleife muss das Element also im Mittel nur bis in die Mitte des bisher sortierten Teilbereichs verschieben. Die Anzahl der Vergleiche und Verschiebungen halbiert sich im Vergleich zum Worst Case dadurch in etwa.&lt;br /&gt;
&lt;br /&gt;
Da in der asymptotischen Landau-Notation konstante Faktoren (wie diese Halbierung) ignoriert werden, dominiert weiterhin die höchste Potenz das Wachstum. Die Zeitkomplexität liegt im Average Case demnach bei &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Worst Case ===&lt;br /&gt;
Eine Liste in umgekehrter (absteigender) Reihenfolge stellt das Worst-Case-Szenario für Insertion Sort dar, beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;[11, 7, 5, 3, 2, 0]&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir betrachten nur die Vergleichsoperationen, die mit der Variablen &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; gezählt werden. Die Anzahl der zu sortierenden Elemente beträgt &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Beim ersten äußeren Schleifendurchlauf ist &amp;lt;math&amp;gt;c = 1&amp;lt;/math&amp;gt;, da nur ein Vergleich durchgeführt wird. Beim zweiten Durchlauf sind es 2 Vergleiche, beim dritten 3 usw., bis zum letzten Durchlauf mit &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Vergleichen.&lt;br /&gt;
&lt;br /&gt;
Die Gesamtanzahl der Vergleiche ergibt sich zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;1 + 2 + 3 + \dots + (n - 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist eine [[Arithmetische-reihe|arithmetische Reihe]]. Mit der gaußschen Summenformel erhält man:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{n(n-1)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; dominiert der quadratische Term, sodass Insertion Sort im Worst Case in der Komplexitätsklasse &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; liegt.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;br /&gt;
[[Kategorie:FI_I_TP2]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Fremdschl%C3%BCssel&amp;diff=2837</id>
		<title>Fremdschlüssel</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Fremdschl%C3%BCssel&amp;diff=2837"/>
		<updated>2026-04-23T09:21:39Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: Weiterleitung nach Schlüssel#Fremdschlüssel erstellt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#redirect[[Schlüssel#Fremdschlüssel]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Referentielle_Integrit%C3%A4t&amp;diff=2836</id>
		<title>Referentielle Integrität</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Referentielle_Integrit%C3%A4t&amp;diff=2836"/>
		<updated>2026-04-23T09:21:21Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: Weiterleitung nach Schlüssel#Referentielle Integrität erstellt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#redirect[[Schlüssel#Referentielle_Integrität]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Schl%C3%BCssel&amp;diff=2835</id>
		<title>Schlüssel</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Schl%C3%BCssel&amp;diff=2835"/>
		<updated>2026-04-23T09:08:15Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: /* Fremdschlüssel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein &#039;&#039;&#039;Schlüssel&#039;&#039;&#039; dient in einer [[Relationale Datenbank|relationalen Datenbank]] dazu, die [[Datensatz|Datensätze]] einer [[Datenbanktabelle]] eindeutig zu identifizieren. Ein Schlüssel ist eine Gruppe von Spalten, die so ausgewählt wird, dass jede Tabellenzeile über den Werten dieser Spaltengruppe eine einmalige Wertekombination hat.&lt;br /&gt;
&lt;br /&gt;
== Superschlüssel ==&lt;br /&gt;
[[Datei:Schlüssel-ZusammenhangSchlüssel.png|mini]]&lt;br /&gt;
Bestimmen einige Attribute einer Relation eindeutig die Werte aller Attribute der Relation, so spricht man von einem &#039;&#039;&#039;Superschlüssel&#039;&#039;&#039;. Das bedeutet, dass jedes Tupel dieser Relation eindeutig durch die Werte dieser Attribute bestimmt ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039; Relation Mitarbeiter {Nachname, Vorname, Geburtsdatum, PLZ, Straße, Umsatz, Dienstjahre}&lt;br /&gt;
&lt;br /&gt;
Im Extremfall verwendet man alle Attribute der Relation, um ein Tupel zu bestimmen. Zum Beispiel gilt: Nachname, Vorname, Geburtsdatum, PLZ, Straße, Umsatz → Dienstjahre. Dies wäre ein legitimer Superschlüssel. Allerdings kann man aus dem Superschlüssel noch Attribute weglassen, um ein Tupel eindeutig zu bestimmen, was zu einem Schlüsselkandidaten führt.&lt;br /&gt;
&lt;br /&gt;
== Schlüsselkandidat ==&lt;br /&gt;
Ein &#039;&#039;&#039;Schlüsselkandidat&#039;&#039;&#039; ist ein minimaler Superschlüssel. Das bedeutet, dass keine echte Teilmenge der Attribute dieses Schlüssels vollständig die Werte aller anderen Attribute der Relation bestimmt. Unter allen Schlüsselkandidaten einer Relation wird ein Primärschlüssel ausgewählt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039; Die Attribute Nachname, Vorname, Geburtsdatum, PLZ könnten ausreichen, um einen Datensatz eindeutig zu identifizieren. Wenn kein Attribut mehr weglassen werden kann und die Attributmenge somit minimal ist, handelt es sich um einen Schlüsselkandidaten. Eine Relation kann mehrere Schlüsselkandidaten haben.&lt;br /&gt;
&lt;br /&gt;
== Primärschlüssel ==&lt;br /&gt;
[[Datei:Schlüssel-SchlüsselInTabellen.png|mini]]&lt;br /&gt;
Ein &#039;&#039;&#039;Primärschlüssel&#039;&#039;&#039;  ist in einer [[Relationale Datenbank|relationalen Datenbank]] ein [[Schlüssel]] zur eindeutigen Identifizierung eines Datensatzes in einer [[Datenbanktabelle]].&lt;br /&gt;
&lt;br /&gt;
=== Definition und Eigenschaften ===&lt;br /&gt;
[[Datei:Datenbanktabelle.jpg|mini]]&lt;br /&gt;
Der Primärschlüssel (engl. &#039;&#039;primary key&#039;&#039;) wird aus der Menge der Schlüsselkandidaten einer Tabelle ausgewählt und muss folgende Eigenschaften erfüllen:&lt;br /&gt;
* &#039;&#039;&#039;Eindeutigkeit&#039;&#039;&#039;: Jeder Wert des Primärschlüssels darf in der Tabelle nur einmal vorkommen&lt;br /&gt;
* &#039;&#039;&#039;Nicht-NULL&#039;&#039;&#039;: Der Primärschlüssel darf keinen NULL-Wert enthalten&lt;br /&gt;
* &#039;&#039;&#039;Minimalität&#039;&#039;&#039;: Der Primärschlüssel soll aus möglichst wenigen Attributen bestehen&lt;br /&gt;
* &#039;&#039;&#039;Stabilität&#039;&#039;&#039;: Die Werte des Primärschlüssels sollten sich während der Lebensdauer des Datensatzes nicht ändern&lt;br /&gt;
&lt;br /&gt;
=== Arten von Primärschlüsseln ===&lt;br /&gt;
==== Natürlicher Schlüssel ====&lt;br /&gt;
Ein natürlicher Schlüssel besteht aus Attributen, die bereits in den Geschäftsdaten vorhanden sind (z.B. Personalnummer, ISBN-Nummer).&lt;br /&gt;
&lt;br /&gt;
==== Künstlicher Schlüssel ====&lt;br /&gt;
Ein künstlicher Schlüssel wird speziell für die Datenbank erzeugt und hat keine natürliche Bedeutung in den Geschäftsdaten (z.B. automatisch generierte IDs).&lt;br /&gt;
&lt;br /&gt;
=== Bedeutung ===&lt;br /&gt;
Primärschlüssel sind fundamental für:&lt;br /&gt;
* Gewährleistung der [[Datenintegrität]]&lt;br /&gt;
* Aufbau von [[Beziehung (Datenbank)|Beziehungen]] zwischen Tabellen über [[Fremdschlüssel]]&lt;br /&gt;
* Erstellung von [[Datenbankindex|Indizes]] für performante Datenabfragen&lt;br /&gt;
* Vermeidung von Datenduplikaten&lt;br /&gt;
&lt;br /&gt;
=== Beispiel ===&lt;br /&gt;
In einer Tabelle &amp;quot;Mitarbeiter&amp;quot; könnte der Primärschlüssel so definiert werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE `mydb`.`Mitarbeiter` (&lt;br /&gt;
    `idMitarbeiter` INT NOT NULL ,&lt;br /&gt;
    `Vorname` VARCHAR(45) NULL ,&lt;br /&gt;
    `Nachname` VARCHAR(45) NULL ,&lt;br /&gt;
    `FKSalon` INT NOT NULL ,&lt;br /&gt;
    PRIMARY KEY (`idMitarbeiter`) &lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fremdschlüssel ==&lt;br /&gt;
Ein &#039;&#039;&#039;Fremdschlüssel&#039;&#039;&#039; (engl. &#039;&#039;Foreign Key&#039;&#039;) ist ein Attribut oder eine Attributkombination einer Relation (Tabelle), welches auf einen [[Primärschlüssel]] (bzw. Schlüsselkandidaten) einer anderen oder der gleichen Relation verweist. Er dient als Verweis zwischen zwei Relationen und zeigt an, welche Tupel (Datensätze) inhaltlich miteinander in Verbindung stehen.&lt;br /&gt;
&lt;br /&gt;
=== Zweck und Vorteile ===&lt;br /&gt;
Die Definition von Fremdschlüsseln ist essenziell für ein relationales Datenbankdesign. Sie ermöglicht es dem Datenbankmanagementsystem (DBMS):&lt;br /&gt;
* Eingegebene Daten automatisch zu überprüfen und fehlerhafte Zuweisungen (z. B. auf nicht existierende Datensätze) abzulehnen und zu verwerfen.&lt;br /&gt;
* Änderungen an verknüpften Tabellen durch kaskadierendes Löschen (&#039;&#039;Cascaded Delete&#039;&#039;) oder Aktualisieren (&#039;&#039;Cascaded Update&#039;&#039;) konsistent zu halten.&lt;br /&gt;
* Spezifische Integritätsbedingungen (Constraints) direkt im Schema zu definieren.&lt;br /&gt;
&lt;br /&gt;
=== Referentielle Integrität ===&lt;br /&gt;
Die wichtigste Bedingung im Zusammenhang mit Fremdschlüsseln ist die &#039;&#039;&#039;referentielle Integrität&#039;&#039;&#039; (auch Beziehungsintegrität). Sie besagt, dass Attributwerte eines Fremdschlüssels zwingend auch als Attributwert des referenzierten Primärschlüssels vorhanden sein müssen. &lt;br /&gt;
&lt;br /&gt;
Über die referentielle Integrität kontrolliert das DBMS die Beziehungen zwischen Datensätzen. Werden dem System diese Beziehungen in der Struktur bekannt gemacht, ist es in der Lage, sie zu überwachen und so die Datenintegrität (Entitätenintegrität) sicherzustellen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel zur Überwachung:&#039;&#039;&#039;&lt;br /&gt;
In einer Friseursalon-Datenbank gibt es eine Tabelle `mitarbeiter`, die über den Fremdschlüssel `idsalon` auf den Primärschlüssel der Tabelle `salon` verweist.&lt;br /&gt;
* Ein neuer Mitarbeiter-Datensatz &amp;lt;code&amp;gt;{6, Müller, Klaus, 4}&amp;lt;/code&amp;gt; würde die Integritätsbedingungen verletzen und vom DBMS &#039;&#039;&#039;zurückgewiesen&#039;&#039;&#039;, wenn es gar keinen Salon mit der ID 4 gibt.&lt;br /&gt;
* Ein bestehender Salon-Datensatz &amp;lt;code&amp;gt;{3, Kaiserschnitt}&amp;lt;/code&amp;gt; ist hingegen unproblematisch, auch wenn noch kein Mitarbeiter auf diesen verweist. Ein Primärschlüssel darf von einem Fremdschlüssel referenziert werden, er muss es aber nicht.&lt;br /&gt;
&lt;br /&gt;
=== Umsetzung in DDL ===&lt;br /&gt;
Das Schema für Tabellen und ihre Beziehungen, einschließlich der Regeln zur referentiellen Integrität, wird in der [[Data Definition Language|DDL]] definiert. In SQL wird dies durch das Schlüsselwort &amp;lt;code&amp;gt;FOREIGN KEY&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;REFERENCES&amp;lt;/code&amp;gt; umgesetzt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel eines Schemas in SQL:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE `salon` (&lt;br /&gt;
    `idsalon` INT NOT NULL AUTO_INCREMENT,&lt;br /&gt;
    `name` VARCHAR(45) NOT NULL,&lt;br /&gt;
    PRIMARY KEY (`idsalon`)&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE `mitarbeiter` (&lt;br /&gt;
    `idmitarbeiter` INT NOT NULL AUTO_INCREMENT,&lt;br /&gt;
    `nachname` VARCHAR(45) NOT NULL, &lt;br /&gt;
    `vorname` VARCHAR(45) NOT NULL,&lt;br /&gt;
    `idsalon` INT NOT NULL,&lt;br /&gt;
    PRIMARY KEY (`idmitarbeiter`),&lt;br /&gt;
    CONSTRAINT `fksalon` FOREIGN KEY (`idsalon`) &lt;br /&gt;
        REFERENCES `friseursalon`.`salon` (`idsalon`) &lt;br /&gt;
        ON DELETE NO ACTION ON UPDATE NO ACTION&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Künstlicher Schlüssel ==&lt;br /&gt;
Ein &#039;&#039;&#039;künstlicher Schlüssel&#039;&#039;&#039; ist ein Datenbankschlüssel, der nicht aus den Attributen in der Tabelle abgeleitet wird. Künstliche Schlüssel werden automatisch gebildet (z. B. als fortlaufende Nummer) und häufig als Primärschlüssel verwendet.&lt;br /&gt;
&lt;br /&gt;
=== Vorteile ===&lt;br /&gt;
* Vereinfachte Referenz auf ein Datenelement&lt;br /&gt;
* Nur ein einzelnes Feld muss als Fremdschlüssel verwaltet werden&lt;br /&gt;
* Der Wert bleibt bei Änderungen des Datenobjektes unverändert&lt;br /&gt;
* Keine Änderung des Fremdschlüssels notwendig&lt;br /&gt;
* Vermeidet Probleme mit natürlich Schlüsseln, die ihre Eindeutigkeit verlieren können&lt;br /&gt;
&lt;br /&gt;
=== Praxisbeispiel ===&lt;br /&gt;
In der Praxis ist es oft nicht klar, welche Felder einen sprechenden Schlüssel bilden. Sobald es z.B. einen Kunden mit gleichem Vornamen, Nachnamen und Geburtstag gibt, müssen zusätzliche Felder (wie z.B. die PLZ) hinzugefügt werden, was künstliche Schlüssel attraktiv macht.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Datenbanken]]&lt;br /&gt;
[[Kategorie:FI_I_SDM]]&lt;br /&gt;
[[Kategorie:FI_I_TP2]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2834</id>
		<title>Laufzeitanalyse</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2834"/>
		<updated>2026-04-21T06:38:52Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: /* Laufzeitanalyse von Insertion Sort */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]]&lt;br /&gt;
Die Laufzeitanalyse untersucht die sogenannte Zeitkomplexität eines [[Algorithmus]]. Man stellt sich dabei im Kern die Frage: &#039;&#039;&#039;„Wie verhält sich die Rechenzeit, wenn die Menge der zu verarbeitenden Daten extrem groß wird?“&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Da die echte Ausführungszeit (in Sekunden) stark vom jeweiligen Computer abhängt, misst man stattdessen die Anzahl der elementaren [[Anweisung|Befehle]] (z. B. Vergleiche oder Tauschoperationen). Diese Anzahl hängt von der Größe der Eingabemenge ab, die in der Informatik meist als &#039;&#039;&#039;Problemgröße &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&#039;&#039;&#039; bezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
Für kleine Datenmengen (z. B. eine Liste mit 10 Elementen sortieren) ist fast jeder Algorithmus schnell genug. Spannend wird es erst bei sehr großen Datenmengen (z. B. Millionen von Kundendatensätzen). Hier betrachtet man die sogenannte asymptotische [[Programmausführung|Laufzeit]]. Sie beschreibt das Verhalten des Algorithmus, wenn die Datenmenge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; theoretisch ins Unendliche wächst.&lt;br /&gt;
&lt;br /&gt;
Lässt sich die Laufzeit eines Algorithmus mathematisch durch eine Funktion beschreiben, wie beispielsweise:&lt;br /&gt;
&amp;lt;math&amp;gt;f(x) = \frac{1}{x} + x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So nähert sich die rot dargestellte Funktion &amp;lt;math&amp;gt;\color{red}{f(x)}&amp;lt;/math&amp;gt; für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; der grün dargestellten Asymptote &amp;lt;math&amp;gt;\color{green}{g(x) = x}&amp;lt;/math&amp;gt; an.&lt;br /&gt;
&lt;br /&gt;
Die Laufzeit wird mit der sogenannten Landau-Notation (Groß-O-Notation, z. B. &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;) abgeschätzt. Dabei werden konstante Vorfaktoren (wie &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt;) oder kleinere Summanden ignoriert, da bei riesigen Datenmengen nur noch der dominierende Teil der Funktion das Wachstum bestimmt.&lt;br /&gt;
&lt;br /&gt;
=== Was die Laufzeitanalyse NICHT ist ===&lt;br /&gt;
Sie misst &#039;&#039;&#039;nicht&#039;&#039;&#039; den tatsächlichen Zeitaufwand in Millisekunden auf einem echten Computer. Sie ist ein theoretisches Modell, um Algorithmen hardwareunabhängig miteinander vergleichen zu können.&lt;br /&gt;
&lt;br /&gt;
== Szenarien der Laufzeitabschätzung ==&lt;br /&gt;
Da Algorithmen je nach Vorsortierung der Daten unterschiedlich schnell arbeiten, unterscheidet man drei klassische Fälle:&lt;br /&gt;
&lt;br /&gt;
* Das &#039;&#039;&#039;Worst-Case-Szenario&#039;&#039;&#039; (Schlechtester Fall): Die Daten liegen so ungünstig vor, dass der Algorithmus maximal lange braucht. Dies liefert eine absolute Sicherheitsgarantie (obere Schranke).&lt;br /&gt;
* Das &#039;&#039;&#039;Best-Case-Szenario&#039;&#039;&#039; (Bester Fall): Die Daten liegen optimal vor (z. B. bereits sortiert). Der Algorithmus ist minimal schnell fertig.&lt;br /&gt;
* Das &#039;&#039;&#039;Average-Case-Szenario&#039;&#039;&#039; (Durchschnittlicher Fall): Beschreibt die zu erwartende Laufzeit bei völlig zufällig gemischten Daten. Dies ist oft das realistischste Szenario in der Praxis.&lt;br /&gt;
&lt;br /&gt;
== Mathematische Berechnung des Average-Case ==&lt;br /&gt;
Oft wird fälschlicherweise angenommen, der Average-Case sei einfach „die Hälfte des Worst-Case“. Das ist mathematisch ungenau. Tatsächlich berechnet man den Average-Case mit Hilfe der Stochastik: Er entspricht dem &#039;&#039;&#039;Erwartungswert&#039;&#039;&#039; der Laufzeit.&lt;br /&gt;
&lt;br /&gt;
Man berechnet ihn, indem man alle möglichen Eingaben durchspielt und gewichtet. Die Formel für den Erwartungswert &amp;lt;math&amp;gt;E(T)&amp;lt;/math&amp;gt; lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \sum_{i=1}^{|F|} p_i \cdot t_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei bedeuten die Variablen:&lt;br /&gt;
* &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; ist die Menge aller theoretisch möglichen Eingaben der Größe &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; (z. B. alle möglichen Anordnungen einer Zahlenliste).&lt;br /&gt;
* &amp;lt;math&amp;gt;|F|&amp;lt;/math&amp;gt; (Betragsstriche) steht in der Mathematik für die Mächtigkeit einer Menge. Es ist also die &#039;&#039;&#039;exakte Anzahl&#039;&#039;&#039; aller dieser möglichen Eingaben.&lt;br /&gt;
* &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; ist die Wahrscheinlichkeit, dass genau die spezifische Eingabe &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; auftritt.&lt;br /&gt;
* &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; ist die Anzahl der Rechenschritte, die der Algorithmus für exakt diese Eingabe &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; braucht.&lt;br /&gt;
&lt;br /&gt;
Für die Schule gehen wir meistens von einer &#039;&#039;&#039;Gleichverteilung&#039;&#039;&#039; aus: Jeder Eingabefall tritt mit exakt derselben Wahrscheinlichkeit auf.&lt;br /&gt;
&lt;br /&gt;
=== Average-Case der Linearen Suche ===&lt;br /&gt;
Wir suchen eine bestimmte Zahl in einem Array der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Wir nehmen an, die Zahl ist im Array und jede Position ist gleich wahrscheinlich.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schritt 1: Wahrscheinlichkeit bestimmen.&#039;&#039;&#039; Die Wahrscheinlichkeit, dass unsere Zahl an Position 1, 2 oder &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; steht, ist jeweils &amp;lt;math&amp;gt;p = \frac{1}{n}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 2: Schritte zählen.&#039;&#039;&#039; Steht die Zahl an Position 1, brauchen wir 1 Vergleich. An Position 2 brauchen wir 2 Vergleiche. An Position &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; brauchen wir &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Vergleiche.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 3: Erwartungswert berechnen.&#039;&#039;&#039; Wir setzen dies in die Formel ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n}\cdot 1 + \frac{1}{n}\cdot 2 + \dots + \frac{1}{n}\cdot n = \sum_{i=1}^{n} \left( \frac{1}{n} \cdot i \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir klammern &amp;lt;math&amp;gt;\frac{1}{n}&amp;lt;/math&amp;gt; aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \sum_{i=1}^{n} i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Summe der Zahlen von 1 bis &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; lässt sich mit der &#039;&#039;&#039;Gaußschen Summenformel&#039;&#039;&#039; (kleiner Gauß) vereinfachen zu &amp;lt;math&amp;gt;\frac{n(n+1)}{2}&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \frac{n(n+1)}{2} = \frac{n+1}{2} = \frac{1}{2}n + \frac{1}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fazit:&#039;&#039;&#039; Im Durchschnitt braucht die lineare Suche &amp;lt;math&amp;gt;\frac{n+1}{2}&amp;lt;/math&amp;gt; Vergleiche. Da in der &amp;lt;math&amp;gt;\mathcal{O}&amp;lt;/math&amp;gt;-Notation Konstanten wie &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; wegfallen, gehört die lineare Suche im Durchschnitt zur Klasse &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Laufzeitanalyse von Insertion Sort ==&lt;br /&gt;
Nun wenden wir unser Wissen auf das Sortierverfahren [[Insertion-sort|Insertion Sort]] (Sortieren durch Einfügen) an. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void sortierenEinfuegen_MC() {&lt;br /&gt;
    int v, i, j;&lt;br /&gt;
    for (i = 1; i &amp;lt; zSortfeld.length; i++) {&lt;br /&gt;
        if (zSortfeld[i] &amp;lt; zSortfeld[i - 1]) {&lt;br /&gt;
            v = zSortfeld[i];&lt;br /&gt;
            j = i;&lt;br /&gt;
            do {&lt;br /&gt;
                zSortfeld[j] = zSortfeld[j - 1];&lt;br /&gt;
                j--;&lt;br /&gt;
            } while ((j &amp;gt; 0) &amp;amp;&amp;amp; (zSortfeld[j - 1] &amp;gt; v));&lt;br /&gt;
            zSortfeld[j] = v; &lt;br /&gt;
        }&lt;br /&gt;
        printAnalyse(i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir analysieren eine Liste mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen. Den Zeitverbrauch für einen einzelnen Vergleich nennen wir &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 1. Best-Case-Szenario (Bester Fall) ===&lt;br /&gt;
Die Liste ist bereits perfekt aufsteigend sortiert (z. B. &amp;lt;code&amp;gt;[0, 2, 3, 5, 7]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Da jedes Element schon größer als sein Vorgänger ist, ist die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung immer falsch. Die innere &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife wird ignoriert.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Wir machen exakt einen Vergleich pro Element in der äußeren Schleife. Das ergibt &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt; Vergleiche.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Der Aufwand wächst wie eine Gerade (linear). Die Komplexität ist &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 2. Worst-Case-Szenario (Schlechtester Fall) ===&lt;br /&gt;
Die Liste ist exakt falsch herum sortiert (z. B. &amp;lt;code&amp;gt;[7, 5, 3, 2, 0]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung ist immer wahr. Jedes Element muss durch die innere Schleife komplett bis an den Anfang der Liste getauscht werden.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Beim 1. Durchlauf machen wir 1 Vergleich. Beim 2. Durchlauf 2 Vergleiche. Beim letzten Durchlauf &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Vergleiche. Wir müssen also wieder addieren: &amp;lt;math&amp;gt;1 + 2 + 3 + \dots + (n-1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Wie bei der linearen Suche hilft uns hier die Gaußsche Summenformel:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot \frac{(n - 1) \cdot n}{2} = \frac{c}{2}n^2 - \frac{c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Asymptote an quadratische Funktion2.png|mini|Quadratisches Wachstum]]&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Für extrem große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; dominiert das &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; alles andere. Wir ignorieren den Faktor &amp;lt;math&amp;gt;\frac{c}{2}&amp;lt;/math&amp;gt; und den kleineren Teil &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Das Wachstum ist quadratisch, also &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 3. Average-Case-Szenario (Durchschnittlicher Fall) ===&lt;br /&gt;
Wir sortieren ein Array der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Wir nehmen an, alle Permutationen (Anordnungen) der Zahlen sind gleich wahrscheinlich. Der Algorithmus fügt nacheinander jedes Element an Index &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; (von Position 2 bis &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) in den bereits sortierten vorderen Teil der Länge &amp;lt;math&amp;gt;i-1&amp;lt;/math&amp;gt; ein.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schritt 1: Wahrscheinlichkeit bestimmen.&#039;&#039;&#039; Beim Einfügen des &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-ten Elements gibt es genau &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; mögliche Zielpositionen (von ganz vorne bis zu seinem aktuellen Platz ganz hinten). Da die ursprüngliche Reihenfolge zufällig ist, ist jede dieser Positionen gleich wahrscheinlich. Die Wahrscheinlichkeit für jede Position ist also &amp;lt;math&amp;gt;p = \frac{1}{i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 2: Schritte zählen.&#039;&#039;&#039; Das Finden der richtigen Position ist wie eine rückwärtsgerichtete lineare Suche. Im besten Fall (das Element ist bereits größer als alle vorherigen und bleibt am Ende) brauchen wir 1 Vergleich. Im Durchschnitt muss das Element mit der Hälfte der bereits sortierten Elemente verglichen werden, um seinen Platz zu finden. Der durchschnittliche Aufwand (Vergleiche) für das Einfügen des &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-ten Elements liegt daher bei etwa &amp;lt;math&amp;gt;\frac{i}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 3: Erwartungswert berechnen.&#039;&#039;&#039; Um die Gesamtlaufzeit zu erhalten, summieren wir den durchschnittlichen Aufwand für alle Elemente, die eingefügt werden müssen (von &amp;lt;math&amp;gt;i=2&amp;lt;/math&amp;gt; bis &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) \approx \sum_{i=2}^{n} \frac{i}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir klammern &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) \approx \frac{1}{2} \cdot \sum_{i=2}^{n} i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Summe der Zahlen von 1 bis &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; lässt sich wieder mit der &#039;&#039;&#039;Gaußschen Summenformel&#039;&#039;&#039; (kleiner Gauß) vereinfachen zu &amp;lt;math&amp;gt;\frac{n(n+1)}{2}&amp;lt;/math&amp;gt;. Da unsere Summe aber erst bei &amp;lt;math&amp;gt;i=2&amp;lt;/math&amp;gt; startet, ziehen wir die 1 (für den fehlenden Schritt &amp;lt;math&amp;gt;i=1&amp;lt;/math&amp;gt;) ab:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) \approx \frac{1}{2} \cdot \left( \frac{n(n+1)}{2} - 1 \right) = \frac{n^2 + n - 2}{4} = \frac{1}{4}n^2 + \frac{1}{4}n - \frac{1}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fazit:&#039;&#039;&#039; Im Durchschnitt braucht der Insertion Sort etwa &amp;lt;math&amp;gt;\frac{1}{4}n^2&amp;lt;/math&amp;gt; Vergleiche. Da in der &amp;lt;math&amp;gt;\mathcal{O}&amp;lt;/math&amp;gt;-Notation Konstanten wie &amp;lt;math&amp;gt;\frac{1}{4}&amp;lt;/math&amp;gt; und Terme niedrigerer Ordnung (wie &amp;lt;math&amp;gt;\frac{1}{4}n - \frac{1}{2}&amp;lt;/math&amp;gt;) wegfallen, gehört der Insertion Sort im Durchschnitt zur Klasse &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2833</id>
		<title>Laufzeitanalyse</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2833"/>
		<updated>2026-04-21T06:36:28Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: /* Average-Case des Insertion Sort */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]]&lt;br /&gt;
Die Laufzeitanalyse untersucht die sogenannte Zeitkomplexität eines [[Algorithmus]]. Man stellt sich dabei im Kern die Frage: &#039;&#039;&#039;„Wie verhält sich die Rechenzeit, wenn die Menge der zu verarbeitenden Daten extrem groß wird?“&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Da die echte Ausführungszeit (in Sekunden) stark vom jeweiligen Computer abhängt, misst man stattdessen die Anzahl der elementaren [[Anweisung|Befehle]] (z. B. Vergleiche oder Tauschoperationen). Diese Anzahl hängt von der Größe der Eingabemenge ab, die in der Informatik meist als &#039;&#039;&#039;Problemgröße &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&#039;&#039;&#039; bezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
Für kleine Datenmengen (z. B. eine Liste mit 10 Elementen sortieren) ist fast jeder Algorithmus schnell genug. Spannend wird es erst bei sehr großen Datenmengen (z. B. Millionen von Kundendatensätzen). Hier betrachtet man die sogenannte asymptotische [[Programmausführung|Laufzeit]]. Sie beschreibt das Verhalten des Algorithmus, wenn die Datenmenge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; theoretisch ins Unendliche wächst.&lt;br /&gt;
&lt;br /&gt;
Lässt sich die Laufzeit eines Algorithmus mathematisch durch eine Funktion beschreiben, wie beispielsweise:&lt;br /&gt;
&amp;lt;math&amp;gt;f(x) = \frac{1}{x} + x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So nähert sich die rot dargestellte Funktion &amp;lt;math&amp;gt;\color{red}{f(x)}&amp;lt;/math&amp;gt; für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; der grün dargestellten Asymptote &amp;lt;math&amp;gt;\color{green}{g(x) = x}&amp;lt;/math&amp;gt; an.&lt;br /&gt;
&lt;br /&gt;
Die Laufzeit wird mit der sogenannten Landau-Notation (Groß-O-Notation, z. B. &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;) abgeschätzt. Dabei werden konstante Vorfaktoren (wie &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt;) oder kleinere Summanden ignoriert, da bei riesigen Datenmengen nur noch der dominierende Teil der Funktion das Wachstum bestimmt.&lt;br /&gt;
&lt;br /&gt;
=== Was die Laufzeitanalyse NICHT ist ===&lt;br /&gt;
Sie misst &#039;&#039;&#039;nicht&#039;&#039;&#039; den tatsächlichen Zeitaufwand in Millisekunden auf einem echten Computer. Sie ist ein theoretisches Modell, um Algorithmen hardwareunabhängig miteinander vergleichen zu können.&lt;br /&gt;
&lt;br /&gt;
== Szenarien der Laufzeitabschätzung ==&lt;br /&gt;
Da Algorithmen je nach Vorsortierung der Daten unterschiedlich schnell arbeiten, unterscheidet man drei klassische Fälle:&lt;br /&gt;
&lt;br /&gt;
* Das &#039;&#039;&#039;Worst-Case-Szenario&#039;&#039;&#039; (Schlechtester Fall): Die Daten liegen so ungünstig vor, dass der Algorithmus maximal lange braucht. Dies liefert eine absolute Sicherheitsgarantie (obere Schranke).&lt;br /&gt;
* Das &#039;&#039;&#039;Best-Case-Szenario&#039;&#039;&#039; (Bester Fall): Die Daten liegen optimal vor (z. B. bereits sortiert). Der Algorithmus ist minimal schnell fertig.&lt;br /&gt;
* Das &#039;&#039;&#039;Average-Case-Szenario&#039;&#039;&#039; (Durchschnittlicher Fall): Beschreibt die zu erwartende Laufzeit bei völlig zufällig gemischten Daten. Dies ist oft das realistischste Szenario in der Praxis.&lt;br /&gt;
&lt;br /&gt;
== Mathematische Berechnung des Average-Case ==&lt;br /&gt;
Oft wird fälschlicherweise angenommen, der Average-Case sei einfach „die Hälfte des Worst-Case“. Das ist mathematisch ungenau. Tatsächlich berechnet man den Average-Case mit Hilfe der Stochastik: Er entspricht dem &#039;&#039;&#039;Erwartungswert&#039;&#039;&#039; der Laufzeit.&lt;br /&gt;
&lt;br /&gt;
Man berechnet ihn, indem man alle möglichen Eingaben durchspielt und gewichtet. Die Formel für den Erwartungswert &amp;lt;math&amp;gt;E(T)&amp;lt;/math&amp;gt; lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \sum_{i=1}^{|F|} p_i \cdot t_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei bedeuten die Variablen:&lt;br /&gt;
* &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; ist die Menge aller theoretisch möglichen Eingaben der Größe &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; (z. B. alle möglichen Anordnungen einer Zahlenliste).&lt;br /&gt;
* &amp;lt;math&amp;gt;|F|&amp;lt;/math&amp;gt; (Betragsstriche) steht in der Mathematik für die Mächtigkeit einer Menge. Es ist also die &#039;&#039;&#039;exakte Anzahl&#039;&#039;&#039; aller dieser möglichen Eingaben.&lt;br /&gt;
* &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; ist die Wahrscheinlichkeit, dass genau die spezifische Eingabe &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; auftritt.&lt;br /&gt;
* &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; ist die Anzahl der Rechenschritte, die der Algorithmus für exakt diese Eingabe &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; braucht.&lt;br /&gt;
&lt;br /&gt;
Für die Schule gehen wir meistens von einer &#039;&#039;&#039;Gleichverteilung&#039;&#039;&#039; aus: Jeder Eingabefall tritt mit exakt derselben Wahrscheinlichkeit auf.&lt;br /&gt;
&lt;br /&gt;
=== Average-Case der Linearen Suche ===&lt;br /&gt;
Wir suchen eine bestimmte Zahl in einem Array der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Wir nehmen an, die Zahl ist im Array und jede Position ist gleich wahrscheinlich.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schritt 1: Wahrscheinlichkeit bestimmen.&#039;&#039;&#039; Die Wahrscheinlichkeit, dass unsere Zahl an Position 1, 2 oder &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; steht, ist jeweils &amp;lt;math&amp;gt;p = \frac{1}{n}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 2: Schritte zählen.&#039;&#039;&#039; Steht die Zahl an Position 1, brauchen wir 1 Vergleich. An Position 2 brauchen wir 2 Vergleiche. An Position &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; brauchen wir &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Vergleiche.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 3: Erwartungswert berechnen.&#039;&#039;&#039; Wir setzen dies in die Formel ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n}\cdot 1 + \frac{1}{n}\cdot 2 + \dots + \frac{1}{n}\cdot n = \sum_{i=1}^{n} \left( \frac{1}{n} \cdot i \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir klammern &amp;lt;math&amp;gt;\frac{1}{n}&amp;lt;/math&amp;gt; aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \sum_{i=1}^{n} i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Summe der Zahlen von 1 bis &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; lässt sich mit der &#039;&#039;&#039;Gaußschen Summenformel&#039;&#039;&#039; (kleiner Gauß) vereinfachen zu &amp;lt;math&amp;gt;\frac{n(n+1)}{2}&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \frac{n(n+1)}{2} = \frac{n+1}{2} = \frac{1}{2}n + \frac{1}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fazit:&#039;&#039;&#039; Im Durchschnitt braucht die lineare Suche &amp;lt;math&amp;gt;\frac{n+1}{2}&amp;lt;/math&amp;gt; Vergleiche. Da in der &amp;lt;math&amp;gt;\mathcal{O}&amp;lt;/math&amp;gt;-Notation Konstanten wie &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; wegfallen, gehört die lineare Suche im Durchschnitt zur Klasse &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Laufzeitanalyse von Insertion Sort ==&lt;br /&gt;
Nun wenden wir unser Wissen auf das Sortierverfahren [[Insertion-sort|Insertion Sort]] (Sortieren durch Einfügen) an. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void sortierenEinfuegen_MC() {&lt;br /&gt;
    int v, i, j;&lt;br /&gt;
    for (i = 1; i &amp;lt; zSortfeld.length; i++) {&lt;br /&gt;
        c++; // Zähler für Vergleiche&lt;br /&gt;
        if (zSortfeld[i] &amp;lt; zSortfeld[i - 1]) {&lt;br /&gt;
            v = zSortfeld[i];&lt;br /&gt;
            j = i;&lt;br /&gt;
            do {&lt;br /&gt;
                zSortfeld[j] = zSortfeld[j - 1];&lt;br /&gt;
                j--;&lt;br /&gt;
            } while ((j &amp;gt; 0) &amp;amp;&amp;amp; (zSortfeld[j - 1] &amp;gt; v));&lt;br /&gt;
            zSortfeld[j] = v; &lt;br /&gt;
        }&lt;br /&gt;
        printAnalyse(i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir analysieren eine Liste mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen. Den Zeitverbrauch für einen einzelnen Vergleich nennen wir &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 1. Best-Case-Szenario (Bester Fall) ===&lt;br /&gt;
Die Liste ist bereits perfekt aufsteigend sortiert (z. B. &amp;lt;code&amp;gt;[0, 2, 3, 5, 7]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Da jedes Element schon größer als sein Vorgänger ist, ist die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung immer falsch. Die innere &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife wird ignoriert.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Wir machen exakt einen Vergleich pro Element in der äußeren Schleife. Das ergibt &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt; Vergleiche.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Der Aufwand wächst wie eine Gerade (linear). Die Komplexität ist &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 2. Worst-Case-Szenario (Schlechtester Fall) ===&lt;br /&gt;
Die Liste ist exakt falsch herum sortiert (z. B. &amp;lt;code&amp;gt;[7, 5, 3, 2, 0]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung ist immer wahr. Jedes Element muss durch die innere Schleife komplett bis an den Anfang der Liste getauscht werden.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Beim 1. Durchlauf machen wir 1 Vergleich. Beim 2. Durchlauf 2 Vergleiche. Beim letzten Durchlauf &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Vergleiche. Wir müssen also wieder addieren: &amp;lt;math&amp;gt;1 + 2 + 3 + \dots + (n-1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Wie bei der linearen Suche hilft uns hier die Gaußsche Summenformel:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot \frac{(n - 1) \cdot n}{2} = \frac{c}{2}n^2 - \frac{c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Asymptote an quadratische Funktion2.png|mini|Quadratisches Wachstum]]&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Für extrem große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; dominiert das &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; alles andere. Wir ignorieren den Faktor &amp;lt;math&amp;gt;\frac{c}{2}&amp;lt;/math&amp;gt; und den kleineren Teil &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Das Wachstum ist quadratisch, also &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 3. Average-Case-Szenario (Durchschnittlicher Fall) ===&lt;br /&gt;
Wir sortieren ein Array der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Wir nehmen an, alle Permutationen (Anordnungen) der Zahlen sind gleich wahrscheinlich. Der Algorithmus fügt nacheinander jedes Element an Index &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; (von Position 2 bis &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) in den bereits sortierten vorderen Teil der Länge &amp;lt;math&amp;gt;i-1&amp;lt;/math&amp;gt; ein.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schritt 1: Wahrscheinlichkeit bestimmen.&#039;&#039;&#039; Beim Einfügen des &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-ten Elements gibt es genau &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; mögliche Zielpositionen (von ganz vorne bis zu seinem aktuellen Platz ganz hinten). Da die ursprüngliche Reihenfolge zufällig ist, ist jede dieser Positionen gleich wahrscheinlich. Die Wahrscheinlichkeit für jede Position ist also &amp;lt;math&amp;gt;p = \frac{1}{i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 2: Schritte zählen.&#039;&#039;&#039; Das Finden der richtigen Position ist wie eine rückwärtsgerichtete lineare Suche. Im besten Fall (das Element ist bereits größer als alle vorherigen und bleibt am Ende) brauchen wir 1 Vergleich. Im Durchschnitt muss das Element mit der Hälfte der bereits sortierten Elemente verglichen werden, um seinen Platz zu finden. Der durchschnittliche Aufwand (Vergleiche) für das Einfügen des &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-ten Elements liegt daher bei etwa &amp;lt;math&amp;gt;\frac{i}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 3: Erwartungswert berechnen.&#039;&#039;&#039; Um die Gesamtlaufzeit zu erhalten, summieren wir den durchschnittlichen Aufwand für alle Elemente, die eingefügt werden müssen (von &amp;lt;math&amp;gt;i=2&amp;lt;/math&amp;gt; bis &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) \approx \sum_{i=2}^{n} \frac{i}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir klammern &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) \approx \frac{1}{2} \cdot \sum_{i=2}^{n} i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Summe der Zahlen von 1 bis &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; lässt sich wieder mit der &#039;&#039;&#039;Gaußschen Summenformel&#039;&#039;&#039; (kleiner Gauß) vereinfachen zu &amp;lt;math&amp;gt;\frac{n(n+1)}{2}&amp;lt;/math&amp;gt;. Da unsere Summe aber erst bei &amp;lt;math&amp;gt;i=2&amp;lt;/math&amp;gt; startet, ziehen wir die 1 (für den fehlenden Schritt &amp;lt;math&amp;gt;i=1&amp;lt;/math&amp;gt;) ab:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) \approx \frac{1}{2} \cdot \left( \frac{n(n+1)}{2} - 1 \right) = \frac{n^2 + n - 2}{4} = \frac{1}{4}n^2 + \frac{1}{4}n - \frac{1}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fazit:&#039;&#039;&#039; Im Durchschnitt braucht der Insertion Sort etwa &amp;lt;math&amp;gt;\frac{1}{4}n^2&amp;lt;/math&amp;gt; Vergleiche. Da in der &amp;lt;math&amp;gt;\mathcal{O}&amp;lt;/math&amp;gt;-Notation Konstanten wie &amp;lt;math&amp;gt;\frac{1}{4}&amp;lt;/math&amp;gt; und Terme niedrigerer Ordnung (wie &amp;lt;math&amp;gt;\frac{1}{4}n - \frac{1}{2}&amp;lt;/math&amp;gt;) wegfallen, gehört der Insertion Sort im Durchschnitt zur Klasse &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2832</id>
		<title>Laufzeitanalyse</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2832"/>
		<updated>2026-04-21T06:36:16Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: /* 3. Average-Case-Szenario (Durchschnittlicher Fall) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]]&lt;br /&gt;
Die Laufzeitanalyse untersucht die sogenannte Zeitkomplexität eines [[Algorithmus]]. Man stellt sich dabei im Kern die Frage: &#039;&#039;&#039;„Wie verhält sich die Rechenzeit, wenn die Menge der zu verarbeitenden Daten extrem groß wird?“&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Da die echte Ausführungszeit (in Sekunden) stark vom jeweiligen Computer abhängt, misst man stattdessen die Anzahl der elementaren [[Anweisung|Befehle]] (z. B. Vergleiche oder Tauschoperationen). Diese Anzahl hängt von der Größe der Eingabemenge ab, die in der Informatik meist als &#039;&#039;&#039;Problemgröße &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&#039;&#039;&#039; bezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
Für kleine Datenmengen (z. B. eine Liste mit 10 Elementen sortieren) ist fast jeder Algorithmus schnell genug. Spannend wird es erst bei sehr großen Datenmengen (z. B. Millionen von Kundendatensätzen). Hier betrachtet man die sogenannte asymptotische [[Programmausführung|Laufzeit]]. Sie beschreibt das Verhalten des Algorithmus, wenn die Datenmenge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; theoretisch ins Unendliche wächst.&lt;br /&gt;
&lt;br /&gt;
Lässt sich die Laufzeit eines Algorithmus mathematisch durch eine Funktion beschreiben, wie beispielsweise:&lt;br /&gt;
&amp;lt;math&amp;gt;f(x) = \frac{1}{x} + x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So nähert sich die rot dargestellte Funktion &amp;lt;math&amp;gt;\color{red}{f(x)}&amp;lt;/math&amp;gt; für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; der grün dargestellten Asymptote &amp;lt;math&amp;gt;\color{green}{g(x) = x}&amp;lt;/math&amp;gt; an.&lt;br /&gt;
&lt;br /&gt;
Die Laufzeit wird mit der sogenannten Landau-Notation (Groß-O-Notation, z. B. &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;) abgeschätzt. Dabei werden konstante Vorfaktoren (wie &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt;) oder kleinere Summanden ignoriert, da bei riesigen Datenmengen nur noch der dominierende Teil der Funktion das Wachstum bestimmt.&lt;br /&gt;
&lt;br /&gt;
=== Was die Laufzeitanalyse NICHT ist ===&lt;br /&gt;
Sie misst &#039;&#039;&#039;nicht&#039;&#039;&#039; den tatsächlichen Zeitaufwand in Millisekunden auf einem echten Computer. Sie ist ein theoretisches Modell, um Algorithmen hardwareunabhängig miteinander vergleichen zu können.&lt;br /&gt;
&lt;br /&gt;
== Szenarien der Laufzeitabschätzung ==&lt;br /&gt;
Da Algorithmen je nach Vorsortierung der Daten unterschiedlich schnell arbeiten, unterscheidet man drei klassische Fälle:&lt;br /&gt;
&lt;br /&gt;
* Das &#039;&#039;&#039;Worst-Case-Szenario&#039;&#039;&#039; (Schlechtester Fall): Die Daten liegen so ungünstig vor, dass der Algorithmus maximal lange braucht. Dies liefert eine absolute Sicherheitsgarantie (obere Schranke).&lt;br /&gt;
* Das &#039;&#039;&#039;Best-Case-Szenario&#039;&#039;&#039; (Bester Fall): Die Daten liegen optimal vor (z. B. bereits sortiert). Der Algorithmus ist minimal schnell fertig.&lt;br /&gt;
* Das &#039;&#039;&#039;Average-Case-Szenario&#039;&#039;&#039; (Durchschnittlicher Fall): Beschreibt die zu erwartende Laufzeit bei völlig zufällig gemischten Daten. Dies ist oft das realistischste Szenario in der Praxis.&lt;br /&gt;
&lt;br /&gt;
== Mathematische Berechnung des Average-Case ==&lt;br /&gt;
Oft wird fälschlicherweise angenommen, der Average-Case sei einfach „die Hälfte des Worst-Case“. Das ist mathematisch ungenau. Tatsächlich berechnet man den Average-Case mit Hilfe der Stochastik: Er entspricht dem &#039;&#039;&#039;Erwartungswert&#039;&#039;&#039; der Laufzeit.&lt;br /&gt;
&lt;br /&gt;
Man berechnet ihn, indem man alle möglichen Eingaben durchspielt und gewichtet. Die Formel für den Erwartungswert &amp;lt;math&amp;gt;E(T)&amp;lt;/math&amp;gt; lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \sum_{i=1}^{|F|} p_i \cdot t_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei bedeuten die Variablen:&lt;br /&gt;
* &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; ist die Menge aller theoretisch möglichen Eingaben der Größe &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; (z. B. alle möglichen Anordnungen einer Zahlenliste).&lt;br /&gt;
* &amp;lt;math&amp;gt;|F|&amp;lt;/math&amp;gt; (Betragsstriche) steht in der Mathematik für die Mächtigkeit einer Menge. Es ist also die &#039;&#039;&#039;exakte Anzahl&#039;&#039;&#039; aller dieser möglichen Eingaben.&lt;br /&gt;
* &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; ist die Wahrscheinlichkeit, dass genau die spezifische Eingabe &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; auftritt.&lt;br /&gt;
* &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; ist die Anzahl der Rechenschritte, die der Algorithmus für exakt diese Eingabe &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; braucht.&lt;br /&gt;
&lt;br /&gt;
Für die Schule gehen wir meistens von einer &#039;&#039;&#039;Gleichverteilung&#039;&#039;&#039; aus: Jeder Eingabefall tritt mit exakt derselben Wahrscheinlichkeit auf.&lt;br /&gt;
&lt;br /&gt;
=== Average-Case der Linearen Suche ===&lt;br /&gt;
Wir suchen eine bestimmte Zahl in einem Array der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Wir nehmen an, die Zahl ist im Array und jede Position ist gleich wahrscheinlich.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schritt 1: Wahrscheinlichkeit bestimmen.&#039;&#039;&#039; Die Wahrscheinlichkeit, dass unsere Zahl an Position 1, 2 oder &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; steht, ist jeweils &amp;lt;math&amp;gt;p = \frac{1}{n}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 2: Schritte zählen.&#039;&#039;&#039; Steht die Zahl an Position 1, brauchen wir 1 Vergleich. An Position 2 brauchen wir 2 Vergleiche. An Position &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; brauchen wir &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Vergleiche.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 3: Erwartungswert berechnen.&#039;&#039;&#039; Wir setzen dies in die Formel ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n}\cdot 1 + \frac{1}{n}\cdot 2 + \dots + \frac{1}{n}\cdot n = \sum_{i=1}^{n} \left( \frac{1}{n} \cdot i \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir klammern &amp;lt;math&amp;gt;\frac{1}{n}&amp;lt;/math&amp;gt; aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \sum_{i=1}^{n} i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Summe der Zahlen von 1 bis &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; lässt sich mit der &#039;&#039;&#039;Gaußschen Summenformel&#039;&#039;&#039; (kleiner Gauß) vereinfachen zu &amp;lt;math&amp;gt;\frac{n(n+1)}{2}&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \frac{n(n+1)}{2} = \frac{n+1}{2} = \frac{1}{2}n + \frac{1}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fazit:&#039;&#039;&#039; Im Durchschnitt braucht die lineare Suche &amp;lt;math&amp;gt;\frac{n+1}{2}&amp;lt;/math&amp;gt; Vergleiche. Da in der &amp;lt;math&amp;gt;\mathcal{O}&amp;lt;/math&amp;gt;-Notation Konstanten wie &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; wegfallen, gehört die lineare Suche im Durchschnitt zur Klasse &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Laufzeitanalyse von Insertion Sort ==&lt;br /&gt;
Nun wenden wir unser Wissen auf das Sortierverfahren [[Insertion-sort|Insertion Sort]] (Sortieren durch Einfügen) an. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void sortierenEinfuegen_MC() {&lt;br /&gt;
    int v, i, j;&lt;br /&gt;
    for (i = 1; i &amp;lt; zSortfeld.length; i++) {&lt;br /&gt;
        c++; // Zähler für Vergleiche&lt;br /&gt;
        if (zSortfeld[i] &amp;lt; zSortfeld[i - 1]) {&lt;br /&gt;
            v = zSortfeld[i];&lt;br /&gt;
            j = i;&lt;br /&gt;
            do {&lt;br /&gt;
                zSortfeld[j] = zSortfeld[j - 1];&lt;br /&gt;
                j--;&lt;br /&gt;
            } while ((j &amp;gt; 0) &amp;amp;&amp;amp; (zSortfeld[j - 1] &amp;gt; v));&lt;br /&gt;
            zSortfeld[j] = v; &lt;br /&gt;
        }&lt;br /&gt;
        printAnalyse(i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir analysieren eine Liste mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen. Den Zeitverbrauch für einen einzelnen Vergleich nennen wir &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 1. Best-Case-Szenario (Bester Fall) ===&lt;br /&gt;
Die Liste ist bereits perfekt aufsteigend sortiert (z. B. &amp;lt;code&amp;gt;[0, 2, 3, 5, 7]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Da jedes Element schon größer als sein Vorgänger ist, ist die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung immer falsch. Die innere &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife wird ignoriert.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Wir machen exakt einen Vergleich pro Element in der äußeren Schleife. Das ergibt &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt; Vergleiche.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Der Aufwand wächst wie eine Gerade (linear). Die Komplexität ist &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 2. Worst-Case-Szenario (Schlechtester Fall) ===&lt;br /&gt;
Die Liste ist exakt falsch herum sortiert (z. B. &amp;lt;code&amp;gt;[7, 5, 3, 2, 0]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung ist immer wahr. Jedes Element muss durch die innere Schleife komplett bis an den Anfang der Liste getauscht werden.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Beim 1. Durchlauf machen wir 1 Vergleich. Beim 2. Durchlauf 2 Vergleiche. Beim letzten Durchlauf &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Vergleiche. Wir müssen also wieder addieren: &amp;lt;math&amp;gt;1 + 2 + 3 + \dots + (n-1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Wie bei der linearen Suche hilft uns hier die Gaußsche Summenformel:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot \frac{(n - 1) \cdot n}{2} = \frac{c}{2}n^2 - \frac{c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Asymptote an quadratische Funktion2.png|mini|Quadratisches Wachstum]]&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Für extrem große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; dominiert das &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; alles andere. Wir ignorieren den Faktor &amp;lt;math&amp;gt;\frac{c}{2}&amp;lt;/math&amp;gt; und den kleineren Teil &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Das Wachstum ist quadratisch, also &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 3. Average-Case-Szenario (Durchschnittlicher Fall) ===&lt;br /&gt;
=== Average-Case des Insertion Sort ===&lt;br /&gt;
Wir sortieren ein Array der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Wir nehmen an, alle Permutationen (Anordnungen) der Zahlen sind gleich wahrscheinlich. Der Algorithmus fügt nacheinander jedes Element an Index &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; (von Position 2 bis &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) in den bereits sortierten vorderen Teil der Länge &amp;lt;math&amp;gt;i-1&amp;lt;/math&amp;gt; ein.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schritt 1: Wahrscheinlichkeit bestimmen.&#039;&#039;&#039; Beim Einfügen des &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-ten Elements gibt es genau &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; mögliche Zielpositionen (von ganz vorne bis zu seinem aktuellen Platz ganz hinten). Da die ursprüngliche Reihenfolge zufällig ist, ist jede dieser Positionen gleich wahrscheinlich. Die Wahrscheinlichkeit für jede Position ist also &amp;lt;math&amp;gt;p = \frac{1}{i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 2: Schritte zählen.&#039;&#039;&#039; Das Finden der richtigen Position ist wie eine rückwärtsgerichtete lineare Suche. Im besten Fall (das Element ist bereits größer als alle vorherigen und bleibt am Ende) brauchen wir 1 Vergleich. Im Durchschnitt muss das Element mit der Hälfte der bereits sortierten Elemente verglichen werden, um seinen Platz zu finden. Der durchschnittliche Aufwand (Vergleiche) für das Einfügen des &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-ten Elements liegt daher bei etwa &amp;lt;math&amp;gt;\frac{i}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 3: Erwartungswert berechnen.&#039;&#039;&#039; Um die Gesamtlaufzeit zu erhalten, summieren wir den durchschnittlichen Aufwand für alle Elemente, die eingefügt werden müssen (von &amp;lt;math&amp;gt;i=2&amp;lt;/math&amp;gt; bis &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) \approx \sum_{i=2}^{n} \frac{i}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir klammern &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) \approx \frac{1}{2} \cdot \sum_{i=2}^{n} i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Summe der Zahlen von 1 bis &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; lässt sich wieder mit der &#039;&#039;&#039;Gaußschen Summenformel&#039;&#039;&#039; (kleiner Gauß) vereinfachen zu &amp;lt;math&amp;gt;\frac{n(n+1)}{2}&amp;lt;/math&amp;gt;. Da unsere Summe aber erst bei &amp;lt;math&amp;gt;i=2&amp;lt;/math&amp;gt; startet, ziehen wir die 1 (für den fehlenden Schritt &amp;lt;math&amp;gt;i=1&amp;lt;/math&amp;gt;) ab:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) \approx \frac{1}{2} \cdot \left( \frac{n(n+1)}{2} - 1 \right) = \frac{n^2 + n - 2}{4} = \frac{1}{4}n^2 + \frac{1}{4}n - \frac{1}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fazit:&#039;&#039;&#039; Im Durchschnitt braucht der Insertion Sort etwa &amp;lt;math&amp;gt;\frac{1}{4}n^2&amp;lt;/math&amp;gt; Vergleiche. Da in der &amp;lt;math&amp;gt;\mathcal{O}&amp;lt;/math&amp;gt;-Notation Konstanten wie &amp;lt;math&amp;gt;\frac{1}{4}&amp;lt;/math&amp;gt; und Terme niedrigerer Ordnung (wie &amp;lt;math&amp;gt;\frac{1}{4}n - \frac{1}{2}&amp;lt;/math&amp;gt;) wegfallen, gehört der Insertion Sort im Durchschnitt zur Klasse &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2831</id>
		<title>Laufzeitanalyse</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2831"/>
		<updated>2026-04-21T06:32:27Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: /* Mathematische Berechnung des Average-Case */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]]&lt;br /&gt;
Die Laufzeitanalyse untersucht die sogenannte Zeitkomplexität eines [[Algorithmus]]. Man stellt sich dabei im Kern die Frage: &#039;&#039;&#039;„Wie verhält sich die Rechenzeit, wenn die Menge der zu verarbeitenden Daten extrem groß wird?“&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Da die echte Ausführungszeit (in Sekunden) stark vom jeweiligen Computer abhängt, misst man stattdessen die Anzahl der elementaren [[Anweisung|Befehle]] (z. B. Vergleiche oder Tauschoperationen). Diese Anzahl hängt von der Größe der Eingabemenge ab, die in der Informatik meist als &#039;&#039;&#039;Problemgröße &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&#039;&#039;&#039; bezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
Für kleine Datenmengen (z. B. eine Liste mit 10 Elementen sortieren) ist fast jeder Algorithmus schnell genug. Spannend wird es erst bei sehr großen Datenmengen (z. B. Millionen von Kundendatensätzen). Hier betrachtet man die sogenannte asymptotische [[Programmausführung|Laufzeit]]. Sie beschreibt das Verhalten des Algorithmus, wenn die Datenmenge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; theoretisch ins Unendliche wächst.&lt;br /&gt;
&lt;br /&gt;
Lässt sich die Laufzeit eines Algorithmus mathematisch durch eine Funktion beschreiben, wie beispielsweise:&lt;br /&gt;
&amp;lt;math&amp;gt;f(x) = \frac{1}{x} + x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So nähert sich die rot dargestellte Funktion &amp;lt;math&amp;gt;\color{red}{f(x)}&amp;lt;/math&amp;gt; für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; der grün dargestellten Asymptote &amp;lt;math&amp;gt;\color{green}{g(x) = x}&amp;lt;/math&amp;gt; an.&lt;br /&gt;
&lt;br /&gt;
Die Laufzeit wird mit der sogenannten Landau-Notation (Groß-O-Notation, z. B. &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;) abgeschätzt. Dabei werden konstante Vorfaktoren (wie &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt;) oder kleinere Summanden ignoriert, da bei riesigen Datenmengen nur noch der dominierende Teil der Funktion das Wachstum bestimmt.&lt;br /&gt;
&lt;br /&gt;
=== Was die Laufzeitanalyse NICHT ist ===&lt;br /&gt;
Sie misst &#039;&#039;&#039;nicht&#039;&#039;&#039; den tatsächlichen Zeitaufwand in Millisekunden auf einem echten Computer. Sie ist ein theoretisches Modell, um Algorithmen hardwareunabhängig miteinander vergleichen zu können.&lt;br /&gt;
&lt;br /&gt;
== Szenarien der Laufzeitabschätzung ==&lt;br /&gt;
Da Algorithmen je nach Vorsortierung der Daten unterschiedlich schnell arbeiten, unterscheidet man drei klassische Fälle:&lt;br /&gt;
&lt;br /&gt;
* Das &#039;&#039;&#039;Worst-Case-Szenario&#039;&#039;&#039; (Schlechtester Fall): Die Daten liegen so ungünstig vor, dass der Algorithmus maximal lange braucht. Dies liefert eine absolute Sicherheitsgarantie (obere Schranke).&lt;br /&gt;
* Das &#039;&#039;&#039;Best-Case-Szenario&#039;&#039;&#039; (Bester Fall): Die Daten liegen optimal vor (z. B. bereits sortiert). Der Algorithmus ist minimal schnell fertig.&lt;br /&gt;
* Das &#039;&#039;&#039;Average-Case-Szenario&#039;&#039;&#039; (Durchschnittlicher Fall): Beschreibt die zu erwartende Laufzeit bei völlig zufällig gemischten Daten. Dies ist oft das realistischste Szenario in der Praxis.&lt;br /&gt;
&lt;br /&gt;
== Mathematische Berechnung des Average-Case ==&lt;br /&gt;
Oft wird fälschlicherweise angenommen, der Average-Case sei einfach „die Hälfte des Worst-Case“. Das ist mathematisch ungenau. Tatsächlich berechnet man den Average-Case mit Hilfe der Stochastik: Er entspricht dem &#039;&#039;&#039;Erwartungswert&#039;&#039;&#039; der Laufzeit.&lt;br /&gt;
&lt;br /&gt;
Man berechnet ihn, indem man alle möglichen Eingaben durchspielt und gewichtet. Die Formel für den Erwartungswert &amp;lt;math&amp;gt;E(T)&amp;lt;/math&amp;gt; lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \sum_{i=1}^{|F|} p_i \cdot t_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei bedeuten die Variablen:&lt;br /&gt;
* &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; ist die Menge aller theoretisch möglichen Eingaben der Größe &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; (z. B. alle möglichen Anordnungen einer Zahlenliste).&lt;br /&gt;
* &amp;lt;math&amp;gt;|F|&amp;lt;/math&amp;gt; (Betragsstriche) steht in der Mathematik für die Mächtigkeit einer Menge. Es ist also die &#039;&#039;&#039;exakte Anzahl&#039;&#039;&#039; aller dieser möglichen Eingaben.&lt;br /&gt;
* &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; ist die Wahrscheinlichkeit, dass genau die spezifische Eingabe &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; auftritt.&lt;br /&gt;
* &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; ist die Anzahl der Rechenschritte, die der Algorithmus für exakt diese Eingabe &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; braucht.&lt;br /&gt;
&lt;br /&gt;
Für die Schule gehen wir meistens von einer &#039;&#039;&#039;Gleichverteilung&#039;&#039;&#039; aus: Jeder Eingabefall tritt mit exakt derselben Wahrscheinlichkeit auf.&lt;br /&gt;
&lt;br /&gt;
=== Average-Case der Linearen Suche ===&lt;br /&gt;
Wir suchen eine bestimmte Zahl in einem Array der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Wir nehmen an, die Zahl ist im Array und jede Position ist gleich wahrscheinlich.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schritt 1: Wahrscheinlichkeit bestimmen.&#039;&#039;&#039; Die Wahrscheinlichkeit, dass unsere Zahl an Position 1, 2 oder &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; steht, ist jeweils &amp;lt;math&amp;gt;p = \frac{1}{n}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 2: Schritte zählen.&#039;&#039;&#039; Steht die Zahl an Position 1, brauchen wir 1 Vergleich. An Position 2 brauchen wir 2 Vergleiche. An Position &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; brauchen wir &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Vergleiche.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 3: Erwartungswert berechnen.&#039;&#039;&#039; Wir setzen dies in die Formel ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n}\cdot 1 + \frac{1}{n}\cdot 2 + \dots + \frac{1}{n}\cdot n = \sum_{i=1}^{n} \left( \frac{1}{n} \cdot i \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir klammern &amp;lt;math&amp;gt;\frac{1}{n}&amp;lt;/math&amp;gt; aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \sum_{i=1}^{n} i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Summe der Zahlen von 1 bis &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; lässt sich mit der &#039;&#039;&#039;Gaußschen Summenformel&#039;&#039;&#039; (kleiner Gauß) vereinfachen zu &amp;lt;math&amp;gt;\frac{n(n+1)}{2}&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \frac{n(n+1)}{2} = \frac{n+1}{2} = \frac{1}{2}n + \frac{1}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fazit:&#039;&#039;&#039; Im Durchschnitt braucht die lineare Suche &amp;lt;math&amp;gt;\frac{n+1}{2}&amp;lt;/math&amp;gt; Vergleiche. Da in der &amp;lt;math&amp;gt;\mathcal{O}&amp;lt;/math&amp;gt;-Notation Konstanten wie &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; wegfallen, gehört die lineare Suche im Durchschnitt zur Klasse &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Laufzeitanalyse von Insertion Sort ==&lt;br /&gt;
Nun wenden wir unser Wissen auf das Sortierverfahren [[Insertion-sort|Insertion Sort]] (Sortieren durch Einfügen) an. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void sortierenEinfuegen_MC() {&lt;br /&gt;
    int v, i, j;&lt;br /&gt;
    for (i = 1; i &amp;lt; zSortfeld.length; i++) {&lt;br /&gt;
        c++; // Zähler für Vergleiche&lt;br /&gt;
        if (zSortfeld[i] &amp;lt; zSortfeld[i - 1]) {&lt;br /&gt;
            v = zSortfeld[i];&lt;br /&gt;
            j = i;&lt;br /&gt;
            do {&lt;br /&gt;
                zSortfeld[j] = zSortfeld[j - 1];&lt;br /&gt;
                j--;&lt;br /&gt;
            } while ((j &amp;gt; 0) &amp;amp;&amp;amp; (zSortfeld[j - 1] &amp;gt; v));&lt;br /&gt;
            zSortfeld[j] = v; &lt;br /&gt;
        }&lt;br /&gt;
        printAnalyse(i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir analysieren eine Liste mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen. Den Zeitverbrauch für einen einzelnen Vergleich nennen wir &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 1. Best-Case-Szenario (Bester Fall) ===&lt;br /&gt;
Die Liste ist bereits perfekt aufsteigend sortiert (z. B. &amp;lt;code&amp;gt;[0, 2, 3, 5, 7]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Da jedes Element schon größer als sein Vorgänger ist, ist die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung immer falsch. Die innere &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife wird ignoriert.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Wir machen exakt einen Vergleich pro Element in der äußeren Schleife. Das ergibt &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt; Vergleiche.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Der Aufwand wächst wie eine Gerade (linear). Die Komplexität ist &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 2. Worst-Case-Szenario (Schlechtester Fall) ===&lt;br /&gt;
Die Liste ist exakt falsch herum sortiert (z. B. &amp;lt;code&amp;gt;[7, 5, 3, 2, 0]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung ist immer wahr. Jedes Element muss durch die innere Schleife komplett bis an den Anfang der Liste getauscht werden.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Beim 1. Durchlauf machen wir 1 Vergleich. Beim 2. Durchlauf 2 Vergleiche. Beim letzten Durchlauf &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Vergleiche. Wir müssen also wieder addieren: &amp;lt;math&amp;gt;1 + 2 + 3 + \dots + (n-1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Wie bei der linearen Suche hilft uns hier die Gaußsche Summenformel:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot \frac{(n - 1) \cdot n}{2} = \frac{c}{2}n^2 - \frac{c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Asymptote an quadratische Funktion2.png|mini|Quadratisches Wachstum]]&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Für extrem große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; dominiert das &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; alles andere. Wir ignorieren den Faktor &amp;lt;math&amp;gt;\frac{c}{2}&amp;lt;/math&amp;gt; und den kleineren Teil &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Das Wachstum ist quadratisch, also &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 3. Average-Case-Szenario (Durchschnittlicher Fall) ===&lt;br /&gt;
Die Werte im Array sind völlig zufällig verteilt.&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Im Durchschnitt müssen wir ein Element nicht bis ganz an den Anfang schieben (wie im Worst-Case), sondern nur bis zur &#039;&#039;&#039;Hälfte&#039;&#039;&#039; des bisher sortierten Bereichs.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Die Anzahl der Vergleiche halbiert sich grob im Vergleich zum Worst-Case. Der Aufwand liegt bei ca. &amp;lt;math&amp;gt;\frac{1}{2} \cdot \frac{c}{2}n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Wie wir gelernt haben, ignoriert die Groß-O-Notation konstante Brüche (wie &amp;lt;math&amp;gt;\frac{1}{4}&amp;lt;/math&amp;gt;). Es bleibt bei der höchsten Potenz: Das Wachstum ist und bleibt quadratisch. Die Komplexität lautet also weiterhin &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Quicksort&amp;diff=2830</id>
		<title>Quicksort</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Quicksort&amp;diff=2830"/>
		<updated>2026-04-20T10:26:21Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Rekursive Baumstruktur.jpg|mini|Rekursive Aufteilung beim Quicksort]]&lt;br /&gt;
Ein in der Praxis oft eingesetzter und sehr effizienter [[Sortieren|Sortieralgorithmus]] ist der &#039;&#039;&#039;Quicksort&#039;&#039;&#039; (englisch für „schnelles Sortieren“). Er funktioniert nach dem Prinzip &#039;&#039;&#039;„Teile und herrsche“&#039;&#039;&#039; (Divide and Conquer), welches in der Programmierung meist mithilfe von [[Rekursion]] realisiert wird. &lt;br /&gt;
&lt;br /&gt;
Die Grundidee ist einfach: Die zu sortierende Datenmenge wird in zwei kleinere Teillisten zerlegt (&amp;quot;Teile&amp;quot;) und diese werden anschließend separat sortiert (&amp;quot;Herrsche&amp;quot;). Diese Teillisten werden wiederum in noch kleinere Teillisten zerlegt, bis eine Liste nur noch aus einem einzigen Element besteht. Eine Liste mit nur einem Element ist naturgemäß immer sortiert – an diesem Punkt endet die [[Rekursion]] [http://openbook.galileocomputing.de/c_von_a_bis_z/022_c_algorithmen_003.htm (vgl. Galileo Computing)].&lt;br /&gt;
&lt;br /&gt;
=== Wie funktioniert die Zerlegung (Partitionierung)? ===&lt;br /&gt;
Die Zerlegung der Liste erfolgt anhand eines sogenannten &#039;&#039;&#039;Pivot-Elements&#039;&#039;&#039; (Dreh- und Angelpunkt). &lt;br /&gt;
# Ein beliebiges Element der Liste wird als Pivot ausgewählt.&lt;br /&gt;
# Alle anderen Elemente der Liste werden mit diesem Pivot verglichen.&lt;br /&gt;
# Die Liste wird in zwei Bereiche unterteilt:&lt;br /&gt;
#* &#039;&#039;&#039;Linke Teilliste:&#039;&#039;&#039; Alle Elemente, die kleiner (oder gleich) dem Pivot sind.&lt;br /&gt;
#* &#039;&#039;&#039;Rechte Teilliste:&#039;&#039;&#039; Alle Elemente, die größer als das Pivot sind.&lt;br /&gt;
&lt;br /&gt;
Nach diesem Schritt steht das Pivot-Element bereits an seiner endgültigen, korrekten Position in der Liste. Die Elemente in der linken und rechten Teilliste sind zwar untereinander noch unsortiert, aber es ist garantiert, dass alle Elemente links vom Pivot kleiner sind als alle Elemente rechts davon.&lt;br /&gt;
&lt;br /&gt;
Anschließend ruft sich der Quicksort-Algorithmus selbst auf (Rekursion), um die linke und die rechte Teilliste nach exakt demselben Muster weiter zu sortieren. Ein großer Vorteil dieses Verfahrens: Die Teillisten sind immer Abschnitte der ursprünglichen Liste. Es wird also (abgesehen vom Speicher für die Rekursionsaufrufe) kein zusätzlicher Speicherplatz für neue Listen benötigt (In-Place-Sortierung).&lt;br /&gt;
&lt;br /&gt;
== Beispiel ==&lt;br /&gt;
Als Beispiel betrachten wir eine unsortierte Liste mit Zahlen. Der Einfachheit halber wählen wir in diesem Beispiel immer das &#039;&#039;&#039;erste Element&#039;&#039;&#039; als Pivot-Element. Zu Beginn ist die Zahl 7 also unser Pivot.&lt;br /&gt;
&lt;br /&gt;
7 &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;10 8&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;11&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;9&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;3 1 5 2&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Zahlen, die kleiner als das Pivot-Element 7 sind, wurden blau markiert. Die Zahlen, die größer als 7 sind, wurden rot markiert. Nach dem ersten Partitionierungsschritt hat QuickSort die Liste folgendermaßen umgeordnet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;3 2 5 6 1 4&amp;lt;/span&amp;gt; &#039;&#039;&#039;7&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun stehen alle Zahlen, die kleiner sind als 7, links von ihr. Alle Zahlen, die größer sind, stehen rechts. Die Reihenfolge der Zahlen innerhalb der blauen und roten Teillisten ist hier noch willkürlich und hängt von der genauen Programmierung ab. Entscheidend ist: &#039;&#039;&#039;Die 7 hat nun ihre endgültige, korrekte Position gefunden.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Nächstes wird die linke (blaue) Teilliste per QuickSort sortiert. Als neues Pivot wählen wir wieder die erste Zahl des Bereichs, die 3. Die Zahlen, die kleiner sind als 3, werden wieder blau markiert, die größeren rot. Die bereits einsortierte 7 und die rechte Teilliste ignorieren wir für den Moment (grau hinterlegt):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;5 6&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:gray&amp;quot;&amp;gt;7 9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Algorithmus sortiert die Elemente relativ zur 3 um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;1 2&amp;lt;/span&amp;gt; &#039;&#039;&#039;3&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;6 5 4&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:gray&amp;quot;&amp;gt;7 9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Prozess wird fortgesetzt (erst die 1 und 2, dann die 6, 5 und 4), bis der gesamte linke Teil des Arrays vollständig sortiert ist. Das Ergebnis sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;1 2 3 4 5 6&#039;&#039; &#039;&#039;&#039;7&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:gray&amp;quot;&amp;gt;9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird die Teilliste rechts von der 7 nach demselben Prinzip sortiert. Wieder wird ein Pivot gewählt (hier die 9), und die kleineren und größeren Zahlen werden auf die jeweils richtige Seite gebracht, bis das gesamte Array sortiert ist.&lt;br /&gt;
&lt;br /&gt;
== Pseudocode ==&lt;br /&gt;
Der folgende Pseudocode illustriert die Arbeitsweise des [[Algorithmus]]. Bei jedem Aufruf von &amp;lt;code&amp;gt;quicksort(...)&amp;lt;/code&amp;gt; gibt &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt; den Index des ersten Elements in der Teilliste an und &amp;lt;code&amp;gt;rechts&amp;lt;/code&amp;gt; den des letzten. Beim ersten Aufruf (oberste Rekursionsebene) ist &amp;lt;code&amp;gt;links = 0&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;rechts = n-1&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
funktion quicksort(links, rechts) &lt;br /&gt;
    falls links &amp;lt; rechts dann &lt;br /&gt;
        // teile() ordnet das Array um und liefert die finale Position des Pivots&lt;br /&gt;
        teiler := teile(links, rechts) &lt;br /&gt;
        &lt;br /&gt;
        // Rekursiver Aufruf für die linke Teilliste&lt;br /&gt;
        quicksort(links, teiler - 1) &lt;br /&gt;
        &lt;br /&gt;
        // Rekursiver Aufruf für die rechte Teilliste&lt;br /&gt;
        quicksort(teiler + 1, rechts) &lt;br /&gt;
    ende&lt;br /&gt;
ende&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wahl des Pivot-Elements ==&lt;br /&gt;
Die Effizienz von Quicksort steht und fällt mit der Wahl des Pivot-Elements. &lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schlechteste Wahl:&#039;&#039;&#039; Das Pivot-Element ist zufällig immer das kleinste oder größte Element der Teilliste. Dann wird die Liste extrem ungleichmäßig geteilt (eine Liste mit 0 Elementen, eine mit &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Elementen).&lt;br /&gt;
* &#039;&#039;&#039;Beste Wahl:&#039;&#039;&#039; Das Pivot-Element ist genau der [[Median]] (der Wert, der exakt in der Mitte der sortierten Liste stehen würde). Die Liste wird in zwei exakt gleich große Hälften geteilt. Da der Median in unsortierten Daten aber aufwendig zu suchen ist, behilft man sich mit Tricks.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Warum das erste Element oft eine schlechte Wahl ist:&#039;&#039;&#039;&lt;br /&gt;
Wenn man immer stur das erste Element als Pivot wählt (wie in unserem Beispiel) und die Eingabeliste ist bereits (oder fast) fertig sortiert, trifft automatisch immer der oben beschriebene schlechteste Fall ein. Der Algorithmus wird dadurch extrem langsam.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Lösungsansätze für die Praxis:&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;Randomisierung:&#039;&#039;&#039; Man wählt ein zufälliges Element aus dem Intervall als Pivot.&lt;br /&gt;
# &#039;&#039;&#039;Median-of-Three:&#039;&#039;&#039; Man betrachtet das erste, das mittlere und das letzte Element der Teilliste und wählt von diesen dreien den mittleren Wert als Pivot. Dies schützt sehr gut vor bereits vorsortierten Listen.&lt;br /&gt;
&lt;br /&gt;
== Java Implementierung ==&lt;br /&gt;
Bisher wurde die allgemeine Funktionsweise beschrieben. Am besten lassen sich komplexe [[Algorithmus|Algorithmen]] nachvollziehen, wenn man sie anhand einer Implementierung mit Hilfe eines [[Debugger|Debuggers]] analysiert. In der folgenden [[Java]]-Implementierung wird ein [[Array]] aus Ganzzahlen (&amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;) sortiert. &lt;br /&gt;
&lt;br /&gt;
Zur Abwechslung wird in dieser Implementierung immer das &#039;&#039;&#039;letzte&#039;&#039;&#039; Element des aktuellen Bereichs (&amp;lt;code&amp;gt;array[rechts]&amp;lt;/code&amp;gt;) als Pivot gewählt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void quickSort(int array[], int links, int rechts) {&lt;br /&gt;
      if (links &amp;lt; rechts) {&lt;br /&gt;
         // Teile das Array und finde die Position des Pivot-Elements&lt;br /&gt;
         int i = teile(array, links, rechts);&lt;br /&gt;
         &lt;br /&gt;
         // Sortiere den linken Teil&lt;br /&gt;
         quickSort(array, links, i - 1);&lt;br /&gt;
         // Sortiere den rechten Teil&lt;br /&gt;
         quickSort(array, i + 1, rechts);&lt;br /&gt;
      }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public int teile(int array[], int links, int rechts) {&lt;br /&gt;
      int pivot, i, j, help;&lt;br /&gt;
      pivot = array[rechts]; // Wir wählen das rechte Randelement als Pivot               &lt;br /&gt;
      i = links;&lt;br /&gt;
      j = rechts - 1;&lt;br /&gt;
      &lt;br /&gt;
      // Von links und rechts zur Mitte laufen, bis sich i und j kreuzen&lt;br /&gt;
      while (i &amp;lt;= j) {&lt;br /&gt;
         // Suche von links ein Element, das größer als das Pivot ist&lt;br /&gt;
         if (array[i] &amp;gt; pivot) {     &lt;br /&gt;
            // Tausche array[i] mit array[j], um es in den rechten Bereich zu bringen&lt;br /&gt;
            help = array[i]; &lt;br /&gt;
            array[i] = array[j]; &lt;br /&gt;
            array[j] = help;                             &lt;br /&gt;
            j--; // j einen Schritt weiter nach links schieben&lt;br /&gt;
         } else {&lt;br /&gt;
            i++; // i war kleiner/gleich Pivot, ist also korrekt positioniert -&amp;gt; weiter nach rechts&lt;br /&gt;
         }          &lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      // Das Pivot-Element an seine endgültige Position (i) tauschen&lt;br /&gt;
      help = array[i];&lt;br /&gt;
      array[i] = array[rechts];&lt;br /&gt;
      array[rechts] = help;&lt;br /&gt;
      &lt;br /&gt;
      // Rückgabe der finalen Position des Pivot-Elements&lt;br /&gt;
      return i;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vorbereitung: Indikatorvariablen und die Harmonische Reihe ==&lt;br /&gt;
Bei komplexeren Algorithmen wie [[Quicksort]] reicht einfaches Zählen der Vergleiche oft nicht mehr aus. Hier arbeiten wir in der Laufzeitanalyse mit einem stochastischen Werkzeug: &#039;&#039;&#039;Indikatorvariablen&#039;&#039;&#039; und der &#039;&#039;&#039;Harmonischen Reihe&#039;&#039;&#039;. Um das Prinzip zu verinnerlichen, analysieren wir vorab einen simplen Code zur Maximumsuche:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public int findMax(int[] A) {&lt;br /&gt;
    int max = A[0];&lt;br /&gt;
    for (int i = 1; i &amp;lt; A.length; i++) {&lt;br /&gt;
        if (A[i] &amp;gt; max) {&lt;br /&gt;
            max = A[i]; // Update-Operation&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return max;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Vergleiche in der &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung werden immer exakt &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Mal ausgeführt. Aber wie oft wird die Variable &amp;lt;code&amp;gt;max&amp;lt;/code&amp;gt; im Durchschnitt tatsächlich überschrieben (Update-Operation)?&lt;br /&gt;
&lt;br /&gt;
Wir stellen uns vor, das Array enthält völlig zufällig gemischte Zahlen.&lt;br /&gt;
* &#039;&#039;&#039;Intuitiver Ansatz:&#039;&#039;&#039; &lt;br /&gt;
Das 1. Element ist automatisch das bisherige Maximum (Wahrscheinlichkeit: 1). &lt;br /&gt;
Damit das 2. Element ein neues Maximum ist, muss es größer als das erste sein. Die Chance dafür ist bei zufälliger Verteilung exakt 50% (&amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt;). &lt;br /&gt;
Damit das 3. Element ein neues Maximum ist, muss es das Größte der ersten drei Elemente sein. Die Chance dafür ist &amp;lt;math&amp;gt;\frac{1}{3}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Mathematischer Ansatz (Indikatorvariablen):&#039;&#039;&#039;&lt;br /&gt;
Wir definieren einen „Schalter“ (die Indikatorvariable) &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt;. Er ist &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;, wenn ein neues Maximum an Stelle &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; gefunden wird, sonst &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Die Wahrscheinlichkeit dafür ist &amp;lt;math&amp;gt;P(X_i = 1) = \frac{1}{i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Um die durchschnittliche Gesamtzahl der Updates zu finden, addieren wir einfach diese Wahrscheinlichkeiten auf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E[X] = \sum_{i=1}^{n} \frac{1}{i} = 1 + \frac{1}{2} + \frac{1}{3} + \frac{1}{4} + \dots + \frac{1}{n}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese spezielle Summe nennt man in der Mathematik die &#039;&#039;&#039;Harmonische Reihe&#039;&#039;&#039; (&amp;lt;math&amp;gt;H_n&amp;lt;/math&amp;gt;). Sie wächst extrem langsam. Für große Zahlen &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; entspricht sie in etwa dem &#039;&#039;&#039;natürlichen Logarithmus&#039;&#039;&#039; (&amp;lt;math&amp;gt;\ln(n)&amp;lt;/math&amp;gt;). &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Erkenntnis für Quicksort:&#039;&#039;&#039; Obwohl das Array &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elemente hat, wird die Update-Operation im Durchschnitt nur logarithmisch oft (&amp;lt;math&amp;gt;\mathcal{O}(\log n)&amp;lt;/math&amp;gt;) ausgeführt. Genau dieses stochastische Prinzip (das Aufsummieren von Einzelwahrscheinlichkeiten, die zu einem Logarithmus führen) ist der Schlüssel, um im nächsten Abschnitt zu beweisen, dass Quicksort im Average-Case bei &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt; liegt.&lt;br /&gt;
&lt;br /&gt;
== Laufzeitanalyse ==&lt;br /&gt;
&lt;br /&gt;
=== Worst-Case (Schlechtester Fall) ===&lt;br /&gt;
[[Datei:Baum N-Ebenen.png|mini|Worst-Case: Entarteter Baum]]&lt;br /&gt;
Betrachten wir zunächst die Worst-Case-[[Laufzeitanalyse]]. Angenommen, wir haben extremes Pech und die Partitionierung ist maximal unausgeglichen. Das gewählte Pivot ist immer das absolut kleinste oder größte Element. Dann enthält eine der Partitionen 0 Elemente und die andere Partition &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Elemente. &lt;br /&gt;
&lt;br /&gt;
Der Aufwand für das Vergleichen (Partitionieren) einer Ebene mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen ist proportional zu &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Nennen wir diesen Aufwand &amp;lt;math&amp;gt;c \cdot n&amp;lt;/math&amp;gt; (wobei &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; eine Konstante für die Dauer eines Vergleichs ist).&lt;br /&gt;
Auf der nächsten Ebene müssen wir &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Elemente vergleichen, der Aufwand ist &amp;lt;math&amp;gt;c \cdot (n-1)&amp;lt;/math&amp;gt;. Danach &amp;lt;math&amp;gt;c \cdot (n-2)&amp;lt;/math&amp;gt; und so weiter.&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Partitionierungszeiten für alle Ebenen aufsummieren, erhalten wir folgende Reihe:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot n + c \cdot (n - 1) + c \cdot (n - 2) + \dots + c \cdot 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir klammern &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; aus und erkennen die bekannte [[Arithmetische-reihe|Gaußsche Summenformel]]:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot (n + (n-1) + (n-2) + \dots + 2) \approx c \cdot \frac{n(n+1)}{2} = \frac{c}{2}n^2 + \frac{c}{2}n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Da in der asymptotischen Laufzeitanalyse konstante Faktoren und kleinere Terme (wie &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) ignoriert werden, wächst der Aufwand quadratisch. &lt;br /&gt;
&#039;&#039;&#039;Fazit Worst-Case:&#039;&#039;&#039; Quicksort hat im schlechtesten Fall eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Best-Case (Bester Fall) ===&lt;br /&gt;
[[Datei:Verzweigungsbaum Quicksort symetrisch.png|mini|Best-Case: Symmetrischer Baum]]&lt;br /&gt;
Der beste Fall liegt vor, wenn das Pivot-Element die Liste immer exakt in der Mitte teilt. Die Teillisten sind dann immer gleich groß.&lt;br /&gt;
[[Datei:SubArrays symetrisch Quicksort.png|mini|Halbierung der Arraygrößen]]&lt;br /&gt;
&lt;br /&gt;
Wird die Anzahl der zu sortierenden Elemente &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; verdoppelt, kommt durch die fortlaufende Halbierung lediglich &#039;&#039;&#039;eine einzige neue Rekursionsebene&#039;&#039;&#039; (ein Baum-Level) hinzu. Dieses Wachstumsverhalten (wie oft kann ich &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; durch 2 teilen, bis 1 übrig bleibt?) wird mathematisch durch den Logarithmus zur Basis 2 ausgedrückt: &amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Zu sortierende Elemente &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 4&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 8&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 16&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 32&lt;br /&gt;
|-&lt;br /&gt;
! Als Zweierpotenz&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^3}&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^4}&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^5}&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Anzahl Rekursionsebenen &amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 2&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 3&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 4&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wir haben also insgesamt &amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt; Ebenen. Auf &#039;&#039;&#039;jeder einzelnen Ebene&#039;&#039;&#039; müssen in der Summe &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elemente betrachtet und mit dem jeweiligen Pivot verglichen werden. &lt;br /&gt;
Wir multiplizieren also die Arbeit pro Ebene (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) mit der Anzahl der Ebenen (&amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&#039;&#039;&#039;Fazit Best-Case:&#039;&#039;&#039; Die Laufzeit beträgt &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Average-Case (Durchschnittlicher Fall) ===&lt;br /&gt;
In der Praxis haben wir selten den perfekten Best-Case, aber fast nie den absoluten Worst-Case. Meistens teilt das Pivot die Liste in ungleiche, aber moderate Verhältnisse (z. B. 30:70 oder 60:40). &lt;br /&gt;
&lt;br /&gt;
Um den Average-Case mathematisch exakt zu beweisen, greifen wir auf unser Vorwissen (Indikatorvariablen und Harmonische Reihe) zurück. Wir fragen uns: &#039;&#039;Wie hoch ist die Wahrscheinlichkeit, dass zwei beliebige Elemente (nennen wir sie &amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt;, wobei &amp;lt;math&amp;gt;z_i &amp;lt; z_j&amp;lt;/math&amp;gt;) im Laufe des Algorithmus direkt miteinander verglichen werden?&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Zwei Elemente werden genau dann miteinander verglichen, wenn eines der beiden als Pivot-Element ausgewählt wird, &#039;&#039;&#039;bevor&#039;&#039;&#039; irgendein anderes Element, dessen Wert zwischen &amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt; liegt, als Pivot gewählt wird.&lt;br /&gt;
* Zwischen &amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt; (inklusive der beiden Randelemente) liegen exakt &amp;lt;math&amp;gt;k = j - i + 1&amp;lt;/math&amp;gt; Elemente. &lt;br /&gt;
* Sind die Daten völlig zufällig verteilt, hat jedes dieser &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; Elemente die gleiche Chance, zuerst als Pivot gewählt zu werden. Damit &amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt; verglichen werden, muss exakt &amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; oder &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt; der &amp;quot;Gewinner&amp;quot; dieser Ziehung sein. Da es &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; günstige Fälle (&amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; oder &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt;) bei &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; möglichen Fällen gibt, beträgt die Wahrscheinlichkeit exakt &amp;lt;math&amp;gt;\frac{2}{k}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Summieren wir diese Wahrscheinlichkeiten (den Erwartungswert) für alle möglichen Elementpaare im gesamten Array auf, erhalten wir folgende Doppel-Summe:&lt;br /&gt;
&amp;lt;math&amp;gt;E(X) = \sum_{i=1}^{n-1} \sum_{j=i+1}^{n} \frac{2}{j - i + 1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ersetzt man den Abstand &amp;lt;math&amp;gt;(j - i + 1)&amp;lt;/math&amp;gt; formal durch die Laufvariable &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, löst sich die innere Summe auf zu:&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_{k=2}^{n} \frac{2}{k} = 2 \cdot \left( \frac{1}{2} + \frac{1}{3} + \dots + \frac{1}{n} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier erkennen wir exakt die &#039;&#039;&#039;Harmonische Reihe&#039;&#039;&#039; (&amp;lt;math&amp;gt;H_n&amp;lt;/math&amp;gt;) wieder! Da die Harmonische Reihe für große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; gegen den natürlichen Logarithmus &amp;lt;math&amp;gt;\ln(n)&amp;lt;/math&amp;gt; konvergiert, ergibt die innere Summe ungefähr &amp;lt;math&amp;gt;2 \cdot \ln(n)&amp;lt;/math&amp;gt;. Da die äußere Summe (stark vereinfacht) &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-mal läuft, erhalten wir insgesamt rund &amp;lt;math&amp;gt;2n \cdot \ln(n)&amp;lt;/math&amp;gt; Vergleiche. &lt;br /&gt;
&lt;br /&gt;
Da konstante Vorfaktoren wie die &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; beim Groß-O ignoriert werden, ist nun stochastisch bewiesen: &lt;br /&gt;
&#039;&#039;&#039;Fazit Average-Case:&#039;&#039;&#039; Auch bei zufälliger Verteilung konvergiert Quicksort dank des logarithmischen Wachstums der harmonischen Reihe sicher gegen die Komplexitätsklasse &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;br /&gt;
[[Kategorie:FI_I_TP2]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2829</id>
		<title>Laufzeitanalyse</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2829"/>
		<updated>2026-04-20T10:26:00Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]]&lt;br /&gt;
Die Laufzeitanalyse untersucht die sogenannte Zeitkomplexität eines [[Algorithmus]]. Man stellt sich dabei im Kern die Frage: &#039;&#039;&#039;„Wie verhält sich die Rechenzeit, wenn die Menge der zu verarbeitenden Daten extrem groß wird?“&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Da die echte Ausführungszeit (in Sekunden) stark vom jeweiligen Computer abhängt, misst man stattdessen die Anzahl der elementaren [[Anweisung|Befehle]] (z. B. Vergleiche oder Tauschoperationen). Diese Anzahl hängt von der Größe der Eingabemenge ab, die in der Informatik meist als &#039;&#039;&#039;Problemgröße &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&#039;&#039;&#039; bezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
Für kleine Datenmengen (z. B. eine Liste mit 10 Elementen sortieren) ist fast jeder Algorithmus schnell genug. Spannend wird es erst bei sehr großen Datenmengen (z. B. Millionen von Kundendatensätzen). Hier betrachtet man die sogenannte asymptotische [[Programmausführung|Laufzeit]]. Sie beschreibt das Verhalten des Algorithmus, wenn die Datenmenge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; theoretisch ins Unendliche wächst.&lt;br /&gt;
&lt;br /&gt;
Lässt sich die Laufzeit eines Algorithmus mathematisch durch eine Funktion beschreiben, wie beispielsweise:&lt;br /&gt;
&amp;lt;math&amp;gt;f(x) = \frac{1}{x} + x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So nähert sich die rot dargestellte Funktion &amp;lt;math&amp;gt;\color{red}{f(x)}&amp;lt;/math&amp;gt; für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; der grün dargestellten Asymptote &amp;lt;math&amp;gt;\color{green}{g(x) = x}&amp;lt;/math&amp;gt; an.&lt;br /&gt;
&lt;br /&gt;
Die Laufzeit wird mit der sogenannten Landau-Notation (Groß-O-Notation, z. B. &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;) abgeschätzt. Dabei werden konstante Vorfaktoren (wie &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt;) oder kleinere Summanden ignoriert, da bei riesigen Datenmengen nur noch der dominierende Teil der Funktion das Wachstum bestimmt.&lt;br /&gt;
&lt;br /&gt;
=== Was die Laufzeitanalyse NICHT ist ===&lt;br /&gt;
Sie misst &#039;&#039;&#039;nicht&#039;&#039;&#039; den tatsächlichen Zeitaufwand in Millisekunden auf einem echten Computer. Sie ist ein theoretisches Modell, um Algorithmen hardwareunabhängig miteinander vergleichen zu können.&lt;br /&gt;
&lt;br /&gt;
== Szenarien der Laufzeitabschätzung ==&lt;br /&gt;
Da Algorithmen je nach Vorsortierung der Daten unterschiedlich schnell arbeiten, unterscheidet man drei klassische Fälle:&lt;br /&gt;
&lt;br /&gt;
* Das &#039;&#039;&#039;Worst-Case-Szenario&#039;&#039;&#039; (Schlechtester Fall): Die Daten liegen so ungünstig vor, dass der Algorithmus maximal lange braucht. Dies liefert eine absolute Sicherheitsgarantie (obere Schranke).&lt;br /&gt;
* Das &#039;&#039;&#039;Best-Case-Szenario&#039;&#039;&#039; (Bester Fall): Die Daten liegen optimal vor (z. B. bereits sortiert). Der Algorithmus ist minimal schnell fertig.&lt;br /&gt;
* Das &#039;&#039;&#039;Average-Case-Szenario&#039;&#039;&#039; (Durchschnittlicher Fall): Beschreibt die zu erwartende Laufzeit bei völlig zufällig gemischten Daten. Dies ist oft das realistischste Szenario in der Praxis.&lt;br /&gt;
&lt;br /&gt;
== Mathematische Berechnung des Average-Case ==&lt;br /&gt;
Oft wird fälschlicherweise angenommen, der Average-Case sei einfach „die Hälfte des Worst-Case“. Das ist mathematisch ungenau. Tatsächlich berechnet man den Average-Case mit Hilfe der Stochastik: Er entspricht dem &#039;&#039;&#039;Erwartungswert&#039;&#039;&#039; der Laufzeit.&lt;br /&gt;
&lt;br /&gt;
Man berechnet ihn, indem man alle möglichen Eingaben durchspielt und gewichtet. Die Formel für den Erwartungswert &amp;lt;math&amp;gt;E(T)&amp;lt;/math&amp;gt; lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \sum_{i=1}^{|E|} p_i \cdot t_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei bedeuten die Variablen:&lt;br /&gt;
* &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; ist die Menge aller theoretisch möglichen Eingaben der Größe &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; (z. B. alle möglichen Anordnungen einer Zahlenliste).&lt;br /&gt;
* &amp;lt;math&amp;gt;|E|&amp;lt;/math&amp;gt; (Betragsstriche) steht in der Mathematik für die Mächtigkeit einer Menge. Es ist also die &#039;&#039;&#039;exakte Anzahl&#039;&#039;&#039; aller dieser möglichen Eingaben.&lt;br /&gt;
* &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; ist die Wahrscheinlichkeit, dass genau die spezifische Eingabe &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; auftritt.&lt;br /&gt;
* &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; ist die Anzahl der Rechenschritte, die der Algorithmus für exakt diese Eingabe &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; braucht.&lt;br /&gt;
&lt;br /&gt;
Für die Schule gehen wir meistens von einer &#039;&#039;&#039;Gleichverteilung&#039;&#039;&#039; aus: Jeder Eingabefall tritt mit exakt derselben Wahrscheinlichkeit auf.&lt;br /&gt;
&lt;br /&gt;
=== Average-Case der Linearen Suche ===&lt;br /&gt;
Wir suchen eine bestimmte Zahl in einem Array der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Wir nehmen an, die Zahl ist im Array und jede Position ist gleich wahrscheinlich.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schritt 1: Wahrscheinlichkeit bestimmen.&#039;&#039;&#039; Die Wahrscheinlichkeit, dass unsere Zahl an Position 1, 2 oder &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; steht, ist jeweils &amp;lt;math&amp;gt;p = \frac{1}{n}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 2: Schritte zählen.&#039;&#039;&#039; Steht die Zahl an Position 1, brauchen wir 1 Vergleich. An Position 2 brauchen wir 2 Vergleiche. An Position &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; brauchen wir &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Vergleiche.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 3: Erwartungswert berechnen.&#039;&#039;&#039; Wir setzen dies in die Formel ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n}\cdot 1 + \frac{1}{n}\cdot 2 + \dots + \frac{1}{n}\cdot n = \sum_{i=1}^{n} \left( \frac{1}{n} \cdot i \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir klammern &amp;lt;math&amp;gt;\frac{1}{n}&amp;lt;/math&amp;gt; aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \sum_{i=1}^{n} i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Summe der Zahlen von 1 bis &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; lässt sich mit der &#039;&#039;&#039;Gaußschen Summenformel&#039;&#039;&#039; (kleiner Gauß) vereinfachen zu &amp;lt;math&amp;gt;\frac{n(n+1)}{2}&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \frac{n(n+1)}{2} = \frac{n+1}{2} = \frac{1}{2}n + \frac{1}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fazit:&#039;&#039;&#039; Im Durchschnitt braucht die lineare Suche &amp;lt;math&amp;gt;\frac{n+1}{2}&amp;lt;/math&amp;gt; Vergleiche. Da in der &amp;lt;math&amp;gt;\mathcal{O}&amp;lt;/math&amp;gt;-Notation Konstanten wie &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; wegfallen, gehört die lineare Suche im Durchschnitt zur Klasse &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Laufzeitanalyse von Insertion Sort ==&lt;br /&gt;
Nun wenden wir unser Wissen auf das Sortierverfahren [[Insertion-sort|Insertion Sort]] (Sortieren durch Einfügen) an. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void sortierenEinfuegen_MC() {&lt;br /&gt;
    int v, i, j;&lt;br /&gt;
    for (i = 1; i &amp;lt; zSortfeld.length; i++) {&lt;br /&gt;
        c++; // Zähler für Vergleiche&lt;br /&gt;
        if (zSortfeld[i] &amp;lt; zSortfeld[i - 1]) {&lt;br /&gt;
            v = zSortfeld[i];&lt;br /&gt;
            j = i;&lt;br /&gt;
            do {&lt;br /&gt;
                zSortfeld[j] = zSortfeld[j - 1];&lt;br /&gt;
                j--;&lt;br /&gt;
            } while ((j &amp;gt; 0) &amp;amp;&amp;amp; (zSortfeld[j - 1] &amp;gt; v));&lt;br /&gt;
            zSortfeld[j] = v; &lt;br /&gt;
        }&lt;br /&gt;
        printAnalyse(i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir analysieren eine Liste mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen. Den Zeitverbrauch für einen einzelnen Vergleich nennen wir &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 1. Best-Case-Szenario (Bester Fall) ===&lt;br /&gt;
Die Liste ist bereits perfekt aufsteigend sortiert (z. B. &amp;lt;code&amp;gt;[0, 2, 3, 5, 7]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Da jedes Element schon größer als sein Vorgänger ist, ist die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung immer falsch. Die innere &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife wird ignoriert.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Wir machen exakt einen Vergleich pro Element in der äußeren Schleife. Das ergibt &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt; Vergleiche.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Der Aufwand wächst wie eine Gerade (linear). Die Komplexität ist &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 2. Worst-Case-Szenario (Schlechtester Fall) ===&lt;br /&gt;
Die Liste ist exakt falsch herum sortiert (z. B. &amp;lt;code&amp;gt;[7, 5, 3, 2, 0]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung ist immer wahr. Jedes Element muss durch die innere Schleife komplett bis an den Anfang der Liste getauscht werden.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Beim 1. Durchlauf machen wir 1 Vergleich. Beim 2. Durchlauf 2 Vergleiche. Beim letzten Durchlauf &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Vergleiche. Wir müssen also wieder addieren: &amp;lt;math&amp;gt;1 + 2 + 3 + \dots + (n-1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Wie bei der linearen Suche hilft uns hier die Gaußsche Summenformel:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot \frac{(n - 1) \cdot n}{2} = \frac{c}{2}n^2 - \frac{c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Asymptote an quadratische Funktion2.png|mini|Quadratisches Wachstum]]&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Für extrem große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; dominiert das &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; alles andere. Wir ignorieren den Faktor &amp;lt;math&amp;gt;\frac{c}{2}&amp;lt;/math&amp;gt; und den kleineren Teil &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Das Wachstum ist quadratisch, also &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 3. Average-Case-Szenario (Durchschnittlicher Fall) ===&lt;br /&gt;
Die Werte im Array sind völlig zufällig verteilt.&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Im Durchschnitt müssen wir ein Element nicht bis ganz an den Anfang schieben (wie im Worst-Case), sondern nur bis zur &#039;&#039;&#039;Hälfte&#039;&#039;&#039; des bisher sortierten Bereichs.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Die Anzahl der Vergleiche halbiert sich grob im Vergleich zum Worst-Case. Der Aufwand liegt bei ca. &amp;lt;math&amp;gt;\frac{1}{2} \cdot \frac{c}{2}n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Wie wir gelernt haben, ignoriert die Groß-O-Notation konstante Brüche (wie &amp;lt;math&amp;gt;\frac{1}{4}&amp;lt;/math&amp;gt;). Es bleibt bei der höchsten Potenz: Das Wachstum ist und bleibt quadratisch. Die Komplexität lautet also weiterhin &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Quicksort&amp;diff=2828</id>
		<title>Quicksort</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Quicksort&amp;diff=2828"/>
		<updated>2026-04-20T07:39:38Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Rekursive Baumstruktur.jpg|mini|Rekursive Aufteilung beim Quicksort]]&lt;br /&gt;
Ein in der Praxis oft eingesetzter und sehr effizienter [[Sortieren|Sortieralgorithmus]] ist der &#039;&#039;&#039;Quicksort&#039;&#039;&#039; (englisch für „schnelles Sortieren“). Er funktioniert nach dem Prinzip &#039;&#039;&#039;„Teile und herrsche“&#039;&#039;&#039; (Divide and Conquer), welches in der Programmierung meist mithilfe von [[Rekursion]] realisiert wird. &lt;br /&gt;
&lt;br /&gt;
Die Grundidee ist einfach: Die zu sortierende Datenmenge wird in zwei kleinere Teillisten zerlegt (&amp;quot;Teile&amp;quot;) und diese werden anschließend separat sortiert (&amp;quot;Herrsche&amp;quot;). Diese Teillisten werden wiederum in noch kleinere Teillisten zerlegt, bis eine Liste nur noch aus einem einzigen Element besteht. Eine Liste mit nur einem Element ist naturgemäß immer sortiert – an diesem Punkt endet die [[Rekursion]] [http://openbook.galileocomputing.de/c_von_a_bis_z/022_c_algorithmen_003.htm (vgl. Galileo Computing)].&lt;br /&gt;
&lt;br /&gt;
=== Wie funktioniert die Zerlegung (Partitionierung)? ===&lt;br /&gt;
Die Zerlegung der Liste erfolgt anhand eines sogenannten &#039;&#039;&#039;Pivot-Elements&#039;&#039;&#039; (Dreh- und Angelpunkt). &lt;br /&gt;
# Ein beliebiges Element der Liste wird als Pivot ausgewählt.&lt;br /&gt;
# Alle anderen Elemente der Liste werden mit diesem Pivot verglichen.&lt;br /&gt;
# Die Liste wird in zwei Bereiche unterteilt:&lt;br /&gt;
#* &#039;&#039;&#039;Linke Teilliste:&#039;&#039;&#039; Alle Elemente, die kleiner (oder gleich) dem Pivot sind.&lt;br /&gt;
#* &#039;&#039;&#039;Rechte Teilliste:&#039;&#039;&#039; Alle Elemente, die größer als das Pivot sind.&lt;br /&gt;
&lt;br /&gt;
Nach diesem Schritt steht das Pivot-Element bereits an seiner endgültigen, korrekten Position in der Liste. Die Elemente in der linken und rechten Teilliste sind zwar untereinander noch unsortiert, aber es ist garantiert, dass alle Elemente links vom Pivot kleiner sind als alle Elemente rechts davon.&lt;br /&gt;
&lt;br /&gt;
Anschließend ruft sich der Quicksort-Algorithmus selbst auf (Rekursion), um die linke und die rechte Teilliste nach exakt demselben Muster weiter zu sortieren. Ein großer Vorteil dieses Verfahrens: Die Teillisten sind immer Abschnitte der ursprünglichen Liste. Es wird also (abgesehen vom Speicher für die Rekursionsaufrufe) kein zusätzlicher Speicherplatz für neue Listen benötigt (In-Place-Sortierung).&lt;br /&gt;
&lt;br /&gt;
== Beispiel ==&lt;br /&gt;
Als Beispiel betrachten wir eine unsortierte Liste mit Zahlen. Der Einfachheit halber wählen wir in diesem Beispiel immer das &#039;&#039;&#039;erste Element&#039;&#039;&#039; als Pivot-Element. Zu Beginn ist die Zahl 7 also unser Pivot.&lt;br /&gt;
&lt;br /&gt;
7 &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;10 8&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;11&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;9&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;3 1 5 2&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Zahlen, die kleiner als das Pivot-Element 7 sind, wurden blau markiert. Die Zahlen, die größer als 7 sind, wurden rot markiert. Nach dem ersten Partitionierungsschritt hat QuickSort die Liste folgendermaßen umgeordnet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;3 2 5 6 1 4&amp;lt;/span&amp;gt; &#039;&#039;&#039;7&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun stehen alle Zahlen, die kleiner sind als 7, links von ihr. Alle Zahlen, die größer sind, stehen rechts. Die Reihenfolge der Zahlen innerhalb der blauen und roten Teillisten ist hier noch willkürlich und hängt von der genauen Programmierung ab. Entscheidend ist: &#039;&#039;&#039;Die 7 hat nun ihre endgültige, korrekte Position gefunden.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Nächstes wird die linke (blaue) Teilliste per QuickSort sortiert. Als neues Pivot wählen wir wieder die erste Zahl des Bereichs, die 3. Die Zahlen, die kleiner sind als 3, werden wieder blau markiert, die größeren rot. Die bereits einsortierte 7 und die rechte Teilliste ignorieren wir für den Moment (grau hinterlegt):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;5 6&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:gray&amp;quot;&amp;gt;7 9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Algorithmus sortiert die Elemente relativ zur 3 um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;1 2&amp;lt;/span&amp;gt; &#039;&#039;&#039;3&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;6 5 4&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:gray&amp;quot;&amp;gt;7 9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Prozess wird fortgesetzt (erst die 1 und 2, dann die 6, 5 und 4), bis der gesamte linke Teil des Arrays vollständig sortiert ist. Das Ergebnis sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;1 2 3 4 5 6&#039;&#039; &#039;&#039;&#039;7&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:gray&amp;quot;&amp;gt;9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird die Teilliste rechts von der 7 nach demselben Prinzip sortiert. Wieder wird ein Pivot gewählt (hier die 9), und die kleineren und größeren Zahlen werden auf die jeweils richtige Seite gebracht, bis das gesamte Array sortiert ist.&lt;br /&gt;
&lt;br /&gt;
== Pseudocode ==&lt;br /&gt;
Der folgende Pseudocode illustriert die Arbeitsweise des [[Algorithmus]]. Bei jedem Aufruf von &amp;lt;code&amp;gt;quicksort(...)&amp;lt;/code&amp;gt; gibt &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt; den Index des ersten Elements in der Teilliste an und &amp;lt;code&amp;gt;rechts&amp;lt;/code&amp;gt; den des letzten. Beim ersten Aufruf (oberste Rekursionsebene) ist &amp;lt;code&amp;gt;links = 0&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;rechts = n-1&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
funktion quicksort(links, rechts) &lt;br /&gt;
    falls links &amp;lt; rechts dann &lt;br /&gt;
        // teile() ordnet das Array um und liefert die finale Position des Pivots&lt;br /&gt;
        teiler := teile(links, rechts) &lt;br /&gt;
        &lt;br /&gt;
        // Rekursiver Aufruf für die linke Teilliste&lt;br /&gt;
        quicksort(links, teiler - 1) &lt;br /&gt;
        &lt;br /&gt;
        // Rekursiver Aufruf für die rechte Teilliste&lt;br /&gt;
        quicksort(teiler + 1, rechts) &lt;br /&gt;
    ende&lt;br /&gt;
ende&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wahl des Pivot-Elements ==&lt;br /&gt;
Die Effizienz von Quicksort steht und fällt mit der Wahl des Pivot-Elements. &lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schlechteste Wahl:&#039;&#039;&#039; Das Pivot-Element ist zufällig immer das kleinste oder größte Element der Teilliste. Dann wird die Liste extrem ungleichmäßig geteilt (eine Liste mit 0 Elementen, eine mit &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Elementen).&lt;br /&gt;
* &#039;&#039;&#039;Beste Wahl:&#039;&#039;&#039; Das Pivot-Element ist genau der [[Median]] (der Wert, der exakt in der Mitte der sortierten Liste stehen würde). Die Liste wird in zwei exakt gleich große Hälften geteilt. Da der Median in unsortierten Daten aber aufwendig zu suchen ist, behilft man sich mit Tricks.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Warum das erste Element oft eine schlechte Wahl ist:&#039;&#039;&#039;&lt;br /&gt;
Wenn man immer stur das erste Element als Pivot wählt (wie in unserem Beispiel) und die Eingabeliste ist bereits (oder fast) fertig sortiert, trifft automatisch immer der oben beschriebene schlechteste Fall ein. Der Algorithmus wird dadurch extrem langsam.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Lösungsansätze für die Praxis:&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;Randomisierung:&#039;&#039;&#039; Man wählt ein zufälliges Element aus dem Intervall als Pivot.&lt;br /&gt;
# &#039;&#039;&#039;Median-of-Three:&#039;&#039;&#039; Man betrachtet das erste, das mittlere und das letzte Element der Teilliste und wählt von diesen dreien den mittleren Wert als Pivot. Dies schützt sehr gut vor bereits vorsortierten Listen.&lt;br /&gt;
&lt;br /&gt;
== Java Implementierung ==&lt;br /&gt;
Bisher wurde die allgemeine Funktionsweise beschrieben. Am besten lassen sich komplexe [[Algorithmus|Algorithmen]] nachvollziehen, wenn man sie anhand einer Implementierung mit Hilfe eines [[Debugger|Debuggers]] analysiert. In der folgenden [[Java]]-Implementierung wird ein [[Array]] aus Ganzzahlen (&amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;) sortiert. &lt;br /&gt;
&lt;br /&gt;
Zur Abwechslung wird in dieser Implementierung immer das &#039;&#039;&#039;letzte&#039;&#039;&#039; Element des aktuellen Bereichs (&amp;lt;code&amp;gt;array[rechts]&amp;lt;/code&amp;gt;) als Pivot gewählt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void quickSort(int array[], int links, int rechts) {&lt;br /&gt;
      if (links &amp;lt; rechts) {&lt;br /&gt;
         // Teile das Array und finde die Position des Pivot-Elements&lt;br /&gt;
         int i = teile(array, links, rechts);&lt;br /&gt;
         &lt;br /&gt;
         // Sortiere den linken Teil&lt;br /&gt;
         quickSort(array, links, i - 1);&lt;br /&gt;
         // Sortiere den rechten Teil&lt;br /&gt;
         quickSort(array, i + 1, rechts);&lt;br /&gt;
      }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public int teile(int array[], int links, int rechts) {&lt;br /&gt;
      int pivot, i, j, help;&lt;br /&gt;
      pivot = array[rechts]; // Wir wählen das rechte Randelement als Pivot               &lt;br /&gt;
      i = links;&lt;br /&gt;
      j = rechts - 1;&lt;br /&gt;
      &lt;br /&gt;
      // Von links und rechts zur Mitte laufen, bis sich i und j kreuzen&lt;br /&gt;
      while (i &amp;lt;= j) {&lt;br /&gt;
         // Suche von links ein Element, das größer als das Pivot ist&lt;br /&gt;
         if (array[i] &amp;gt; pivot) {     &lt;br /&gt;
            // Tausche array[i] mit array[j], um es in den rechten Bereich zu bringen&lt;br /&gt;
            help = array[i]; &lt;br /&gt;
            array[i] = array[j]; &lt;br /&gt;
            array[j] = help;                             &lt;br /&gt;
            j--; // j einen Schritt weiter nach links schieben&lt;br /&gt;
         } else {&lt;br /&gt;
            i++; // i war kleiner/gleich Pivot, ist also korrekt positioniert -&amp;gt; weiter nach rechts&lt;br /&gt;
         }          &lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      // Das Pivot-Element an seine endgültige Position (i) tauschen&lt;br /&gt;
      help = array[i];&lt;br /&gt;
      array[i] = array[rechts];&lt;br /&gt;
      array[rechts] = help;&lt;br /&gt;
      &lt;br /&gt;
      // Rückgabe der finalen Position des Pivot-Elements&lt;br /&gt;
      return i;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Maximumsuche ==&lt;br /&gt;
Bei komplexeren Algorithmen wie [[Quicksort]] reicht einfaches Zählen nicht mehr aus. Hier arbeiten wir mit einem mathematischen Trick: &#039;&#039;&#039;Indikatorvariablen&#039;&#039;&#039; und der &#039;&#039;&#039;Harmonischen Reihe&#039;&#039;&#039;. Um das zu verstehen, analysieren wir einen simplen Code zur Maximumsuche:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public int findMax(int[] A) {&lt;br /&gt;
    int max = A[0];&lt;br /&gt;
    for (int i = 1; i &amp;lt; A.length; i++) {&lt;br /&gt;
        if (A[i] &amp;gt; max) {&lt;br /&gt;
            max = A[i]; // Update-Operation&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return max;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Vergleiche in der &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung werden immer &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Mal ausgeführt. Aber wie oft wird die Variable &amp;lt;code&amp;gt;max&amp;lt;/code&amp;gt; im Durchschnitt überschrieben (Update-Operation)?&lt;br /&gt;
&lt;br /&gt;
Wir stellen uns vor, das Array enthält völlig zufällig gemischte Zahlen.&lt;br /&gt;
* &#039;&#039;&#039;Intuitiver Ansatz:&#039;&#039;&#039; &lt;br /&gt;
Das 1. Element ist automatisch das bisherige Maximum (Wahrscheinlichkeit: 1). &lt;br /&gt;
Damit das 2. Element ein neues Maximum ist, muss es größer als das erste sein. Die Chance dafür ist exakt 50% (&amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt;). &lt;br /&gt;
Damit das 3. Element ein neues Maximum ist, muss es das Größte der ersten drei Elemente sein. Die Chance dafür ist &amp;lt;math&amp;gt;\frac{1}{3}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Mathematischer Ansatz (Indikatorvariablen):&#039;&#039;&#039;&lt;br /&gt;
Wir definieren einen „Schalter“ (die Indikatorvariable) &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt;. Er ist &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;, wenn ein neues Maximum an Stelle &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; gefunden wird, sonst &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Die Wahrscheinlichkeit dafür ist &amp;lt;math&amp;gt;P(X_i = 1) = \frac{1}{i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Um die durchschnittliche Gesamtzahl der Updates zu finden, addieren wir einfach diese Wahrscheinlichkeiten auf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E[X] = \sum_{i=1}^{n} \frac{1}{i} = 1 + \frac{1}{2} + \frac{1}{3} + \frac{1}{4} + \dots + \frac{1}{n}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese spezielle Summe nennt man in der Mathematik die &#039;&#039;&#039;Harmonische Reihe&#039;&#039;&#039; (&amp;lt;math&amp;gt;H_n&amp;lt;/math&amp;gt;). Sie wächst extrem langsam. Für große Zahlen &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; entspricht sie in etwa dem &#039;&#039;&#039;natürlichen Logarithmus&#039;&#039;&#039; (&amp;lt;math&amp;gt;\ln(n)&amp;lt;/math&amp;gt;). &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Erkenntnis für Quicksort:&#039;&#039;&#039; Obwohl das Array &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elemente hat, wird die Update-Operation im Durchschnitt nur logarithmisch oft (&amp;lt;math&amp;gt;\mathcal{O}(\log n)&amp;lt;/math&amp;gt;) ausgeführt. Genau dieses stochastische Prinzip (das Aufsummieren von Einzelwahrscheinlichkeiten, die zu einem Logarithmus führen) ist der Schlüssel, um später selbst zu beweisen, dass Quicksort im Average-Case bei &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt; liegt.&lt;br /&gt;
&lt;br /&gt;
== Laufzeitanalyse ==&lt;br /&gt;
&lt;br /&gt;
=== Worst-Case (Schlechtester Fall) ===&lt;br /&gt;
[[Datei:Baum N-Ebenen.png|mini|Worst-Case: Entarteter Baum]]&lt;br /&gt;
Betrachten wir zunächst die Worst-Case-[[Laufzeitanalyse]]. Angenommen, wir haben extremes Pech und die Partitionierung ist maximal unausgeglichen. Das gewählte Pivot ist immer das absolut kleinste oder größte Element. Dann enthält eine der Partitionen 0 Elemente und die andere Partition &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Elemente. &lt;br /&gt;
&lt;br /&gt;
Der Aufwand für das Vergleichen (Partitionieren) einer Ebene mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen ist proportional zu &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Nennen wir diesen Aufwand &amp;lt;math&amp;gt;c \cdot n&amp;lt;/math&amp;gt; (wobei &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; eine Konstante für die Dauer eines Vergleichs ist).&lt;br /&gt;
Auf der nächsten Ebene müssen wir &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Elemente vergleichen, der Aufwand ist &amp;lt;math&amp;gt;c \cdot (n-1)&amp;lt;/math&amp;gt;. Danach &amp;lt;math&amp;gt;c \cdot (n-2)&amp;lt;/math&amp;gt; und so weiter.&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Partitionierungszeiten für alle Ebenen aufsummieren, erhalten wir folgende Reihe:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot n + c \cdot (n - 1) + c \cdot (n - 2) + \dots + c \cdot 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir klammern &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; aus und erkennen die bekannte [[Arithmetische-reihe|Gaußsche Summenformel]]:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot (n + (n-1) + (n-2) + \dots + 2) \approx c \cdot \frac{n(n+1)}{2} = \frac{c}{2}n^2 + \frac{c}{2}n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Da in der asymtotischen Laufzeitanalyse konstante Faktoren und kleinere Terme (wie &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) ignoriert werden, wächst der Aufwand quadratisch. &lt;br /&gt;
&#039;&#039;&#039;Fazit Worst-Case:&#039;&#039;&#039; Quicksort hat im schlechtesten Fall eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Best-Case (Bester Fall) ===&lt;br /&gt;
[[Datei:Verzweigungsbaum Quicksort symetrisch.png|mini|Best-Case: Symmetrischer Baum]]&lt;br /&gt;
Der beste Fall liegt vor, wenn das Pivot-Element die Liste immer exakt in der Mitte teilt. Die Teillisten sind dann immer gleich groß.&lt;br /&gt;
[[Datei:SubArrays symetrisch Quicksort.png|mini|Halbierung der Arraygrößen]]&lt;br /&gt;
&lt;br /&gt;
Wird die Anzahl der zu sortierenden Elemente &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; verdoppelt, kommt durch die fortlaufende Halbierung lediglich &#039;&#039;&#039;eine einzige neue Rekursionsebene&#039;&#039;&#039; (ein Baum-Level) hinzu. Dieses Wachstumsverhalten (wie oft kann ich &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; durch 2 teilen, bis 1 übrig bleibt?) wird mathematisch durch den Logarithmus zur Basis 2 ausgedrückt: &amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Zu sortierende Elemente &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 4&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 8&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 16&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 32&lt;br /&gt;
|-&lt;br /&gt;
! Als Zweierpotenz&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^3}&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^4}&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^5}&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Anzahl Rekursionsebenen &amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 2&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 3&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 4&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wir haben also insgesamt &amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt; Ebenen. Auf &#039;&#039;&#039;jeder einzelnen Ebene&#039;&#039;&#039; müssen in der Summe &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elemente betrachtet und mit dem jeweiligen Pivot verglichen werden. &lt;br /&gt;
Wir multiplizieren also die Arbeit pro Ebene (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) mit der Anzahl der Ebenen (&amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&#039;&#039;&#039;Fazit Best-Case:&#039;&#039;&#039; Die Laufzeit beträgt &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Average-Case (Durchschnittlicher Fall) ===&lt;br /&gt;
In der Praxis haben wir selten den perfekten Best-Case, aber fast nie den absoluten Worst-Case. Meistens teilt das Pivot die Liste beispielsweise im Verhältnis 30:70 oder 60:40. &lt;br /&gt;
&lt;br /&gt;
Um den Average-Case mathematisch exakt zu beweisen, greifen wir auf das Prinzip der &#039;&#039;&#039;Indikatorvariablen&#039;&#039;&#039; und der &#039;&#039;&#039;Harmonischen Reihe&#039;&#039;&#039; zurück. Wir fragen uns: &#039;&#039;Wie hoch ist die Wahrscheinlichkeit, dass zwei beliebige Elemente (nennen wir sie &amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt;) im Laufe des Algorithmus miteinander verglichen werden?&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Zwei Elemente werden genau dann miteinander verglichen, wenn eines der beiden als Pivot-Element ausgewählt wird, &#039;&#039;&#039;bevor&#039;&#039;&#039; irgendein Element, dessen Wert zwischen den beiden liegt, als Pivot gewählt wird.&lt;br /&gt;
* Sind die Daten völlig zufällig verteilt, ist die Wahrscheinlichkeit, dass von &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; Elementen genau das größte oder kleinste zuerst als Pivot gewählt wird, exakt &amp;lt;math&amp;gt;\frac{2}{k}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Summieren wir diese Wahrscheinlichkeiten (Erwartungswert) für alle Elementpaare im Array auf, erhalten wir folgende Doppel-Summe:&lt;br /&gt;
&amp;lt;math&amp;gt;E(X) = \sum_{i=1}^{n-1} \sum_{j=i+1}^{n} \frac{2}{j - i + 1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Setzt man für den Abstand &amp;lt;math&amp;gt;(j - i + 1)&amp;lt;/math&amp;gt; eine neue Variable &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; ein, löst sich die innere Summe auf zu:&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_{k=2}^{n} \frac{2}{k} = 2 \cdot \left( \frac{1}{2} + \frac{1}{3} + \dots + \frac{1}{n} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier erkennen wir exakt die &#039;&#039;&#039;Harmonische Reihe&#039;&#039;&#039; (&amp;lt;math&amp;gt;H_n&amp;lt;/math&amp;gt;) wieder, die wir bereits bei der Maximumsuche kennengelernt haben! Da die Harmonische Reihe für große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; gegen den natürlichen Logarithmus &amp;lt;math&amp;gt;\ln(n)&amp;lt;/math&amp;gt; konvergiert, ergibt die innere Summe ungefähr &amp;lt;math&amp;gt;2 \cdot \ln(n)&amp;lt;/math&amp;gt;. Da die äußere Summe &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-mal läuft, erhalten wir insgesamt rund &amp;lt;math&amp;gt;2n \cdot \ln(n)&amp;lt;/math&amp;gt; Vergleiche. &lt;br /&gt;
&lt;br /&gt;
Da Konstanten wie der Faktor 2 beim Groß-O ignoriert werden, ist nun stochastisch bewiesen: &lt;br /&gt;
&#039;&#039;&#039;Fazit Average-Case:&#039;&#039;&#039; Auch bei zufälliger Verteilung konvergiert Quicksort dank des logarithmischen Wachstums der harmonischen Reihe sicher gegen die Komplexitätsklasse &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;br /&gt;
[[Kategorie:FI_I_TP2]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2827</id>
		<title>Laufzeitanalyse</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2827"/>
		<updated>2026-04-20T07:39:06Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]]&lt;br /&gt;
Die Laufzeitanalyse untersucht die sogenannte Zeitkomplexität eines [[Algorithmus]]. Man stellt sich dabei im Kern die Frage: &#039;&#039;&#039;„Wie verhält sich die Rechenzeit, wenn die Menge der zu verarbeitenden Daten extrem groß wird?“&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Da die echte Ausführungszeit (in Sekunden) stark vom jeweiligen Computer abhängt, misst man stattdessen die Anzahl der elementaren [[Anweisung|Befehle]] (z. B. Vergleiche oder Tauschoperationen). Diese Anzahl hängt von der Größe der Eingabemenge ab, die in der Informatik meist als &#039;&#039;&#039;Problemgröße &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&#039;&#039;&#039; bezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
Für kleine Datenmengen (z. B. eine Liste mit 10 Elementen sortieren) ist fast jeder Algorithmus schnell genug. Spannend wird es erst bei sehr großen Datenmengen (z. B. Millionen von Kundendatensätzen). Hier betrachtet man die sogenannte asymptotische [[Programmausführung|Laufzeit]]. Sie beschreibt das Verhalten des Algorithmus, wenn die Datenmenge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; theoretisch ins Unendliche wächst.&lt;br /&gt;
&lt;br /&gt;
Lässt sich die Laufzeit eines Algorithmus mathematisch durch eine Funktion beschreiben, wie beispielsweise:&lt;br /&gt;
&amp;lt;math&amp;gt;f(x) = \frac{1}{x} + x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So nähert sich die rot dargestellte Funktion &amp;lt;math&amp;gt;\color{red}{f(x)}&amp;lt;/math&amp;gt; für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; der grün dargestellten Asymptote &amp;lt;math&amp;gt;\color{green}{g(x) = x}&amp;lt;/math&amp;gt; an.&lt;br /&gt;
&lt;br /&gt;
Die Laufzeit wird mit der sogenannten Landau-Notation (Groß-O-Notation, z. B. &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;) abgeschätzt. Dabei werden konstante Vorfaktoren (wie &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt;) oder kleinere Summanden ignoriert, da bei riesigen Datenmengen nur noch der dominierende Teil der Funktion das Wachstum bestimmt.&lt;br /&gt;
&lt;br /&gt;
=== Was die Laufzeitanalyse NICHT ist ===&lt;br /&gt;
Sie misst &#039;&#039;&#039;nicht&#039;&#039;&#039; den tatsächlichen Zeitaufwand in Millisekunden auf einem echten Computer. Sie ist ein theoretisches Modell, um Algorithmen hardwareunabhängig miteinander vergleichen zu können.&lt;br /&gt;
&lt;br /&gt;
== Szenarien der Laufzeitabschätzung ==&lt;br /&gt;
Da Algorithmen je nach Vorsortierung der Daten unterschiedlich schnell arbeiten, unterscheidet man drei klassische Fälle:&lt;br /&gt;
&lt;br /&gt;
* Das &#039;&#039;&#039;Worst-Case-Szenario&#039;&#039;&#039; (Schlechtester Fall): Die Daten liegen so ungünstig vor, dass der Algorithmus maximal lange braucht. Dies liefert eine absolute Sicherheitsgarantie (obere Schranke).&lt;br /&gt;
* Das &#039;&#039;&#039;Best-Case-Szenario&#039;&#039;&#039; (Bester Fall): Die Daten liegen optimal vor (z. B. bereits sortiert). Der Algorithmus ist minimal schnell fertig.&lt;br /&gt;
* Das &#039;&#039;&#039;Average-Case-Szenario&#039;&#039;&#039; (Durchschnittlicher Fall): Beschreibt die zu erwartende Laufzeit bei völlig zufällig gemischten Daten. Dies ist oft das realistischste Szenario in der Praxis.&lt;br /&gt;
&lt;br /&gt;
== Mathematische Berechnung des Average-Case ==&lt;br /&gt;
Oft wird fälschlicherweise angenommen, der Average-Case sei einfach „die Hälfte des Worst-Case“. Das ist mathematisch ungenau. Tatsächlich berechnet man den Average-Case mit Hilfe der Stochastik: Er entspricht dem &#039;&#039;&#039;Erwartungswert&#039;&#039;&#039; der Laufzeit.&lt;br /&gt;
&lt;br /&gt;
Man berechnet ihn, indem man alle möglichen Eingaben durchspielt und gewichtet. Die Formel für den Erwartungswert &amp;lt;math&amp;gt;E(T)&amp;lt;/math&amp;gt; lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \sum_{i=1}^{|E|} p_i \cdot t_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; ist die Wahrscheinlichkeit für einen bestimmten Fall &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; ist die Anzahl der Rechenschritte, die der Algorithmus in diesem Fall braucht.&lt;br /&gt;
&lt;br /&gt;
Für die Schule gehen wir meistens von einer &#039;&#039;&#039;Gleichverteilung&#039;&#039;&#039; aus: Jeder Fall tritt mit derselben Wahrscheinlichkeit auf.&lt;br /&gt;
&lt;br /&gt;
=== Average-Case der Linearen Suche ===&lt;br /&gt;
Wir suchen eine bestimmte Zahl in einem Array der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Wir nehmen an, die Zahl ist im Array und jede Position ist gleich wahrscheinlich.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schritt 1: Wahrscheinlichkeit bestimmen.&#039;&#039;&#039; Die Wahrscheinlichkeit, dass unsere Zahl an Position 1, 2 oder &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; steht, ist jeweils &amp;lt;math&amp;gt;p = \frac{1}{n}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 2: Schritte zählen.&#039;&#039;&#039; Steht die Zahl an Position 1, brauchen wir 1 Vergleich. An Position 2 brauchen wir 2 Vergleiche. An Position &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; brauchen wir &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Vergleiche.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 3: Erwartungswert berechnen.&#039;&#039;&#039; Wir setzen dies in die Formel ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n}\cdot 1 + \frac{1}{n}\cdot 2 + \dots + \frac{1}{n}\cdot n = \sum_{i=1}^{n} \left( \frac{1}{n} \cdot i \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir klammern &amp;lt;math&amp;gt;\frac{1}{n}&amp;lt;/math&amp;gt; aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \sum_{i=1}^{n} i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Summe der Zahlen von 1 bis &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; lässt sich mit der &#039;&#039;&#039;Gaußschen Summenformel&#039;&#039;&#039; (kleiner Gauß) vereinfachen zu &amp;lt;math&amp;gt;\frac{n(n+1)}{2}&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \frac{n(n+1)}{2} = \frac{n+1}{2} = \frac{1}{2}n + \frac{1}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fazit:&#039;&#039;&#039; Im Durchschnitt braucht die lineare Suche &amp;lt;math&amp;gt;\frac{n+1}{2}&amp;lt;/math&amp;gt; Vergleiche. Da in der &amp;lt;math&amp;gt;\mathcal{O}&amp;lt;/math&amp;gt;-Notation Konstanten wie &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; wegfallen, gehört die lineare Suche im Durchschnitt zur Klasse &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Laufzeitanalyse von Insertion Sort ==&lt;br /&gt;
Nun wenden wir unser Wissen auf das Sortierverfahren [[Insertion-sort|Insertion Sort]] (Sortieren durch Einfügen) an. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void sortierenEinfuegen_MC() {&lt;br /&gt;
    int v, i, j;&lt;br /&gt;
    for (i = 1; i &amp;lt; zSortfeld.length; i++) {&lt;br /&gt;
        c++; // Zähler für Vergleiche&lt;br /&gt;
        if (zSortfeld[i] &amp;lt; zSortfeld[i - 1]) {&lt;br /&gt;
            v = zSortfeld[i];&lt;br /&gt;
            j = i;&lt;br /&gt;
            do {&lt;br /&gt;
                zSortfeld[j] = zSortfeld[j - 1];&lt;br /&gt;
                j--;&lt;br /&gt;
            } while ((j &amp;gt; 0) &amp;amp;&amp;amp; (zSortfeld[j - 1] &amp;gt; v));&lt;br /&gt;
            zSortfeld[j] = v; &lt;br /&gt;
        }&lt;br /&gt;
        printAnalyse(i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir analysieren eine Liste mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen. Den Zeitverbrauch für einen einzelnen Vergleich nennen wir &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 1. Best-Case-Szenario (Bester Fall) ===&lt;br /&gt;
Die Liste ist bereits perfekt aufsteigend sortiert (z. B. &amp;lt;code&amp;gt;[0, 2, 3, 5, 7]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Da jedes Element schon größer als sein Vorgänger ist, ist die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung immer falsch. Die innere &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife wird ignoriert.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Wir machen exakt einen Vergleich pro Element in der äußeren Schleife. Das ergibt &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt; Vergleiche.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Der Aufwand wächst wie eine Gerade (linear). Die Komplexität ist &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 2. Worst-Case-Szenario (Schlechtester Fall) ===&lt;br /&gt;
Die Liste ist exakt falsch herum sortiert (z. B. &amp;lt;code&amp;gt;[7, 5, 3, 2, 0]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung ist immer wahr. Jedes Element muss durch die innere Schleife komplett bis an den Anfang der Liste getauscht werden.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Beim 1. Durchlauf machen wir 1 Vergleich. Beim 2. Durchlauf 2 Vergleiche. Beim letzten Durchlauf &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Vergleiche. Wir müssen also wieder addieren: &amp;lt;math&amp;gt;1 + 2 + 3 + \dots + (n-1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Wie bei der linearen Suche hilft uns hier die Gaußsche Summenformel:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot \frac{(n - 1) \cdot n}{2} = \frac{c}{2}n^2 - \frac{c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Asymptote an quadratische Funktion2.png|mini|Quadratisches Wachstum]]&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Für extrem große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; dominiert das &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; alles andere. Wir ignorieren den Faktor &amp;lt;math&amp;gt;\frac{c}{2}&amp;lt;/math&amp;gt; und den kleineren Teil &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Das Wachstum ist quadratisch, also &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 3. Average-Case-Szenario (Durchschnittlicher Fall) ===&lt;br /&gt;
Die Werte im Array sind völlig zufällig verteilt.&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Im Durchschnitt müssen wir ein Element nicht bis ganz an den Anfang schieben (wie im Worst-Case), sondern nur bis zur &#039;&#039;&#039;Hälfte&#039;&#039;&#039; des bisher sortierten Bereichs.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Die Anzahl der Vergleiche halbiert sich grob im Vergleich zum Worst-Case. Der Aufwand liegt bei ca. &amp;lt;math&amp;gt;\frac{1}{2} \cdot \frac{c}{2}n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Wie wir gelernt haben, ignoriert die Groß-O-Notation konstante Brüche (wie &amp;lt;math&amp;gt;\frac{1}{4}&amp;lt;/math&amp;gt;). Es bleibt bei der höchsten Potenz: Das Wachstum ist und bleibt quadratisch. Die Komplexität lautet also weiterhin &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Quicksort&amp;diff=2826</id>
		<title>Quicksort</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Quicksort&amp;diff=2826"/>
		<updated>2026-04-20T07:37:11Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Rekursive Baumstruktur.jpg|mini|Rekursive Aufteilung beim Quicksort]]&lt;br /&gt;
Ein in der Praxis oft eingesetzter und sehr effizienter [[Sortieren|Sortieralgorithmus]] ist der &#039;&#039;&#039;Quicksort&#039;&#039;&#039; (englisch für „schnelles Sortieren“). Er funktioniert nach dem Prinzip &#039;&#039;&#039;„Teile und herrsche“&#039;&#039;&#039; (Divide and Conquer), welches in der Programmierung meist mithilfe von [[Rekursion]] realisiert wird. &lt;br /&gt;
&lt;br /&gt;
Die Grundidee ist einfach: Die zu sortierende Datenmenge wird in zwei kleinere Teillisten zerlegt (&amp;quot;Teile&amp;quot;) und diese werden anschließend separat sortiert (&amp;quot;Herrsche&amp;quot;). Diese Teillisten werden wiederum in noch kleinere Teillisten zerlegt, bis eine Liste nur noch aus einem einzigen Element besteht. Eine Liste mit nur einem Element ist naturgemäß immer sortiert – an diesem Punkt endet die [[Rekursion]] [http://openbook.galileocomputing.de/c_von_a_bis_z/022_c_algorithmen_003.htm (vgl. Galileo Computing)].&lt;br /&gt;
&lt;br /&gt;
=== Wie funktioniert die Zerlegung (Partitionierung)? ===&lt;br /&gt;
Die Zerlegung der Liste erfolgt anhand eines sogenannten &#039;&#039;&#039;Pivot-Elements&#039;&#039;&#039; (Dreh- und Angelpunkt). &lt;br /&gt;
# Ein beliebiges Element der Liste wird als Pivot ausgewählt.&lt;br /&gt;
# Alle anderen Elemente der Liste werden mit diesem Pivot verglichen.&lt;br /&gt;
# Die Liste wird in zwei Bereiche unterteilt:&lt;br /&gt;
#* &#039;&#039;&#039;Linke Teilliste:&#039;&#039;&#039; Alle Elemente, die kleiner (oder gleich) dem Pivot sind.&lt;br /&gt;
#* &#039;&#039;&#039;Rechte Teilliste:&#039;&#039;&#039; Alle Elemente, die größer als das Pivot sind.&lt;br /&gt;
&lt;br /&gt;
Nach diesem Schritt steht das Pivot-Element bereits an seiner endgültigen, korrekten Position in der Liste. Die Elemente in der linken und rechten Teilliste sind zwar untereinander noch unsortiert, aber es ist garantiert, dass alle Elemente links vom Pivot kleiner sind als alle Elemente rechts davon.&lt;br /&gt;
&lt;br /&gt;
Anschließend ruft sich der Quicksort-Algorithmus selbst auf (Rekursion), um die linke und die rechte Teilliste nach exakt demselben Muster weiter zu sortieren. Ein großer Vorteil dieses Verfahrens: Die Teillisten sind immer Abschnitte der ursprünglichen Liste. Es wird also (abgesehen vom Speicher für die Rekursionsaufrufe) kein zusätzlicher Speicherplatz für neue Listen benötigt (In-Place-Sortierung).&lt;br /&gt;
&lt;br /&gt;
== Beispiel ==&lt;br /&gt;
Als Beispiel betrachten wir eine unsortierte Liste mit Zahlen. Der Einfachheit halber wählen wir in diesem Beispiel immer das &#039;&#039;&#039;erste Element&#039;&#039;&#039; als Pivot-Element. Zu Beginn ist die Zahl 7 also unser Pivot.&lt;br /&gt;
&lt;br /&gt;
7 &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;10 8&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;11&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;9&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;3 1 5 2&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Zahlen, die kleiner als das Pivot-Element 7 sind, wurden blau markiert. Die Zahlen, die größer als 7 sind, wurden rot markiert. Nach dem ersten Partitionierungsschritt hat QuickSort die Liste folgendermaßen umgeordnet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;3 2 5 6 1 4&amp;lt;/span&amp;gt; &#039;&#039;&#039;7&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun stehen alle Zahlen, die kleiner sind als 7, links von ihr. Alle Zahlen, die größer sind, stehen rechts. Die Reihenfolge der Zahlen innerhalb der blauen und roten Teillisten ist hier noch willkürlich und hängt von der genauen Programmierung ab. Entscheidend ist: &#039;&#039;&#039;Die 7 hat nun ihre endgültige, korrekte Position gefunden.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Nächstes wird die linke (blaue) Teilliste per QuickSort sortiert. Als neues Pivot wählen wir wieder die erste Zahl des Bereichs, die 3. Die Zahlen, die kleiner sind als 3, werden wieder blau markiert, die größeren rot. Die bereits einsortierte 7 und die rechte Teilliste ignorieren wir für den Moment (grau hinterlegt):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;2&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;5 6&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:gray&amp;quot;&amp;gt;7 9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Algorithmus sortiert die Elemente relativ zur 3 um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;1 2&amp;lt;/span&amp;gt; &#039;&#039;&#039;3&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;6 5 4&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color:gray&amp;quot;&amp;gt;7 9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Prozess wird fortgesetzt (erst die 1 und 2, dann die 6, 5 und 4), bis der gesamte linke Teil des Arrays vollständig sortiert ist. Das Ergebnis sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;1 2 3 4 5 6&#039;&#039; &#039;&#039;&#039;7&#039;&#039;&#039; &amp;lt;span style=&amp;quot;color:gray&amp;quot;&amp;gt;9 11 8 10 12&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird die Teilliste rechts von der 7 nach demselben Prinzip sortiert. Wieder wird ein Pivot gewählt (hier die 9), und die kleineren und größeren Zahlen werden auf die jeweils richtige Seite gebracht, bis das gesamte Array sortiert ist.&lt;br /&gt;
&lt;br /&gt;
== Pseudocode ==&lt;br /&gt;
Der folgende Pseudocode illustriert die Arbeitsweise des [[Algorithmus]]. Bei jedem Aufruf von &amp;lt;code&amp;gt;quicksort(...)&amp;lt;/code&amp;gt; gibt &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt; den Index des ersten Elements in der Teilliste an und &amp;lt;code&amp;gt;rechts&amp;lt;/code&amp;gt; den des letzten. Beim ersten Aufruf (oberste Rekursionsebene) ist &amp;lt;code&amp;gt;links = 0&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;rechts = n-1&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
funktion quicksort(links, rechts) &lt;br /&gt;
    falls links &amp;lt; rechts dann &lt;br /&gt;
        // teile() ordnet das Array um und liefert die finale Position des Pivots&lt;br /&gt;
        teiler := teile(links, rechts) &lt;br /&gt;
        &lt;br /&gt;
        // Rekursiver Aufruf für die linke Teilliste&lt;br /&gt;
        quicksort(links, teiler - 1) &lt;br /&gt;
        &lt;br /&gt;
        // Rekursiver Aufruf für die rechte Teilliste&lt;br /&gt;
        quicksort(teiler + 1, rechts) &lt;br /&gt;
    ende&lt;br /&gt;
ende&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wahl des Pivot-Elements ==&lt;br /&gt;
Die Effizienz von Quicksort steht und fällt mit der Wahl des Pivot-Elements. &lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schlechteste Wahl:&#039;&#039;&#039; Das Pivot-Element ist zufällig immer das kleinste oder größte Element der Teilliste. Dann wird die Liste extrem ungleichmäßig geteilt (eine Liste mit 0 Elementen, eine mit &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Elementen).&lt;br /&gt;
* &#039;&#039;&#039;Beste Wahl:&#039;&#039;&#039; Das Pivot-Element ist genau der [[Median]] (der Wert, der exakt in der Mitte der sortierten Liste stehen würde). Die Liste wird in zwei exakt gleich große Hälften geteilt. Da der Median in unsortierten Daten aber aufwendig zu suchen ist, behilft man sich mit Tricks.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Warum das erste Element oft eine schlechte Wahl ist:&#039;&#039;&#039;&lt;br /&gt;
Wenn man immer stur das erste Element als Pivot wählt (wie in unserem Beispiel) und die Eingabeliste ist bereits (oder fast) fertig sortiert, trifft automatisch immer der oben beschriebene schlechteste Fall ein. Der Algorithmus wird dadurch extrem langsam.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Lösungsansätze für die Praxis:&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;Randomisierung:&#039;&#039;&#039; Man wählt ein zufälliges Element aus dem Intervall als Pivot.&lt;br /&gt;
# &#039;&#039;&#039;Median-of-Three:&#039;&#039;&#039; Man betrachtet das erste, das mittlere und das letzte Element der Teilliste und wählt von diesen dreien den mittleren Wert als Pivot. Dies schützt sehr gut vor bereits vorsortierten Listen.&lt;br /&gt;
&lt;br /&gt;
== Java Implementierung ==&lt;br /&gt;
Bisher wurde die allgemeine Funktionsweise beschrieben. Am besten lassen sich komplexe [[Algorithmus|Algorithmen]] nachvollziehen, wenn man sie anhand einer Implementierung mit Hilfe eines [[Debugger|Debuggers]] analysiert. In der folgenden [[Java]]-Implementierung wird ein [[Array]] aus Ganzzahlen (&amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;) sortiert. &lt;br /&gt;
&lt;br /&gt;
Zur Abwechslung wird in dieser Implementierung immer das &#039;&#039;&#039;letzte&#039;&#039;&#039; Element des aktuellen Bereichs (&amp;lt;code&amp;gt;array[rechts]&amp;lt;/code&amp;gt;) als Pivot gewählt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void quickSort(int array[], int links, int rechts) {&lt;br /&gt;
      if (links &amp;lt; rechts) {&lt;br /&gt;
         // Teile das Array und finde die Position des Pivot-Elements&lt;br /&gt;
         int i = teile(array, links, rechts);&lt;br /&gt;
         &lt;br /&gt;
         // Sortiere den linken Teil&lt;br /&gt;
         quickSort(array, links, i - 1);&lt;br /&gt;
         // Sortiere den rechten Teil&lt;br /&gt;
         quickSort(array, i + 1, rechts);&lt;br /&gt;
      }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public int teile(int array[], int links, int rechts) {&lt;br /&gt;
      int pivot, i, j, help;&lt;br /&gt;
      pivot = array[rechts]; // Wir wählen das rechte Randelement als Pivot               &lt;br /&gt;
      i = links;&lt;br /&gt;
      j = rechts - 1;&lt;br /&gt;
      &lt;br /&gt;
      // Von links und rechts zur Mitte laufen, bis sich i und j kreuzen&lt;br /&gt;
      while (i &amp;lt;= j) {&lt;br /&gt;
         // Suche von links ein Element, das größer als das Pivot ist&lt;br /&gt;
         if (array[i] &amp;gt; pivot) {     &lt;br /&gt;
            // Tausche array[i] mit array[j], um es in den rechten Bereich zu bringen&lt;br /&gt;
            help = array[i]; &lt;br /&gt;
            array[i] = array[j]; &lt;br /&gt;
            array[j] = help;                             &lt;br /&gt;
            j--; // j einen Schritt weiter nach links schieben&lt;br /&gt;
         } else {&lt;br /&gt;
            i++; // i war kleiner/gleich Pivot, ist also korrekt positioniert -&amp;gt; weiter nach rechts&lt;br /&gt;
         }          &lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      // Das Pivot-Element an seine endgültige Position (i) tauschen&lt;br /&gt;
      help = array[i];&lt;br /&gt;
      array[i] = array[rechts];&lt;br /&gt;
      array[rechts] = help;&lt;br /&gt;
      &lt;br /&gt;
      // Rückgabe der finalen Position des Pivot-Elements&lt;br /&gt;
      return i;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laufzeitanalyse ==&lt;br /&gt;
&lt;br /&gt;
=== Worst-Case (Schlechtester Fall) ===&lt;br /&gt;
[[Datei:Baum N-Ebenen.png|mini|Worst-Case: Entarteter Baum]]&lt;br /&gt;
Betrachten wir zunächst die Worst-Case-[[Laufzeitanalyse]]. Angenommen, wir haben extremes Pech und die Partitionierung ist maximal unausgeglichen. Das gewählte Pivot ist immer das absolut kleinste oder größte Element. Dann enthält eine der Partitionen 0 Elemente und die andere Partition &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Elemente. &lt;br /&gt;
&lt;br /&gt;
Der Aufwand für das Vergleichen (Partitionieren) einer Ebene mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen ist proportional zu &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Nennen wir diesen Aufwand &amp;lt;math&amp;gt;c \cdot n&amp;lt;/math&amp;gt; (wobei &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; eine Konstante für die Dauer eines Vergleichs ist).&lt;br /&gt;
Auf der nächsten Ebene müssen wir &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Elemente vergleichen, der Aufwand ist &amp;lt;math&amp;gt;c \cdot (n-1)&amp;lt;/math&amp;gt;. Danach &amp;lt;math&amp;gt;c \cdot (n-2)&amp;lt;/math&amp;gt; und so weiter.&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Partitionierungszeiten für alle Ebenen aufsummieren, erhalten wir folgende Reihe:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot n + c \cdot (n - 1) + c \cdot (n - 2) + \dots + c \cdot 2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir klammern &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; aus und erkennen die bekannte [[Arithmetische-reihe|Gaußsche Summenformel]]:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot (n + (n-1) + (n-2) + \dots + 2) \approx c \cdot \frac{n(n+1)}{2} = \frac{c}{2}n^2 + \frac{c}{2}n&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Da in der asymtotischen Laufzeitanalyse konstante Faktoren und kleinere Terme (wie &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) ignoriert werden, wächst der Aufwand quadratisch. &lt;br /&gt;
&#039;&#039;&#039;Fazit Worst-Case:&#039;&#039;&#039; Quicksort hat im schlechtesten Fall eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Best-Case (Bester Fall) ===&lt;br /&gt;
[[Datei:Verzweigungsbaum Quicksort symetrisch.png|mini|Best-Case: Symmetrischer Baum]]&lt;br /&gt;
Der beste Fall liegt vor, wenn das Pivot-Element die Liste immer exakt in der Mitte teilt. Die Teillisten sind dann immer gleich groß.&lt;br /&gt;
[[Datei:SubArrays symetrisch Quicksort.png|mini|Halbierung der Arraygrößen]]&lt;br /&gt;
&lt;br /&gt;
Wird die Anzahl der zu sortierenden Elemente &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; verdoppelt, kommt durch die fortlaufende Halbierung lediglich &#039;&#039;&#039;eine einzige neue Rekursionsebene&#039;&#039;&#039; (ein Baum-Level) hinzu. Dieses Wachstumsverhalten (wie oft kann ich &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; durch 2 teilen, bis 1 übrig bleibt?) wird mathematisch durch den Logarithmus zur Basis 2 ausgedrückt: &amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Zu sortierende Elemente &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 4&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 8&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 16&lt;br /&gt;
| style=&amp;quot;color:green&amp;quot; | 32&lt;br /&gt;
|-&lt;br /&gt;
! Als Zweierpotenz&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^3}&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^4}&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;\color{green}{2^5}&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Anzahl Rekursionsebenen &amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 2&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 3&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 4&lt;br /&gt;
| style=&amp;quot;color:red&amp;quot; | 5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wir haben also insgesamt &amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt; Ebenen. Auf &#039;&#039;&#039;jeder einzelnen Ebene&#039;&#039;&#039; müssen in der Summe &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elemente betrachtet und mit dem jeweiligen Pivot verglichen werden. &lt;br /&gt;
Wir multiplizieren also die Arbeit pro Ebene (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) mit der Anzahl der Ebenen (&amp;lt;math&amp;gt;\log_2(n)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&#039;&#039;&#039;Fazit Best-Case:&#039;&#039;&#039; Die Laufzeit beträgt &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Average-Case (Durchschnittlicher Fall) ===&lt;br /&gt;
In der Praxis haben wir selten den perfekten Best-Case, aber fast nie den absoluten Worst-Case. Meistens teilt das Pivot die Liste beispielsweise im Verhältnis 30:70 oder 60:40. &lt;br /&gt;
&lt;br /&gt;
Um den Average-Case mathematisch exakt zu beweisen, greifen wir auf das Prinzip der &#039;&#039;&#039;Indikatorvariablen&#039;&#039;&#039; und der &#039;&#039;&#039;Harmonischen Reihe&#039;&#039;&#039; zurück. Wir fragen uns: &#039;&#039;Wie hoch ist die Wahrscheinlichkeit, dass zwei beliebige Elemente (nennen wir sie &amp;lt;math&amp;gt;z_i&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;z_j&amp;lt;/math&amp;gt;) im Laufe des Algorithmus miteinander verglichen werden?&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Zwei Elemente werden genau dann miteinander verglichen, wenn eines der beiden als Pivot-Element ausgewählt wird, &#039;&#039;&#039;bevor&#039;&#039;&#039; irgendein Element, dessen Wert zwischen den beiden liegt, als Pivot gewählt wird.&lt;br /&gt;
* Sind die Daten völlig zufällig verteilt, ist die Wahrscheinlichkeit, dass von &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; Elementen genau das größte oder kleinste zuerst als Pivot gewählt wird, exakt &amp;lt;math&amp;gt;\frac{2}{k}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Summieren wir diese Wahrscheinlichkeiten (Erwartungswert) für alle Elementpaare im Array auf, erhalten wir folgende Doppel-Summe:&lt;br /&gt;
&amp;lt;math&amp;gt;E(X) = \sum_{i=1}^{n-1} \sum_{j=i+1}^{n} \frac{2}{j - i + 1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Setzt man für den Abstand &amp;lt;math&amp;gt;(j - i + 1)&amp;lt;/math&amp;gt; eine neue Variable &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; ein, löst sich die innere Summe auf zu:&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_{k=2}^{n} \frac{2}{k} = 2 \cdot \left( \frac{1}{2} + \frac{1}{3} + \dots + \frac{1}{n} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier erkennen wir exakt die &#039;&#039;&#039;Harmonische Reihe&#039;&#039;&#039; (&amp;lt;math&amp;gt;H_n&amp;lt;/math&amp;gt;) wieder, die wir bereits bei der Maximumsuche kennengelernt haben! Da die Harmonische Reihe für große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; gegen den natürlichen Logarithmus &amp;lt;math&amp;gt;\ln(n)&amp;lt;/math&amp;gt; konvergiert, ergibt die innere Summe ungefähr &amp;lt;math&amp;gt;2 \cdot \ln(n)&amp;lt;/math&amp;gt;. Da die äußere Summe &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-mal läuft, erhalten wir insgesamt rund &amp;lt;math&amp;gt;2n \cdot \ln(n)&amp;lt;/math&amp;gt; Vergleiche. &lt;br /&gt;
&lt;br /&gt;
Da Konstanten wie der Faktor 2 beim Groß-O ignoriert werden, ist nun stochastisch bewiesen: &lt;br /&gt;
&#039;&#039;&#039;Fazit Average-Case:&#039;&#039;&#039; Auch bei zufälliger Verteilung konvergiert Quicksort dank des logarithmischen Wachstums der harmonischen Reihe sicher gegen die Komplexitätsklasse &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;br /&gt;
[[Kategorie:FI_I_TP2]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2825</id>
		<title>Laufzeitanalyse</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2825"/>
		<updated>2026-04-20T07:35:10Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]]&lt;br /&gt;
Die Laufzeitanalyse untersucht die sogenannte Zeitkomplexität eines [[Algorithmus]]. Man stellt sich dabei im Kern die Frage: &#039;&#039;&#039;„Wie verhält sich die Rechenzeit, wenn die Menge der zu verarbeitenden Daten extrem groß wird?“&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Da die echte Ausführungszeit (in Sekunden) stark vom jeweiligen Computer abhängt, misst man stattdessen die Anzahl der elementaren [[Anweisung|Befehle]] (z. B. Vergleiche oder Tauschoperationen). Diese Anzahl hängt von der Größe der Eingabemenge ab, die in der Informatik meist als &#039;&#039;&#039;Problemgröße &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&#039;&#039;&#039; bezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
Für kleine Datenmengen (z. B. eine Liste mit 10 Elementen sortieren) ist fast jeder Algorithmus schnell genug. Spannend wird es erst bei sehr großen Datenmengen (z. B. Millionen von Kundendatensätzen). Hier betrachtet man die sogenannte asymptotische [[Programmausführung|Laufzeit]]. Sie beschreibt das Verhalten des Algorithmus, wenn die Datenmenge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; theoretisch ins Unendliche wächst.&lt;br /&gt;
&lt;br /&gt;
Lässt sich die Laufzeit eines Algorithmus mathematisch durch eine Funktion beschreiben, wie beispielsweise:&lt;br /&gt;
&amp;lt;math&amp;gt;f(x) = \frac{1}{x} + x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So nähert sich die rot dargestellte Funktion &amp;lt;math&amp;gt;\color{red}{f(x)}&amp;lt;/math&amp;gt; für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; der grün dargestellten Asymptote &amp;lt;math&amp;gt;\color{green}{g(x) = x}&amp;lt;/math&amp;gt; an.&lt;br /&gt;
&lt;br /&gt;
Die Laufzeit wird mit der sogenannten Landau-Notation (Groß-O-Notation, z. B. &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;) abgeschätzt. Dabei werden konstante Vorfaktoren (wie &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt;) oder kleinere Summanden ignoriert, da bei riesigen Datenmengen nur noch der dominierende Teil der Funktion das Wachstum bestimmt.&lt;br /&gt;
&lt;br /&gt;
=== Was die Laufzeitanalyse NICHT ist ===&lt;br /&gt;
Sie misst &#039;&#039;&#039;nicht&#039;&#039;&#039; den tatsächlichen Zeitaufwand in Millisekunden auf einem echten Computer. Sie ist ein theoretisches Modell, um Algorithmen hardwareunabhängig miteinander vergleichen zu können.&lt;br /&gt;
&lt;br /&gt;
== Szenarien der Laufzeitabschätzung ==&lt;br /&gt;
Da Algorithmen je nach Vorsortierung der Daten unterschiedlich schnell arbeiten, unterscheidet man drei klassische Fälle:&lt;br /&gt;
&lt;br /&gt;
* Das &#039;&#039;&#039;Worst-Case-Szenario&#039;&#039;&#039; (Schlechtester Fall): Die Daten liegen so ungünstig vor, dass der Algorithmus maximal lange braucht. Dies liefert eine absolute Sicherheitsgarantie (obere Schranke).&lt;br /&gt;
* Das &#039;&#039;&#039;Best-Case-Szenario&#039;&#039;&#039; (Bester Fall): Die Daten liegen optimal vor (z. B. bereits sortiert). Der Algorithmus ist minimal schnell fertig.&lt;br /&gt;
* Das &#039;&#039;&#039;Average-Case-Szenario&#039;&#039;&#039; (Durchschnittlicher Fall): Beschreibt die zu erwartende Laufzeit bei völlig zufällig gemischten Daten. Dies ist oft das realistischste Szenario in der Praxis.&lt;br /&gt;
&lt;br /&gt;
== Mathematische Berechnung des Average-Case ==&lt;br /&gt;
Oft wird fälschlicherweise angenommen, der Average-Case sei einfach „die Hälfte des Worst-Case“. Das ist mathematisch ungenau. Tatsächlich berechnet man den Average-Case mit Hilfe der Stochastik: Er entspricht dem &#039;&#039;&#039;Erwartungswert&#039;&#039;&#039; der Laufzeit.&lt;br /&gt;
&lt;br /&gt;
Man berechnet ihn, indem man alle möglichen Eingaben durchspielt und gewichtet. Die Formel für den Erwartungswert &amp;lt;math&amp;gt;E(T)&amp;lt;/math&amp;gt; lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \sum_{i=1}^{|E|} p_i \cdot t_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; ist die Wahrscheinlichkeit für einen bestimmten Fall &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; ist die Anzahl der Rechenschritte, die der Algorithmus in diesem Fall braucht.&lt;br /&gt;
&lt;br /&gt;
Für die Schule gehen wir meistens von einer &#039;&#039;&#039;Gleichverteilung&#039;&#039;&#039; aus: Jeder Fall tritt mit derselben Wahrscheinlichkeit auf.&lt;br /&gt;
&lt;br /&gt;
=== Average-Case der Linearen Suche ===&lt;br /&gt;
Wir suchen eine bestimmte Zahl in einem Array der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Wir nehmen an, die Zahl ist im Array und jede Position ist gleich wahrscheinlich.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schritt 1: Wahrscheinlichkeit bestimmen.&#039;&#039;&#039; Die Wahrscheinlichkeit, dass unsere Zahl an Position 1, 2 oder &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; steht, ist jeweils &amp;lt;math&amp;gt;p = \frac{1}{n}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 2: Schritte zählen.&#039;&#039;&#039; Steht die Zahl an Position 1, brauchen wir 1 Vergleich. An Position 2 brauchen wir 2 Vergleiche. An Position &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; brauchen wir &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Vergleiche.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 3: Erwartungswert berechnen.&#039;&#039;&#039; Wir setzen dies in die Formel ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n}\cdot 1 + \frac{1}{n}\cdot 2 + \dots + \frac{1}{n}\cdot n = \sum_{i=1}^{n} \left( \frac{1}{n} \cdot i \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir klammern &amp;lt;math&amp;gt;\frac{1}{n}&amp;lt;/math&amp;gt; aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \sum_{i=1}^{n} i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Summe der Zahlen von 1 bis &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; lässt sich mit der &#039;&#039;&#039;Gaußschen Summenformel&#039;&#039;&#039; (kleiner Gauß) vereinfachen zu &amp;lt;math&amp;gt;\frac{n(n+1)}{2}&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \frac{n(n+1)}{2} = \frac{n+1}{2} = \frac{1}{2}n + \frac{1}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fazit:&#039;&#039;&#039; Im Durchschnitt braucht die lineare Suche &amp;lt;math&amp;gt;\frac{n+1}{2}&amp;lt;/math&amp;gt; Vergleiche. Da in der &amp;lt;math&amp;gt;\mathcal{O}&amp;lt;/math&amp;gt;-Notation Konstanten wie &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; wegfallen, gehört die lineare Suche im Durchschnitt zur Klasse &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Maximumsuche ===&lt;br /&gt;
Bei komplexeren Algorithmen wie [[Quicksort]] reicht einfaches Zählen nicht mehr aus. Hier arbeiten wir mit einem mathematischen Trick: &#039;&#039;&#039;Indikatorvariablen&#039;&#039;&#039; und der &#039;&#039;&#039;Harmonischen Reihe&#039;&#039;&#039;. Um das zu verstehen, analysieren wir einen simplen Code zur Maximumsuche:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public int findMax(int[] A) {&lt;br /&gt;
    int max = A[0];&lt;br /&gt;
    for (int i = 1; i &amp;lt; A.length; i++) {&lt;br /&gt;
        if (A[i] &amp;gt; max) {&lt;br /&gt;
            max = A[i]; // Update-Operation&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return max;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Vergleiche in der &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung werden immer &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Mal ausgeführt. Aber wie oft wird die Variable &amp;lt;code&amp;gt;max&amp;lt;/code&amp;gt; im Durchschnitt überschrieben (Update-Operation)?&lt;br /&gt;
&lt;br /&gt;
Wir stellen uns vor, das Array enthält völlig zufällig gemischte Zahlen.&lt;br /&gt;
* &#039;&#039;&#039;Intuitiver Ansatz:&#039;&#039;&#039; &lt;br /&gt;
Das 1. Element ist automatisch das bisherige Maximum (Wahrscheinlichkeit: 1). &lt;br /&gt;
Damit das 2. Element ein neues Maximum ist, muss es größer als das erste sein. Die Chance dafür ist exakt 50% (&amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt;). &lt;br /&gt;
Damit das 3. Element ein neues Maximum ist, muss es das Größte der ersten drei Elemente sein. Die Chance dafür ist &amp;lt;math&amp;gt;\frac{1}{3}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Mathematischer Ansatz (Indikatorvariablen):&#039;&#039;&#039;&lt;br /&gt;
Wir definieren einen „Schalter“ (die Indikatorvariable) &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt;. Er ist &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;, wenn ein neues Maximum an Stelle &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; gefunden wird, sonst &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Die Wahrscheinlichkeit dafür ist &amp;lt;math&amp;gt;P(X_i = 1) = \frac{1}{i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Um die durchschnittliche Gesamtzahl der Updates zu finden, addieren wir einfach diese Wahrscheinlichkeiten auf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E[X] = \sum_{i=1}^{n} \frac{1}{i} = 1 + \frac{1}{2} + \frac{1}{3} + \frac{1}{4} + \dots + \frac{1}{n}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese spezielle Summe nennt man in der Mathematik die &#039;&#039;&#039;Harmonische Reihe&#039;&#039;&#039; (&amp;lt;math&amp;gt;H_n&amp;lt;/math&amp;gt;). Sie wächst extrem langsam. Für große Zahlen &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; entspricht sie in etwa dem &#039;&#039;&#039;natürlichen Logarithmus&#039;&#039;&#039; (&amp;lt;math&amp;gt;\ln(n)&amp;lt;/math&amp;gt;). &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Erkenntnis für Quicksort:&#039;&#039;&#039; Obwohl das Array &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elemente hat, wird die Update-Operation im Durchschnitt nur logarithmisch oft (&amp;lt;math&amp;gt;\mathcal{O}(\log n)&amp;lt;/math&amp;gt;) ausgeführt. Genau dieses stochastische Prinzip (das Aufsummieren von Einzelwahrscheinlichkeiten, die zu einem Logarithmus führen) ist der Schlüssel, um später selbst zu beweisen, dass Quicksort im Average-Case bei &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt; liegt.&lt;br /&gt;
&lt;br /&gt;
== Laufzeitanalyse von Insertion Sort ==&lt;br /&gt;
Nun wenden wir unser Wissen auf das Sortierverfahren [[Insertion-sort|Insertion Sort]] (Sortieren durch Einfügen) an. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void sortierenEinfuegen_MC() {&lt;br /&gt;
    int v, i, j;&lt;br /&gt;
    for (i = 1; i &amp;lt; zSortfeld.length; i++) {&lt;br /&gt;
        c++; // Zähler für Vergleiche&lt;br /&gt;
        if (zSortfeld[i] &amp;lt; zSortfeld[i - 1]) {&lt;br /&gt;
            v = zSortfeld[i];&lt;br /&gt;
            j = i;&lt;br /&gt;
            do {&lt;br /&gt;
                zSortfeld[j] = zSortfeld[j - 1];&lt;br /&gt;
                j--;&lt;br /&gt;
            } while ((j &amp;gt; 0) &amp;amp;&amp;amp; (zSortfeld[j - 1] &amp;gt; v));&lt;br /&gt;
            zSortfeld[j] = v; &lt;br /&gt;
        }&lt;br /&gt;
        printAnalyse(i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir analysieren eine Liste mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen. Den Zeitverbrauch für einen einzelnen Vergleich nennen wir &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 1. Best-Case-Szenario (Bester Fall) ===&lt;br /&gt;
Die Liste ist bereits perfekt aufsteigend sortiert (z. B. &amp;lt;code&amp;gt;[0, 2, 3, 5, 7]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Da jedes Element schon größer als sein Vorgänger ist, ist die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung immer falsch. Die innere &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife wird ignoriert.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Wir machen exakt einen Vergleich pro Element in der äußeren Schleife. Das ergibt &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt; Vergleiche.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Der Aufwand wächst wie eine Gerade (linear). Die Komplexität ist &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 2. Worst-Case-Szenario (Schlechtester Fall) ===&lt;br /&gt;
Die Liste ist exakt falsch herum sortiert (z. B. &amp;lt;code&amp;gt;[7, 5, 3, 2, 0]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung ist immer wahr. Jedes Element muss durch die innere Schleife komplett bis an den Anfang der Liste getauscht werden.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Beim 1. Durchlauf machen wir 1 Vergleich. Beim 2. Durchlauf 2 Vergleiche. Beim letzten Durchlauf &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Vergleiche. Wir müssen also wieder addieren: &amp;lt;math&amp;gt;1 + 2 + 3 + \dots + (n-1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Wie bei der linearen Suche hilft uns hier die Gaußsche Summenformel:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot \frac{(n - 1) \cdot n}{2} = \frac{c}{2}n^2 - \frac{c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Asymptote an quadratische Funktion2.png|mini|Quadratisches Wachstum]]&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Für extrem große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; dominiert das &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; alles andere. Wir ignorieren den Faktor &amp;lt;math&amp;gt;\frac{c}{2}&amp;lt;/math&amp;gt; und den kleineren Teil &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Das Wachstum ist quadratisch, also &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 3. Average-Case-Szenario (Durchschnittlicher Fall) ===&lt;br /&gt;
Die Werte im Array sind völlig zufällig verteilt.&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Im Durchschnitt müssen wir ein Element nicht bis ganz an den Anfang schieben (wie im Worst-Case), sondern nur bis zur &#039;&#039;&#039;Hälfte&#039;&#039;&#039; des bisher sortierten Bereichs.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Die Anzahl der Vergleiche halbiert sich grob im Vergleich zum Worst-Case. Der Aufwand liegt bei ca. &amp;lt;math&amp;gt;\frac{1}{2} \cdot \frac{c}{2}n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Wie wir gelernt haben, ignoriert die Groß-O-Notation konstante Brüche (wie &amp;lt;math&amp;gt;\frac{1}{4}&amp;lt;/math&amp;gt;). Es bleibt bei der höchsten Potenz: Das Wachstum ist und bleibt quadratisch. Die Komplexität lautet also weiterhin &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2824</id>
		<title>Laufzeitanalyse</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2824"/>
		<updated>2026-04-20T07:34:36Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: /* Beispiel 2: Vorbereitung auf Quicksort (Maximumsuche) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]]&lt;br /&gt;
Die Laufzeitanalyse untersucht die sogenannte Zeitkomplexität eines [[Algorithmus]]. Man stellt sich dabei im Kern die Frage: &#039;&#039;&#039;„Wie verhält sich die Rechenzeit, wenn die Menge der zu verarbeitenden Daten extrem groß wird?“&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Da die echte Ausführungszeit (in Sekunden) stark vom jeweiligen Computer abhängt, misst man stattdessen die Anzahl der elementaren [[Anweisung|Befehle]] (z. B. Vergleiche oder Tauschoperationen). Diese Anzahl hängt von der Größe der Eingabemenge ab, die in der Informatik meist als &#039;&#039;&#039;Problemgröße &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&#039;&#039;&#039; bezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
Für kleine Datenmengen (z. B. eine Liste mit 10 Elementen sortieren) ist fast jeder Algorithmus schnell genug. Spannend wird es erst bei sehr großen Datenmengen (z. B. Millionen von Kundendatensätzen). Hier betrachtet man die sogenannte asymptotische [[Programmausführung|Laufzeit]]. Sie beschreibt das Verhalten des Algorithmus, wenn die Datenmenge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; theoretisch ins Unendliche wächst.&lt;br /&gt;
&lt;br /&gt;
Lässt sich die Laufzeit eines Algorithmus mathematisch durch eine Funktion beschreiben, wie beispielsweise:&lt;br /&gt;
&amp;lt;math&amp;gt;f(x) = \frac{1}{x} + x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So nähert sich die rot dargestellte Funktion &amp;lt;math&amp;gt;\color{red}{f(x)}&amp;lt;/math&amp;gt; für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; der grün dargestellten Asymptote &amp;lt;math&amp;gt;\color{green}{g(x) = x}&amp;lt;/math&amp;gt; an.&lt;br /&gt;
&lt;br /&gt;
Die Laufzeit wird mit der sogenannten Landau-Notation (Groß-O-Notation, z. B. &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;) abgeschätzt. Dabei werden konstante Vorfaktoren (wie &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt;) oder kleinere Summanden ignoriert, da bei riesigen Datenmengen nur noch der dominierende Teil der Funktion das Wachstum bestimmt.&lt;br /&gt;
&lt;br /&gt;
=== Was die Laufzeitanalyse NICHT ist ===&lt;br /&gt;
Sie misst &#039;&#039;&#039;nicht&#039;&#039;&#039; den tatsächlichen Zeitaufwand in Millisekunden auf einem echten Computer. Sie ist ein theoretisches Modell, um Algorithmen hardwareunabhängig miteinander vergleichen zu können.&lt;br /&gt;
&lt;br /&gt;
== Szenarien der Laufzeitabschätzung ==&lt;br /&gt;
Da Algorithmen je nach Vorsortierung der Daten unterschiedlich schnell arbeiten, unterscheidet man drei klassische Fälle:&lt;br /&gt;
&lt;br /&gt;
* Das &#039;&#039;&#039;Worst-Case-Szenario&#039;&#039;&#039; (Schlechtester Fall): Die Daten liegen so ungünstig vor, dass der Algorithmus maximal lange braucht. Dies liefert eine absolute Sicherheitsgarantie (obere Schranke).&lt;br /&gt;
* Das &#039;&#039;&#039;Best-Case-Szenario&#039;&#039;&#039; (Bester Fall): Die Daten liegen optimal vor (z. B. bereits sortiert). Der Algorithmus ist minimal schnell fertig.&lt;br /&gt;
* Das &#039;&#039;&#039;Average-Case-Szenario&#039;&#039;&#039; (Durchschnittlicher Fall): Beschreibt die zu erwartende Laufzeit bei völlig zufällig gemischten Daten. Dies ist oft das realistischste Szenario in der Praxis.&lt;br /&gt;
&lt;br /&gt;
== Mathematische Berechnung des Average-Case ==&lt;br /&gt;
Oft wird fälschlicherweise angenommen, der Average-Case sei einfach „die Hälfte des Worst-Case“. Das ist mathematisch ungenau. Tatsächlich berechnet man den Average-Case mit Hilfe der Stochastik: Er entspricht dem &#039;&#039;&#039;Erwartungswert&#039;&#039;&#039; der Laufzeit.&lt;br /&gt;
&lt;br /&gt;
Man berechnet ihn, indem man alle möglichen Eingaben durchspielt und gewichtet. Die Formel für den Erwartungswert &amp;lt;math&amp;gt;E(T)&amp;lt;/math&amp;gt; lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \sum_{i=1}^{|E|} p_i \cdot t_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; ist die Wahrscheinlichkeit für einen bestimmten Fall &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; ist die Anzahl der Rechenschritte, die der Algorithmus in diesem Fall braucht.&lt;br /&gt;
&lt;br /&gt;
Für die Schule gehen wir meistens von einer &#039;&#039;&#039;Gleichverteilung&#039;&#039;&#039; aus: Jeder Fall tritt mit derselben Wahrscheinlichkeit auf.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel 1: Average-Case der Linearen Suche ===&lt;br /&gt;
Wir suchen eine bestimmte Zahl in einem Array der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Wir nehmen an, die Zahl ist im Array und jede Position ist gleich wahrscheinlich.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schritt 1: Wahrscheinlichkeit bestimmen.&#039;&#039;&#039; Die Wahrscheinlichkeit, dass unsere Zahl an Position 1, 2 oder &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; steht, ist jeweils &amp;lt;math&amp;gt;p = \frac{1}{n}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 2: Schritte zählen.&#039;&#039;&#039; Steht die Zahl an Position 1, brauchen wir 1 Vergleich. An Position 2 brauchen wir 2 Vergleiche. An Position &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; brauchen wir &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Vergleiche.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 3: Erwartungswert berechnen.&#039;&#039;&#039; Wir setzen dies in die Formel ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n}\cdot 1 + \frac{1}{n}\cdot 2 + \dots + \frac{1}{n}\cdot n = \sum_{i=1}^{n} \left( \frac{1}{n} \cdot i \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir klammern &amp;lt;math&amp;gt;\frac{1}{n}&amp;lt;/math&amp;gt; aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \sum_{i=1}^{n} i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Summe der Zahlen von 1 bis &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; lässt sich mit der &#039;&#039;&#039;Gaußschen Summenformel&#039;&#039;&#039; (kleiner Gauß) vereinfachen zu &amp;lt;math&amp;gt;\frac{n(n+1)}{2}&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \frac{n(n+1)}{2} = \frac{n+1}{2} = \frac{1}{2}n + \frac{1}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fazit:&#039;&#039;&#039; Im Durchschnitt braucht die lineare Suche &amp;lt;math&amp;gt;\frac{n+1}{2}&amp;lt;/math&amp;gt; Vergleiche. Da in der &amp;lt;math&amp;gt;\mathcal{O}&amp;lt;/math&amp;gt;-Notation Konstanten wie &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; wegfallen, gehört die lineare Suche im Durchschnitt zur Klasse &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Maximumsuche ===&lt;br /&gt;
Bei komplexeren Algorithmen wie [[Quicksort]] reicht einfaches Zählen nicht mehr aus. Hier arbeiten wir mit einem mathematischen Trick: &#039;&#039;&#039;Indikatorvariablen&#039;&#039;&#039; und der &#039;&#039;&#039;Harmonischen Reihe&#039;&#039;&#039;. Um das zu verstehen, analysieren wir einen simplen Code zur Maximumsuche:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public int findMax(int[] A) {&lt;br /&gt;
    int max = A[0];&lt;br /&gt;
    for (int i = 1; i &amp;lt; A.length; i++) {&lt;br /&gt;
        if (A[i] &amp;gt; max) {&lt;br /&gt;
            max = A[i]; // Update-Operation&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return max;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Vergleiche in der &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung werden immer &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Mal ausgeführt. Aber wie oft wird die Variable &amp;lt;code&amp;gt;max&amp;lt;/code&amp;gt; im Durchschnitt überschrieben (Update-Operation)?&lt;br /&gt;
&lt;br /&gt;
Wir stellen uns vor, das Array enthält völlig zufällig gemischte Zahlen.&lt;br /&gt;
* &#039;&#039;&#039;Intuitiver Ansatz:&#039;&#039;&#039; &lt;br /&gt;
Das 1. Element ist automatisch das bisherige Maximum (Wahrscheinlichkeit: 1). &lt;br /&gt;
Damit das 2. Element ein neues Maximum ist, muss es größer als das erste sein. Die Chance dafür ist exakt 50% (&amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt;). &lt;br /&gt;
Damit das 3. Element ein neues Maximum ist, muss es das Größte der ersten drei Elemente sein. Die Chance dafür ist &amp;lt;math&amp;gt;\frac{1}{3}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Mathematischer Ansatz (Indikatorvariablen):&#039;&#039;&#039;&lt;br /&gt;
Wir definieren einen „Schalter“ (die Indikatorvariable) &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt;. Er ist &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;, wenn ein neues Maximum an Stelle &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; gefunden wird, sonst &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Die Wahrscheinlichkeit dafür ist &amp;lt;math&amp;gt;P(X_i = 1) = \frac{1}{i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Um die durchschnittliche Gesamtzahl der Updates zu finden, addieren wir einfach diese Wahrscheinlichkeiten auf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E[X] = \sum_{i=1}^{n} \frac{1}{i} = 1 + \frac{1}{2} + \frac{1}{3} + \frac{1}{4} + \dots + \frac{1}{n}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese spezielle Summe nennt man in der Mathematik die &#039;&#039;&#039;Harmonische Reihe&#039;&#039;&#039; (&amp;lt;math&amp;gt;H_n&amp;lt;/math&amp;gt;). Sie wächst extrem langsam. Für große Zahlen &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; entspricht sie in etwa dem &#039;&#039;&#039;natürlichen Logarithmus&#039;&#039;&#039; (&amp;lt;math&amp;gt;\ln(n)&amp;lt;/math&amp;gt;). &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Erkenntnis für Quicksort:&#039;&#039;&#039; Obwohl das Array &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elemente hat, wird die Update-Operation im Durchschnitt nur logarithmisch oft (&amp;lt;math&amp;gt;\mathcal{O}(\log n)&amp;lt;/math&amp;gt;) ausgeführt. Genau dieses stochastische Prinzip (das Aufsummieren von Einzelwahrscheinlichkeiten, die zu einem Logarithmus führen) ist der Schlüssel, um später selbst zu beweisen, dass Quicksort im Average-Case bei &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt; liegt.&lt;br /&gt;
&lt;br /&gt;
== Beispiel 3: Laufzeitanalyse von Insertion Sort ==&lt;br /&gt;
Nun wenden wir unser Wissen auf das Sortierverfahren [[Insertion-sort|Insertion Sort]] (Sortieren durch Einfügen) an. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void sortierenEinfuegen_MC() {&lt;br /&gt;
    int v, i, j;&lt;br /&gt;
    for (i = 1; i &amp;lt; zSortfeld.length; i++) {&lt;br /&gt;
        c++; // Zähler für Vergleiche&lt;br /&gt;
        if (zSortfeld[i] &amp;lt; zSortfeld[i - 1]) {&lt;br /&gt;
            v = zSortfeld[i];&lt;br /&gt;
            j = i;&lt;br /&gt;
            do {&lt;br /&gt;
                zSortfeld[j] = zSortfeld[j - 1];&lt;br /&gt;
                j--;&lt;br /&gt;
            } while ((j &amp;gt; 0) &amp;amp;&amp;amp; (zSortfeld[j - 1] &amp;gt; v));&lt;br /&gt;
            zSortfeld[j] = v; &lt;br /&gt;
        }&lt;br /&gt;
        printAnalyse(i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir analysieren eine Liste mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen. Den Zeitverbrauch für einen einzelnen Vergleich nennen wir &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 1. Best-Case-Szenario (Bester Fall) ===&lt;br /&gt;
Die Liste ist bereits perfekt aufsteigend sortiert (z. B. &amp;lt;code&amp;gt;[0, 2, 3, 5, 7]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Da jedes Element schon größer als sein Vorgänger ist, ist die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung immer falsch. Die innere &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife wird ignoriert.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Wir machen exakt einen Vergleich pro Element in der äußeren Schleife. Das ergibt &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt; Vergleiche.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Der Aufwand wächst wie eine Gerade (linear). Die Komplexität ist &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 2. Worst-Case-Szenario (Schlechtester Fall) ===&lt;br /&gt;
Die Liste ist exakt falsch herum sortiert (z. B. &amp;lt;code&amp;gt;[7, 5, 3, 2, 0]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung ist immer wahr. Jedes Element muss durch die innere Schleife komplett bis an den Anfang der Liste getauscht werden.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Beim 1. Durchlauf machen wir 1 Vergleich. Beim 2. Durchlauf 2 Vergleiche. Beim letzten Durchlauf &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Vergleiche. Wir müssen also wieder addieren: &amp;lt;math&amp;gt;1 + 2 + 3 + \dots + (n-1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Wie bei der linearen Suche hilft uns hier die Gaußsche Summenformel:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot \frac{(n - 1) \cdot n}{2} = \frac{c}{2}n^2 - \frac{c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Asymptote an quadratische Funktion2.png|mini|Quadratisches Wachstum]]&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Für extrem große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; dominiert das &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; alles andere. Wir ignorieren den Faktor &amp;lt;math&amp;gt;\frac{c}{2}&amp;lt;/math&amp;gt; und den kleineren Teil &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Das Wachstum ist quadratisch, also &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 3. Average-Case-Szenario (Durchschnittlicher Fall) ===&lt;br /&gt;
Die Werte im Array sind völlig zufällig verteilt.&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Im Durchschnitt müssen wir ein Element nicht bis ganz an den Anfang schieben (wie im Worst-Case), sondern nur bis zur &#039;&#039;&#039;Hälfte&#039;&#039;&#039; des bisher sortierten Bereichs.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Die Anzahl der Vergleiche halbiert sich grob im Vergleich zum Worst-Case. Der Aufwand liegt bei ca. &amp;lt;math&amp;gt;\frac{1}{2} \cdot \frac{c}{2}n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Wie wir gelernt haben, ignoriert die Groß-O-Notation konstante Brüche (wie &amp;lt;math&amp;gt;\frac{1}{4}&amp;lt;/math&amp;gt;). Es bleibt bei der höchsten Potenz: Das Wachstum ist und bleibt quadratisch. Die Komplexität lautet also weiterhin &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2823</id>
		<title>Laufzeitanalyse</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2823"/>
		<updated>2026-04-20T07:34:12Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]]&lt;br /&gt;
Die Laufzeitanalyse untersucht die sogenannte Zeitkomplexität eines [[Algorithmus]]. Man stellt sich dabei im Kern die Frage: &#039;&#039;&#039;„Wie verhält sich die Rechenzeit, wenn die Menge der zu verarbeitenden Daten extrem groß wird?“&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Da die echte Ausführungszeit (in Sekunden) stark vom jeweiligen Computer abhängt, misst man stattdessen die Anzahl der elementaren [[Anweisung|Befehle]] (z. B. Vergleiche oder Tauschoperationen). Diese Anzahl hängt von der Größe der Eingabemenge ab, die in der Informatik meist als &#039;&#039;&#039;Problemgröße &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&#039;&#039;&#039; bezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
Für kleine Datenmengen (z. B. eine Liste mit 10 Elementen sortieren) ist fast jeder Algorithmus schnell genug. Spannend wird es erst bei sehr großen Datenmengen (z. B. Millionen von Kundendatensätzen). Hier betrachtet man die sogenannte asymptotische [[Programmausführung|Laufzeit]]. Sie beschreibt das Verhalten des Algorithmus, wenn die Datenmenge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; theoretisch ins Unendliche wächst.&lt;br /&gt;
&lt;br /&gt;
Lässt sich die Laufzeit eines Algorithmus mathematisch durch eine Funktion beschreiben, wie beispielsweise:&lt;br /&gt;
&amp;lt;math&amp;gt;f(x) = \frac{1}{x} + x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So nähert sich die rot dargestellte Funktion &amp;lt;math&amp;gt;\color{red}{f(x)}&amp;lt;/math&amp;gt; für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; der grün dargestellten Asymptote &amp;lt;math&amp;gt;\color{green}{g(x) = x}&amp;lt;/math&amp;gt; an.&lt;br /&gt;
&lt;br /&gt;
Die Laufzeit wird mit der sogenannten Landau-Notation (Groß-O-Notation, z. B. &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;) abgeschätzt. Dabei werden konstante Vorfaktoren (wie &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt;) oder kleinere Summanden ignoriert, da bei riesigen Datenmengen nur noch der dominierende Teil der Funktion das Wachstum bestimmt.&lt;br /&gt;
&lt;br /&gt;
=== Was die Laufzeitanalyse NICHT ist ===&lt;br /&gt;
Sie misst &#039;&#039;&#039;nicht&#039;&#039;&#039; den tatsächlichen Zeitaufwand in Millisekunden auf einem echten Computer. Sie ist ein theoretisches Modell, um Algorithmen hardwareunabhängig miteinander vergleichen zu können.&lt;br /&gt;
&lt;br /&gt;
== Szenarien der Laufzeitabschätzung ==&lt;br /&gt;
Da Algorithmen je nach Vorsortierung der Daten unterschiedlich schnell arbeiten, unterscheidet man drei klassische Fälle:&lt;br /&gt;
&lt;br /&gt;
* Das &#039;&#039;&#039;Worst-Case-Szenario&#039;&#039;&#039; (Schlechtester Fall): Die Daten liegen so ungünstig vor, dass der Algorithmus maximal lange braucht. Dies liefert eine absolute Sicherheitsgarantie (obere Schranke).&lt;br /&gt;
* Das &#039;&#039;&#039;Best-Case-Szenario&#039;&#039;&#039; (Bester Fall): Die Daten liegen optimal vor (z. B. bereits sortiert). Der Algorithmus ist minimal schnell fertig.&lt;br /&gt;
* Das &#039;&#039;&#039;Average-Case-Szenario&#039;&#039;&#039; (Durchschnittlicher Fall): Beschreibt die zu erwartende Laufzeit bei völlig zufällig gemischten Daten. Dies ist oft das realistischste Szenario in der Praxis.&lt;br /&gt;
&lt;br /&gt;
== Mathematische Berechnung des Average-Case ==&lt;br /&gt;
Oft wird fälschlicherweise angenommen, der Average-Case sei einfach „die Hälfte des Worst-Case“. Das ist mathematisch ungenau. Tatsächlich berechnet man den Average-Case mit Hilfe der Stochastik: Er entspricht dem &#039;&#039;&#039;Erwartungswert&#039;&#039;&#039; der Laufzeit.&lt;br /&gt;
&lt;br /&gt;
Man berechnet ihn, indem man alle möglichen Eingaben durchspielt und gewichtet. Die Formel für den Erwartungswert &amp;lt;math&amp;gt;E(T)&amp;lt;/math&amp;gt; lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \sum_{i=1}^{|E|} p_i \cdot t_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; ist die Wahrscheinlichkeit für einen bestimmten Fall &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; ist die Anzahl der Rechenschritte, die der Algorithmus in diesem Fall braucht.&lt;br /&gt;
&lt;br /&gt;
Für die Schule gehen wir meistens von einer &#039;&#039;&#039;Gleichverteilung&#039;&#039;&#039; aus: Jeder Fall tritt mit derselben Wahrscheinlichkeit auf.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel 1: Average-Case der Linearen Suche ===&lt;br /&gt;
Wir suchen eine bestimmte Zahl in einem Array der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Wir nehmen an, die Zahl ist im Array und jede Position ist gleich wahrscheinlich.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schritt 1: Wahrscheinlichkeit bestimmen.&#039;&#039;&#039; Die Wahrscheinlichkeit, dass unsere Zahl an Position 1, 2 oder &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; steht, ist jeweils &amp;lt;math&amp;gt;p = \frac{1}{n}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 2: Schritte zählen.&#039;&#039;&#039; Steht die Zahl an Position 1, brauchen wir 1 Vergleich. An Position 2 brauchen wir 2 Vergleiche. An Position &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; brauchen wir &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Vergleiche.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 3: Erwartungswert berechnen.&#039;&#039;&#039; Wir setzen dies in die Formel ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n}\cdot 1 + \frac{1}{n}\cdot 2 + \dots + \frac{1}{n}\cdot n = \sum_{i=1}^{n} \left( \frac{1}{n} \cdot i \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir klammern &amp;lt;math&amp;gt;\frac{1}{n}&amp;lt;/math&amp;gt; aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \sum_{i=1}^{n} i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Summe der Zahlen von 1 bis &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; lässt sich mit der &#039;&#039;&#039;Gaußschen Summenformel&#039;&#039;&#039; (kleiner Gauß) vereinfachen zu &amp;lt;math&amp;gt;\frac{n(n+1)}{2}&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \frac{n(n+1)}{2} = \frac{n+1}{2} = \frac{1}{2}n + \frac{1}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fazit:&#039;&#039;&#039; Im Durchschnitt braucht die lineare Suche &amp;lt;math&amp;gt;\frac{n+1}{2}&amp;lt;/math&amp;gt; Vergleiche. Da in der &amp;lt;math&amp;gt;\mathcal{O}&amp;lt;/math&amp;gt;-Notation Konstanten wie &amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt; wegfallen, gehört die lineare Suche im Durchschnitt zur Klasse &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel 2: Vorbereitung auf Quicksort (Maximumsuche) ===&lt;br /&gt;
Bei komplexeren Algorithmen wie [[Quicksort]] reicht einfaches Zählen nicht mehr aus. Hier arbeiten wir mit einem mathematischen Trick: &#039;&#039;&#039;Indikatorvariablen&#039;&#039;&#039; und der &#039;&#039;&#039;Harmonischen Reihe&#039;&#039;&#039;. Um das zu verstehen, analysieren wir einen simplen Code zur Maximumsuche:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public int findMax(int[] A) {&lt;br /&gt;
    int max = A[0];&lt;br /&gt;
    for (int i = 1; i &amp;lt; A.length; i++) {&lt;br /&gt;
        if (A[i] &amp;gt; max) {&lt;br /&gt;
            max = A[i]; // Update-Operation&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return max;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Vergleiche in der &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung werden immer &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Mal ausgeführt. Aber wie oft wird die Variable &amp;lt;code&amp;gt;max&amp;lt;/code&amp;gt; im Durchschnitt überschrieben (Update-Operation)?&lt;br /&gt;
&lt;br /&gt;
Wir stellen uns vor, das Array enthält völlig zufällig gemischte Zahlen.&lt;br /&gt;
* &#039;&#039;&#039;Intuitiver Ansatz:&#039;&#039;&#039; &lt;br /&gt;
Das 1. Element ist automatisch das bisherige Maximum (Wahrscheinlichkeit: 1). &lt;br /&gt;
Damit das 2. Element ein neues Maximum ist, muss es größer als das erste sein. Die Chance dafür ist exakt 50% (&amp;lt;math&amp;gt;\frac{1}{2}&amp;lt;/math&amp;gt;). &lt;br /&gt;
Damit das 3. Element ein neues Maximum ist, muss es das Größte der ersten drei Elemente sein. Die Chance dafür ist &amp;lt;math&amp;gt;\frac{1}{3}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Mathematischer Ansatz (Indikatorvariablen):&#039;&#039;&#039;&lt;br /&gt;
Wir definieren einen „Schalter“ (die Indikatorvariable) &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt;. Er ist &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;, wenn ein neues Maximum an Stelle &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; gefunden wird, sonst &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;. Die Wahrscheinlichkeit dafür ist &amp;lt;math&amp;gt;P(X_i = 1) = \frac{1}{i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Um die durchschnittliche Gesamtzahl der Updates zu finden, addieren wir einfach diese Wahrscheinlichkeiten auf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E[X] = \sum_{i=1}^{n} \frac{1}{i} = 1 + \frac{1}{2} + \frac{1}{3} + \frac{1}{4} + \dots + \frac{1}{n}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese spezielle Summe nennt man in der Mathematik die &#039;&#039;&#039;Harmonische Reihe&#039;&#039;&#039; (&amp;lt;math&amp;gt;H_n&amp;lt;/math&amp;gt;). Sie wächst extrem langsam. Für große Zahlen &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; entspricht sie in etwa dem &#039;&#039;&#039;natürlichen Logarithmus&#039;&#039;&#039; (&amp;lt;math&amp;gt;\ln(n)&amp;lt;/math&amp;gt;). &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Erkenntnis für Quicksort:&#039;&#039;&#039; Obwohl das Array &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elemente hat, wird die Update-Operation im Durchschnitt nur logarithmisch oft (&amp;lt;math&amp;gt;\mathcal{O}(\log n)&amp;lt;/math&amp;gt;) ausgeführt. Genau dieses stochastische Prinzip (das Aufsummieren von Einzelwahrscheinlichkeiten, die zu einem Logarithmus führen) ist der Schlüssel, um später selbst zu beweisen, dass Quicksort im Average-Case bei &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt; liegt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Beispiel 3: Laufzeitanalyse von Insertion Sort ==&lt;br /&gt;
Nun wenden wir unser Wissen auf das Sortierverfahren [[Insertion-sort|Insertion Sort]] (Sortieren durch Einfügen) an. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void sortierenEinfuegen_MC() {&lt;br /&gt;
    int v, i, j;&lt;br /&gt;
    for (i = 1; i &amp;lt; zSortfeld.length; i++) {&lt;br /&gt;
        c++; // Zähler für Vergleiche&lt;br /&gt;
        if (zSortfeld[i] &amp;lt; zSortfeld[i - 1]) {&lt;br /&gt;
            v = zSortfeld[i];&lt;br /&gt;
            j = i;&lt;br /&gt;
            do {&lt;br /&gt;
                zSortfeld[j] = zSortfeld[j - 1];&lt;br /&gt;
                j--;&lt;br /&gt;
            } while ((j &amp;gt; 0) &amp;amp;&amp;amp; (zSortfeld[j - 1] &amp;gt; v));&lt;br /&gt;
            zSortfeld[j] = v; &lt;br /&gt;
        }&lt;br /&gt;
        printAnalyse(i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir analysieren eine Liste mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen. Den Zeitverbrauch für einen einzelnen Vergleich nennen wir &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 1. Best-Case-Szenario (Bester Fall) ===&lt;br /&gt;
Die Liste ist bereits perfekt aufsteigend sortiert (z. B. &amp;lt;code&amp;gt;[0, 2, 3, 5, 7]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Da jedes Element schon größer als sein Vorgänger ist, ist die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung immer falsch. Die innere &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife wird ignoriert.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Wir machen exakt einen Vergleich pro Element in der äußeren Schleife. Das ergibt &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt; Vergleiche.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Der Aufwand wächst wie eine Gerade (linear). Die Komplexität ist &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 2. Worst-Case-Szenario (Schlechtester Fall) ===&lt;br /&gt;
Die Liste ist exakt falsch herum sortiert (z. B. &amp;lt;code&amp;gt;[7, 5, 3, 2, 0]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung ist immer wahr. Jedes Element muss durch die innere Schleife komplett bis an den Anfang der Liste getauscht werden.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Beim 1. Durchlauf machen wir 1 Vergleich. Beim 2. Durchlauf 2 Vergleiche. Beim letzten Durchlauf &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Vergleiche. Wir müssen also wieder addieren: &amp;lt;math&amp;gt;1 + 2 + 3 + \dots + (n-1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Wie bei der linearen Suche hilft uns hier die Gaußsche Summenformel:&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot \frac{(n - 1) \cdot n}{2} = \frac{c}{2}n^2 - \frac{c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Asymptote an quadratische Funktion2.png|mini|Quadratisches Wachstum]]&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Für extrem große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; dominiert das &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; alles andere. Wir ignorieren den Faktor &amp;lt;math&amp;gt;\frac{c}{2}&amp;lt;/math&amp;gt; und den kleineren Teil &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Das Wachstum ist quadratisch, also &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 3. Average-Case-Szenario (Durchschnittlicher Fall) ===&lt;br /&gt;
Die Werte im Array sind völlig zufällig verteilt.&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Im Durchschnitt müssen wir ein Element nicht bis ganz an den Anfang schieben (wie im Worst-Case), sondern nur bis zur &#039;&#039;&#039;Hälfte&#039;&#039;&#039; des bisher sortierten Bereichs.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Die Anzahl der Vergleiche halbiert sich grob im Vergleich zum Worst-Case. Der Aufwand liegt bei ca. &amp;lt;math&amp;gt;\frac{1}{2} \cdot \frac{c}{2}n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Wie wir gelernt haben, ignoriert die Groß-O-Notation konstante Brüche (wie &amp;lt;math&amp;gt;\frac{1}{4}&amp;lt;/math&amp;gt;). Es bleibt bei der höchsten Potenz: Das Wachstum ist und bleibt quadratisch. Die Komplexität lautet also weiterhin &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2822</id>
		<title>Laufzeitanalyse</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2822"/>
		<updated>2026-04-20T07:32:07Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]]&lt;br /&gt;
Die Laufzeitanalyse untersucht die Zeitkomplexität eines [[Algorithmus]]. Da die reale Ausführungszeit stark von der Hardwareausstattung des Zielsystems abhängt, wird für eine allgemeine, hardwareunabhängige Betrachtung die Anzahl der elementaren [[Anweisung|Befehle]] (Operationen) analysiert, die ein Algorithmus zur Lösung eines Problems benötigt. Diese Anzahl ist abhängig von der Größe der Eingabemenge, deren Umfang auch als &#039;&#039;&#039;Problemgröße&#039;&#039;&#039; (oft &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) bezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
Für kleine Eingabemengen ist die Analyse häufig von geringerer Bedeutung, da auch ineffiziente [[Algorithmus|Algorithmen]] hier oft ausreichend schnelle Ergebnisse liefern. Entscheidend ist die Betrachtung für sehr große Problemgrößen, insbesondere unter ungünstigen Startbedingungen. Hier spricht man von der asymptotischen [[Programmausführung|Laufzeit]]. In Anlehnung an eine mathematische Asymptote beschreibt sie das Zeitverhalten des Algorithmus für eine potenziell unendlich wachsende Eingabemenge (z. B. eine theoretisch unendlich lange Zahlenfolge beim [[Sortieren]]).&lt;br /&gt;
&lt;br /&gt;
Lässt sich die Laufzeit eines Algorithmus mathematisch durch eine Funktion beschreiben, wie beispielsweise:&lt;br /&gt;
&amp;lt;math&amp;gt;f(x) = \frac{1}{x} + x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So nähert sich die rot dargestellte Funktion &amp;lt;math&amp;gt;\color{red}{f(x)}&amp;lt;/math&amp;gt; für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; der grün dargestellten Asymptote &amp;lt;math&amp;gt;\color{green}{g(x) = x}&amp;lt;/math&amp;gt; an.&lt;br /&gt;
&lt;br /&gt;
Die Laufzeit wird in Abhängigkeit von der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; der Eingabe angegeben und für immer größer werdende &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; asymptotisch unter Verwendung der Landau-Notation (Groß-O-Notation) abgeschätzt.&lt;br /&gt;
&lt;br /&gt;
In der Praxis ist die Laufzeitanalyse essenziell, um entscheiden zu können, ob ein Programm bei stark anwachsenden Datenmengen noch performant und wirtschaftlich betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
=== Abgrenzung ===&lt;br /&gt;
Es ist wichtig zu betonen, was die asymptotische Laufzeitanalyse &#039;&#039;&#039;nicht&#039;&#039;&#039; leistet:&lt;br /&gt;
Sie misst nicht den tatsächlichen Zeitaufwand (in Sekunden) eines implementierten Algorithmus auf einem spezifischen Computer für eine konkrete, endliche Eingabemenge.&lt;br /&gt;
&lt;br /&gt;
== Szenarien der Laufzeitabschätzung ==&lt;br /&gt;
Man unterscheidet bei der Laufzeitabschätzung klassischerweise drei Varianten:&lt;br /&gt;
&lt;br /&gt;
* Das &#039;&#039;&#039;Worst-Case-Szenario&#039;&#039;&#039; (schlechtester Fall): Beschreibt diejenige Datenanordnung, die zu einer maximalen Durchlaufzeit des Algorithmus führt. Dies entspricht der oberen Schranke des Problems.&lt;br /&gt;
* Das &#039;&#039;&#039;Average-Case-Szenario&#039;&#039;&#039; (durchschnittlicher Fall): Beschreibt eine zufällige Problemgröße und Datenanordnung, die zu einer mittleren (erwarteten) Durchlaufzeit führt.&lt;br /&gt;
* Das &#039;&#039;&#039;Best-Case-Szenario&#039;&#039;&#039; (bester Fall): Beschreibt diejenige Datenanordnung, die zu einer minimalen Durchlaufzeit führt (z. B. eine bereits vollständig sortierte Liste). Dies entspricht der unteren Schranke.&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Laufzeitanalyse von Insertion Sort ==&lt;br /&gt;
Im Folgenden wird die Laufzeitanalyse beispielhaft anhand des [[Insertion-sort|Insertion Sorts]] (Sortieren durch Einfügen) durchgeführt. Wir betrachten eine mögliche Implementierung in der Programmiersprache [[Java]]. Der Algorithmus arbeitet mit einem [[Array]] als Datenstruktur und verwendet als [[Kontrollstruktur|Kontrollstrukturen]] [[Verzweigung|Verzweigungen]] und [[Schleife|Schleifen]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void sortierenEinfuegen_MC() {&lt;br /&gt;
    int v, i, j;&lt;br /&gt;
    for (i = 1; i &amp;lt; zSortfeld.length; i++) {&lt;br /&gt;
        c++; // Zähler für Vergleiche&lt;br /&gt;
        if (zSortfeld[i] &amp;lt; zSortfeld[i - 1]) {&lt;br /&gt;
            v = zSortfeld[i];&lt;br /&gt;
            j = i;&lt;br /&gt;
            do {&lt;br /&gt;
                zSortfeld[j] = zSortfeld[j - 1];&lt;br /&gt;
                j--;&lt;br /&gt;
            } while ((j &amp;gt; 0) &amp;amp;&amp;amp; (zSortfeld[j - 1] &amp;gt; v));&lt;br /&gt;
            zSortfeld[j] = v; &lt;br /&gt;
        }&lt;br /&gt;
        printAnalyse(i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir analysieren nun die drei Szenarien für eine Liste mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen und betrachten zunächst den Zeitverbrauch für die durchzuführenden Vergleichsoperationen. Den konstanten Zeitverbrauch für einen einzelnen Vergleich bezeichnen wir allgemein mit &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 1. Best-Case-Szenario (Bester Fall) ===&lt;br /&gt;
Ein Best-Case-Szenario liegt vor, wenn die Liste bereits aufsteigend sortiert ist, beispielsweise:&lt;br /&gt;
&amp;lt;code&amp;gt;[0, 2, 3, 5, 7, 11]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die äußere &amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;-Schleife durchläuft das Array &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Mal. Da das aktuell betrachtete Element stets größer oder gleich seinem Vorgänger ist, ist die Bedingung der &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Anweisung (&amp;lt;code&amp;gt;zSortfeld[i] &amp;lt; zSortfeld[i - 1]&amp;lt;/code&amp;gt;) &#039;&#039;&#039;immer falsch&#039;&#039;&#039;. Die innere &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife wird somit nie betreten.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Es findet pro Durchlauf der äußeren Schleife exakt ein Vergleich statt. Der Gesamtaufwand lautet &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Für sehr große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; wächst der Aufwand linear zur Eingabemenge. Der Insertion Sort hat im Best-Case somit eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 2. Worst-Case-Szenario (Schlechtester Fall) ===&lt;br /&gt;
Ein Worst-Case-Szenario liegt vor, wenn die Liste in genau umgekehrter (absteigender) Reihenfolge vorliegt, beispielsweise:&lt;br /&gt;
&amp;lt;code&amp;gt;[11, 7, 5, 3, 2, 0]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung ist hier &#039;&#039;&#039;immer wahr&#039;&#039;&#039;. Das aktuell betrachtete Element muss in der inneren &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife jedes Mal bis ganz an den Anfang des Arrays verschoben werden.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; &lt;br /&gt;
** Beim ersten Durchlauf vergleichen wir nur die 7 mit der 11 (1 Vergleich). Aufwand: &amp;lt;math&amp;gt;c \cdot 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
** Beim zweiten Durchlauf müssen wir das nächste Element mit den zwei bereits sortierten vergleichen. Aufwand: &amp;lt;math&amp;gt;c \cdot 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
** Dies setzt sich fort bis zum letzten Durchlauf, der &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Vergleiche erfordert. Aufwand: &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Der Gesamtaufwand für die Vergleiche entspricht einer [[Arithmetische-reihe|arithmetischen Reihe]] (Gaußsche Summenformel):&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot (1 + 2 + 3 + \dots + (n - 1)) = c \cdot \frac{(n - 1) \cdot n}{2} = \frac{c}{2}n^2 - \frac{c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Asymptote an quadratische Funktion2.png|mini|Quadratisches Wachstum]]&lt;br /&gt;
Für die asymptotische Betrachtung extrem großer Werte von &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; verwerfen wir den langsamer wachsenden (niederwertigen) Term &amp;lt;math&amp;gt;\frac{c}{2}n&amp;lt;/math&amp;gt; sowie die Konstante &amp;lt;math&amp;gt;\frac{c}{2}&amp;lt;/math&amp;gt;. &lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Der Aufwand wächst quadratisch. Der Insertion Sort hat im Worst-Case somit eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 3. Average-Case-Szenario (Durchschnittlicher Fall) ===&lt;br /&gt;
Das Average-Case-Szenario geht von einer völlig zufälligen Verteilung der Werte im Array aus.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Im Durchschnitt ist das betrachtete Element kleiner als die Hälfte der bereits sortierten Elemente. Die innere Schleife muss das Element also nicht bis ganz an den Anfang, sondern im Mittel nur bis in die Mitte des bisher sortierten Teilbereichs verschieben.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Die Anzahl der Vergleiche und Verschiebungen halbiert sich im Vergleich zum Worst-Case in etwa. Der rechnerische Aufwand läge grob bei &amp;lt;math&amp;gt;\frac{1}{2} \cdot \frac{c}{2}n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Da in der asymptotischen Landau-Notation konstante Faktoren (wie die Halbierung) ignoriert werden, da primär die höchste Potenz das Wachstum dominiert, bleibt es auch hier bei einem quadratischen Wachstum. Die Zeitkomplexität im Average-Case lautet ebenfalls &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Einfluss weiterer Operationen ===&lt;br /&gt;
Wie beeinflussen Lese- und Schreiboperationen (das tatsächliche Verschieben der Werte im Array) die Zeitkomplexität? Für das Verschieben käme lediglich ein konstanter Aufwand je Vergleich hinzu. Nennen wir diesen Aufwand &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;. Die angepasste Worst-Case-Funktion lautet dann beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{v+c}{2}n^2 - \frac{v+c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier gilt: Für sehr große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; werden der niederwertige Term und die konstanten Faktoren ignoriert. Es bleibt bei der Erkenntnis, dass Lese- und Schreibzugriffe die fundamentale Komplexitätsklasse nicht verändern. Das Laufzeitverhalten bleibt asymptotisch in der Klasse &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2821</id>
		<title>Laufzeitanalyse</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2821"/>
		<updated>2026-04-20T07:31:38Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung == [[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]] Die Laufzeitanalyse untersucht die Zeitkomplexität eines [[Algorithmus]]. Da die reale Ausführungszeit stark von der Hardwareausstattung des Zielsystems abhängt, wird für eine allgemeine, hardwareunabhängige Betrachtung die Anzahl der elementaren [[Anweisung|Befehle]] (Operationen) analysiert, die ein Algorithmus zur Lösung eines Problems benötigt. Diese Anzahl ist abhängig von der Größe der Eingabemenge, deren Umfang auch als &#039;&#039;&#039;Problemgröße&#039;&#039;&#039; (oft &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) bezeichnet wird. Für kleine Eingabemengen ist die Analyse häufig von geringerer Bedeutung, da auch ineffiziente [[Algorithmus|Algorithmen]] hier oft ausreichend schnelle Ergebnisse liefern. Entscheidend ist die Betrachtung für sehr große Problemgrößen, insbesondere unter ungünstigen Startbedingungen. Hier spricht man von der asymptotischen [[Programmausführung|Laufzeit]]. In Anlehnung an eine mathematische Asymptote beschreibt sie das Zeitverhalten des Algorithmus für eine potenziell unendlich wachsende Eingabemenge (z. B. eine theoretisch unendlich lange Zahlenfolge beim [[Sortieren]]). Lässt sich die Laufzeit eines Algorithmus mathematisch durch eine Funktion beschreiben, wie beispielsweise: &amp;lt;math&amp;gt;f(x) = \frac{1}{x} + x&amp;lt;/math&amp;gt; So nähert sich die rot dargestellte Funktion &amp;lt;math&amp;gt;\color{red}{f(x)}&amp;lt;/math&amp;gt; für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; der grün dargestellten Asymptote &amp;lt;math&amp;gt;\color{green}{g(x) = x}&amp;lt;/math&amp;gt; an. Die Laufzeit wird in Abhängigkeit von der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; der Eingabe angegeben und für immer größer werdende &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; asymptotisch unter Verwendung der Landau-Notation (Groß-O-Notation) abgeschätzt. In der Praxis ist die Laufzeitanalyse essenziell, um entscheiden zu können, ob ein Programm bei stark anwachsenden Datenmengen noch performant und wirtschaftlich betrieben werden kann. === Abgrenzung === Es ist wichtig zu betonen, was die asymptotische Laufzeitanalyse &#039;&#039;&#039;nicht&#039;&#039;&#039; leistet: Sie misst nicht den tatsächlichen Zeitaufwand (in Sekunden) eines implementierten Algorithmus auf einem spezifischen Computer für eine konkrete, endliche Eingabemenge. == Szenarien der Laufzeitabschätzung == Man unterscheidet bei der Laufzeitabschätzung klassischerweise drei Varianten: * Das &#039;&#039;&#039;Worst-Case-Szenario&#039;&#039;&#039; (schlechtester Fall): Beschreibt diejenige Datenanordnung, die zu einer maximalen Durchlaufzeit des Algorithmus führt. Dies entspricht der oberen Schranke des Problems. * Das &#039;&#039;&#039;Average-Case-Szenario&#039;&#039;&#039; (durchschnittlicher Fall): Beschreibt eine zufällige Problemgröße und Datenanordnung, die zu einer mittleren (erwarteten) Durchlaufzeit führt. * Das &#039;&#039;&#039;Best-Case-Szenario&#039;&#039;&#039; (bester Fall): Beschreibt diejenige Datenanordnung, die zu einer minimalen Durchlaufzeit führt (z. B. eine bereits vollständig sortierte Liste). Dies entspricht der unteren Schranke. == Mathematische Berechnung des Average-Case (Erwartungswert) == Im Gegensatz zum Worst-Case oder Best-Case, die lediglich Extremfälle betrachten, ist die Berechnung des Average-Case (durchschnittlicher Fall) mathematisch anspruchsvoller. Er entspricht in der Stochastik dem &#039;&#039;&#039;Erwartungswert&#039;&#039;&#039; der Laufzeit. Um diesen zu berechnen, müssen alle möglichen Eingaben der Größe &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; sowie deren Eintrittswahrscheinlichkeiten berücksichtigt werden. Die allgemeine Formel für den Erwartungswert &amp;lt;math&amp;gt;E(T)&amp;lt;/math&amp;gt; der Laufzeit lautet: &amp;lt;math&amp;gt;E(T) = \sum_{i=1}^{|E|} p_i \cdot t_i&amp;lt;/math&amp;gt; Dabei ist: * &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; die Menge aller möglichen Eingaben der Größe &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. * &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; die Wahrscheinlichkeit, dass genau die Eingabe &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; auftritt. * &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; die Anzahl der elementaren Rechenschritte (Laufzeit), die der Algorithmus für die Eingabe &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; benötigt. Häufig wird der Einfachheit halber eine &#039;&#039;&#039;Gleichverteilung&#039;&#039;&#039; (Laplace-Annahme) vorausgesetzt. Das bedeutet, man geht davon aus, dass jede mögliche Eingabekonfiguration gleich wahrscheinlich ist. === Beispiel: Average-Case der Linearen Suche === Um das Prinzip zu verdeutlichen, betrachten wir den Algorithmus der linearen Suche: Ein Array der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; wird von vorne nach hinten durchsucht, bis ein gesuchtes Element &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; gefunden wird. Wir treffen für diese Analyse folgende Annahmen: # Das gesuchte Element &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; befindet sich definitiv im Array. # Jede Position &amp;lt;math&amp;gt;1, 2, \dots, n&amp;lt;/math&amp;gt; im Array ist für das Element &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; gleich wahrscheinlich. Die Wahrscheinlichkeit &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;, dass es an einer bestimmten Stelle steht, ist somit &amp;lt;math&amp;gt;p = \frac{1}{n}&amp;lt;/math&amp;gt;. Wenn das Element an der ersten Stelle steht, benötigen wir 1 Vergleich. Steht es an der zweiten Stelle, benötigen wir 2 Vergleiche, und so weiter. Steht es an der letzten Stelle, benötigen wir &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Vergleiche. Wir setzen diese Werte in die Formel für den Erwartungswert ein: &amp;lt;math&amp;gt;E(T) = \sum_{i=1}^{n} \left( \frac{1}{n} \cdot i \right)&amp;lt;/math&amp;gt; Da &amp;lt;math&amp;gt;\frac{1}{n}&amp;lt;/math&amp;gt; ein konstanter Faktor unabhängig vom Laufindex &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; ist, können wir ihn vor die Summe ziehen: &amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \sum_{i=1}^{n} i&amp;lt;/math&amp;gt; Der hintere Teil der Gleichung ist die bekannte &#039;&#039;&#039;Gaußsche Summenformel&#039;&#039;&#039; (arithmetische Reihe), die sich auflösen lässt zu &amp;lt;math&amp;gt;\frac{n(n+1)}{2}&amp;lt;/math&amp;gt;. Wir setzen dies ein: &amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \frac{n(n+1)}{2}&amp;lt;/math&amp;gt; Durch Kürzen des &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; erhalten wir das finale Ergebnis: &amp;lt;math&amp;gt;E(T) = \frac{n+1}{2} = \frac{1}{2}n + \frac{1}{2}&amp;lt;/math&amp;gt; &#039;&#039;&#039;Fazit:&#039;&#039;&#039; Im Durchschnitt benötigt die lineare Suche &amp;lt;math&amp;gt;\frac{n+1}{2}&amp;lt;/math&amp;gt; Vergleiche. Für die asymptotische Laufzeitanalyse ignorieren wir Konstanten und niederwertige Terme, sodass auch der Average-Case der linearen Suche in der Komplexitätsklasse &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt; liegt. === Indikatorvariablen und die Harmonische Reihe === Bei einfachen Algorithmen reicht eine Gleichverteilung zur Herleitung oft aus. Bei komplexeren, teile-und-herrsche-basierten Algorithmen wie [[Quicksort]] reicht dies nicht mehr. Um den Average-Case von Quicksort (&amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt;) selbstständig ermitteln zu können, benötigt man zwei mathematische Werkzeuge: &#039;&#039;&#039;Indikatorzufallsvariablen&#039;&#039;&#039; und die &#039;&#039;&#039;Harmonische Reihe&#039;&#039;&#039;. Um diese Konzepte zu erlernen, betrachten wir als vorbereitendes Beispiel die Bestimmung des Maximums in einem unsortierten Array: &amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt; public int findMax(int[] A) { int max = A[0]; for (int i = 1; i &amp;lt; A.length; i++) { if (A[i] &amp;gt; max) { max = A[i]; // Update-Operation } } return max; } &amp;lt;/syntaxhighlight&amp;gt; Die grundlegenden Vergleiche (&amp;lt;code&amp;gt;A[i] &amp;gt; max&amp;lt;/code&amp;gt;) finden in jedem Fall &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Mal statt. Die spannende Frage für die Laufzeitanalyse (insbesondere beim Schreiben in den Speicher) ist jedoch: &#039;&#039;&#039;Wie oft wird die Update-Operation &amp;lt;code&amp;gt;max = A[i]&amp;lt;/code&amp;gt; im Durchschnitt ausgeführt?&#039;&#039;&#039; Wir gehen von einer zufälligen Permutation (Anordnung) der Zahlen aus. * &#039;&#039;&#039;Schritt 1: Indikatorvariablen definieren&#039;&#039;&#039; Wir definieren eine Indikatorzufallsvariable &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt;. Diese nimmt den Wert &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; an, wenn an der Stelle &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; ein neues Maximum gefunden wird, und &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;, wenn nicht. Die Gesamtzahl der Updates &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; ist die Summe aller &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt;: &amp;lt;math&amp;gt;X = \sum_{i=1}^{n} X_i&amp;lt;/math&amp;gt;. * &#039;&#039;&#039;Schritt 2: Wahrscheinlichkeit ermitteln&#039;&#039;&#039; Damit an der Stelle &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; ein neues Maximum gefunden wird, muss die Zahl &amp;lt;math&amp;gt;A[i]&amp;lt;/math&amp;gt; die größte unter den ersten &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; Zahlen sein. Da wir von einer zufälligen Anordnung ausgehen, ist jede der ersten &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; Zahlen mit gleicher Wahrscheinlichkeit die größte. Die Wahrscheinlichkeit ist also: &amp;lt;math&amp;gt;P(X_i = 1) = \frac{1}{i}&amp;lt;/math&amp;gt; Der Erwartungswert &amp;lt;math&amp;gt;E[X_i]&amp;lt;/math&amp;gt; ist bei Indikatorvariablen identisch mit ihrer Wahrscheinlichkeit: &amp;lt;math&amp;gt;E[X_i] = \frac{1}{i}&amp;lt;/math&amp;gt;. * &#039;&#039;&#039;Schritt 3: Erwartungswert der Summe berechnen&#039;&#039;&#039; Dank der Linearität des Erwartungswertes (Erwartungswerte von Summen dürfen als Summe der Erwartungswerte geschrieben werden) erhalten wir: &amp;lt;math&amp;gt;E[X] = E\left[\sum_{i=1}^{n} X_i\right] = \sum_{i=1}^{n} E[X_i] = \sum_{i=1}^{n} \frac{1}{i} = 1 + \frac{1}{2} + \frac{1}{3} + \dots + \frac{1}{n}&amp;lt;/math&amp;gt; Diese Summe ist in der Mathematik als die &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-te Partialsumme der &#039;&#039;&#039;Harmonischen Reihe&#039;&#039;&#039; (&amp;lt;math&amp;gt;H_n&amp;lt;/math&amp;gt;) bekannt. Für große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; wächst die harmonische Reihe logarithmisch, sie lässt sich annähern durch den natürlichen Logarithmus: &amp;lt;math&amp;gt;H_n \approx \ln(n)&amp;lt;/math&amp;gt;. &#039;&#039;&#039;Erkenntnis für Quicksort:&#039;&#039;&#039; Die Update-Operation wird im Average-Case also nur &amp;lt;math&amp;gt;\mathcal{O}(\log n)&amp;lt;/math&amp;gt; Mal ausgeführt, obwohl das Array &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elemente hat. Genau dieses Prinzip – das Aufsummieren von stochastischen Wahrscheinlichkeiten der einzelnen Array-Elemente, die zu einer harmonischen Reihe und damit zu einem logarithmischen Faktor führen – ist der mathematische Schlüssel, um die Average-Case-Laufzeit von &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt; bei Quicksort herzuleiten. == Beispiel: Laufzeitanalyse von Insertion Sort == Im Folgenden wird die Laufzeitanalyse beispielhaft anhand des [[Insertion-sort|Insertion Sorts]] (Sortieren durch Einfügen) durchgeführt. Wir betrachten eine mögliche Implementierung in der Programmiersprache [[Java]]. Der Algorithmus arbeitet mit einem [[Array]] als Datenstruktur und verwendet als [[Kontrollstruktur|Kontrollstrukturen]] [[Verzweigung|Verzweigungen]] und [[Schleife|Schleifen]]. &amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt; public void sortierenEinfuegen_MC() {     int v, i, j;     for (i = 1; i &amp;lt; zSortfeld.length; i++) {         c++; // Zähler für Vergleiche         if (zSortfeld[i] &amp;lt; zSortfeld[i - 1]) {             v = zSortfeld[i];             j = i;             do {                 zSortfeld[j] = zSortfeld[j - 1];                 j--;             } while ((j &amp;gt; 0) &amp;amp;&amp;amp; (zSortfeld[j - 1] &amp;gt; v));             zSortfeld[j] = v;          }         printAnalyse(i);     } } &amp;lt;/syntaxhighlight&amp;gt; Wir analysieren nun die drei Szenarien für eine Liste mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen und betrachten zunächst den Zeitverbrauch für die durchzuführenden Vergleichsoperationen. Den konstanten Zeitverbrauch für einen einzelnen Vergleich bezeichnen wir allgemein mit &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;. === 1. Best-Case-Szenario (Bester Fall) === Ein Best-Case-Szenario liegt vor, wenn die Liste bereits aufsteigend sortiert ist, beispielsweise: &amp;lt;code&amp;gt;[0, 2, 3, 5, 7, 11]&amp;lt;/code&amp;gt; * &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die äußere &amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;-Schleife durchläuft das Array &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Mal. Da das aktuell betrachtete Element stets größer oder gleich seinem Vorgänger ist, ist die Bedingung der &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Anweisung (&amp;lt;code&amp;gt;zSortfeld[i] &amp;lt; zSortfeld[i - 1]&amp;lt;/code&amp;gt;) &#039;&#039;&#039;immer falsch&#039;&#039;&#039;. Die innere &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife wird somit nie betreten. * &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Es findet pro Durchlauf der äußeren Schleife exakt ein Vergleich statt. Der Gesamtaufwand lautet &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt;. * &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Für sehr große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; wächst der Aufwand linear zur Eingabemenge. Der Insertion Sort hat im Best-Case somit eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;. === 2. Worst-Case-Szenario (Schlechtester Fall) === Ein Worst-Case-Szenario liegt vor, wenn die Liste in genau umgekehrter (absteigender) Reihenfolge vorliegt, beispielsweise: &amp;lt;code&amp;gt;[11, 7, 5, 3, 2, 0]&amp;lt;/code&amp;gt; * &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung ist hier &#039;&#039;&#039;immer wahr&#039;&#039;&#039;. Das aktuell betrachtete Element muss in der inneren &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife jedes Mal bis ganz an den Anfang des Arrays verschoben werden. * &#039;&#039;&#039;Aufwand:&#039;&#039;&#039;  ** Beim ersten Durchlauf vergleichen wir nur die 7 mit der 11 (1 Vergleich). Aufwand: &amp;lt;math&amp;gt;c \cdot 1&amp;lt;/math&amp;gt;. ** Beim zweiten Durchlauf müssen wir das nächste Element mit den zwei bereits sortierten vergleichen. Aufwand: &amp;lt;math&amp;gt;c \cdot 2&amp;lt;/math&amp;gt;. ** Dies setzt sich fort bis zum letzten Durchlauf, der &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Vergleiche erfordert. Aufwand: &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt;. Der Gesamtaufwand für die Vergleiche entspricht einer [[Arithmetische-reihe|arithmetischen Reihe]] (Gaußsche Summenformel): &amp;lt;math&amp;gt;c \cdot (1 + 2 + 3 + \dots + (n - 1)) = c \cdot \frac{(n - 1) \cdot n}{2} = \frac{c}{2}n^2 - \frac{c}{2}n&amp;lt;/math&amp;gt; [[Datei:Asymptote an quadratische Funktion2.png|mini|Quadratisches Wachstum]] Für die asymptotische Betrachtung extrem großer Werte von &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; verwerfen wir den langsamer wachsenden (niederwertigen) Term &amp;lt;math&amp;gt;\frac{c}{2}n&amp;lt;/math&amp;gt; sowie die Konstante &amp;lt;math&amp;gt;\frac{c}{2}&amp;lt;/math&amp;gt;.  * &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Der Aufwand wächst quadratisch. Der Insertion Sort hat im Worst-Case somit eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;. === 3. Average-Case-Szenario (Durchschnittlicher Fall) === Das Average-Case-Szenario geht von einer völlig zufälligen Verteilung der Werte im Array aus. * &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Im Durchschnitt ist das betrachtete Element kleiner als die Hälfte der bereits sortierten Elemente. Die innere Schleife muss das Element also nicht bis ganz an den Anfang, sondern im Mittel nur bis in die Mitte des bisher sortierten Teilbereichs verschieben. * &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Die Anzahl der Vergleiche und Verschiebungen halbiert sich im Vergleich zum Worst-Case in etwa. Der rechnerische Aufwand läge grob bei &amp;lt;math&amp;gt;\frac{1}{2} \cdot \frac{c}{2}n^2&amp;lt;/math&amp;gt;. * &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Da in der asymptotischen Landau-Notation konstante Faktoren (wie die Halbierung) ignoriert werden, da primär die höchste Potenz das Wachstum dominiert, bleibt es auch hier bei einem quadratischen Wachstum. Die Zeitkomplexität im Average-Case lautet ebenfalls &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;. === Einfluss weiterer Operationen === Wie beeinflussen Lese- und Schreiboperationen (das tatsächliche Verschieben der Werte im Array) die Zeitkomplexität? Für das Verschieben käme lediglich ein konstanter Aufwand je Vergleich hinzu. Nennen wir diesen Aufwand &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;. Die angepasste Worst-Case-Funktion lautet dann beispielsweise: &amp;lt;math&amp;gt;\frac{v+c}{2}n^2 - \frac{v+c}{2}n&amp;lt;/math&amp;gt; Auch hier gilt: Für sehr große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; werden der niederwertige Term und die konstanten Faktoren ignoriert. Es bleibt bei der Erkenntnis, dass Lese- und Schreibzugriffe die fundamentale Komplexitätsklasse nicht verändern. Das Laufzeitverhalten bleibt asymptotisch in der Klasse &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;. [[Kategorie:Programmierung]] [[Kategorie:AHR_I_Informatik_LK]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2820</id>
		<title>Laufzeitanalyse</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2820"/>
		<updated>2026-04-20T07:16:21Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Einführung ==&lt;br /&gt;
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]]&lt;br /&gt;
[[Datei:Asymptote an quadratische Funktion2.png|mini|Quadratisches Wachstum]]&lt;br /&gt;
Die &#039;&#039;&#039;Laufzeitanalyse&#039;&#039;&#039; untersucht, wie viele Rechenschritte ein [[Algorithmus]] benötigt, um ein Problem zu lösen.&lt;br /&gt;
&lt;br /&gt;
Da die tatsächliche Laufzeit eines Programms stark vom Computer abhängt (z. B. Prozessor oder Arbeitsspeicher), betrachtet man nicht die Zeit in Sekunden, sondern die Anzahl der benötigten &#039;&#039;&#039;Operationen&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Diese Anzahl hängt von der Größe der Eingabe ab.  &lt;br /&gt;
Die Eingabegröße wird meist mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* Beim Sortieren ist &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; die Anzahl der zu sortierenden Werte.&lt;br /&gt;
* Beim Durchsuchen einer Liste ist &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; die Länge der Liste.&lt;br /&gt;
&lt;br /&gt;
Für kleine Eingaben spielt die Laufzeitanalyse oft kaum eine Rolle, weil selbst langsame Verfahren schnell genug sind.  &lt;br /&gt;
Wichtig wird sie bei großen Datenmengen.&lt;br /&gt;
&lt;br /&gt;
Die Frage lautet dann:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wie wächst der Aufwand, wenn die Eingabe immer größer wird?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Genau das beschreibt die &#039;&#039;&#039;asymptotische Laufzeit&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wenn sich die Laufzeit eines Algorithmus durch eine Funktion beschreiben lässt, z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;f(x)=\frac{1}{x}+x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dann erkennt man für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;, dass der Anteil &amp;lt;math&amp;gt;\frac{1}{x}&amp;lt;/math&amp;gt; immer kleiner wird.  &lt;br /&gt;
Das Verhalten der Funktion wird dann fast nur noch durch &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; bestimmt.&lt;br /&gt;
&lt;br /&gt;
Deshalb betrachtet man bei der Laufzeitanalyse nur das Verhalten für große Werte von &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.  &lt;br /&gt;
Dieses Verhalten beschreibt man mit der &#039;&#039;&#039;Landau-Notation&#039;&#039;&#039; (Groß-O-Notation), z. B.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit kann man Algorithmen vergleichen, ohne von einer bestimmten Hardware abhängig zu sein.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Abgrenzung ===&lt;br /&gt;
Die asymptotische Laufzeitanalyse gibt &#039;&#039;&#039;nicht&#039;&#039;&#039; an, wie viele Sekunden ein Programm wirklich benötigt.&lt;br /&gt;
&lt;br /&gt;
Sie beschreibt nur, &#039;&#039;&#039;wie stark der Aufwand wächst&#039;&#039;&#039;, wenn die Eingabemenge größer wird.&lt;br /&gt;
Das macht verschiedene Algorithmen vergleichbar.&lt;br /&gt;
&lt;br /&gt;
== Szenarien der Laufzeitabschätzung ==&lt;br /&gt;
&lt;br /&gt;
Bei der Laufzeitanalyse betrachtet man meist drei Fälle:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Best Case&#039;&#039;&#039; (bester Fall):  &lt;br /&gt;
  Der Algorithmus arbeitet unter idealen Bedingungen und benötigt minimalen Aufwand.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Worst Case&#039;&#039;&#039; (schlechtester Fall):  &lt;br /&gt;
  Der Algorithmus trifft auf die ungünstigste Eingabe und benötigt maximalen Aufwand.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Average Case&#039;&#039;&#039; (durchschnittlicher Fall):  &lt;br /&gt;
  Man betrachtet den durchschnittlichen Aufwand bei zufälligen Eingaben.&lt;br /&gt;
&lt;br /&gt;
Diese drei Fälle helfen dabei, das Verhalten eines Algorithmus besser einzuschätzen.&lt;br /&gt;
&lt;br /&gt;
== Average-Case und Erwartungswert ==&lt;br /&gt;
&lt;br /&gt;
Der Average-Case beschreibt die &#039;&#039;&#039;durchschnittliche Laufzeit&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Mathematisch wird diese mit dem &#039;&#039;&#039;Erwartungswert&#039;&#039;&#039; berechnet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T)=\sum_{i=1}^{|E|} p_i \cdot t_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei gilt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;: Laufzeit bei einer bestimmten Eingabe&lt;br /&gt;
* &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt;: Wahrscheinlichkeit dieser Eingabe&lt;br /&gt;
&lt;br /&gt;
Der Erwartungswert ist also:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Laufzeit × Wahrscheinlichkeit&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
für alle möglichen Eingaben zusammengezählt.&lt;br /&gt;
&lt;br /&gt;
Oft nimmt man an, dass alle Eingaben gleich wahrscheinlich sind.  &lt;br /&gt;
Dann spricht man von einer &#039;&#039;&#039;Gleichverteilung&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: Lineare Suche ===&lt;br /&gt;
&lt;br /&gt;
Bei der linearen Suche wird eine Liste von vorne nach hinten durchsucht.&lt;br /&gt;
&lt;br /&gt;
Steht das gesuchte Element:&lt;br /&gt;
&lt;br /&gt;
* an Position 1 → 1 Vergleich&lt;br /&gt;
* an Position 2 → 2 Vergleiche&lt;br /&gt;
* ...&lt;br /&gt;
* an Position &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; → &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Vergleiche&lt;br /&gt;
&lt;br /&gt;
Wenn jede Position gleich wahrscheinlich ist, ergibt sich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T)=\frac{1}{n}\sum_{i=1}^{n} i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit der Gaußschen Summenformel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_{i=1}^{n} i = \frac{n(n+1)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T)=\frac{1}{n}\cdot\frac{n(n+1)}{2}=\frac{n+1}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Durchschnitt benötigt die lineare Suche also etwa halb so viele Vergleiche wie im schlechtesten Fall.&lt;br /&gt;
&lt;br /&gt;
Für große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; dominiert der Term &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, daher gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Laufzeitanalyse von Insertion Sort ==&lt;br /&gt;
&lt;br /&gt;
Beim [[Insertion-sort|Insertion Sort]] wird jedes neue Element an der richtigen Stelle in den bereits sortierten Teil eingefügt.&lt;br /&gt;
&lt;br /&gt;
=== Best Case ===&lt;br /&gt;
&lt;br /&gt;
Der beste Fall liegt vor, wenn die Liste bereits sortiert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;[1,2,3,4,5]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann genügt pro Element ein Vergleich.&lt;br /&gt;
&lt;br /&gt;
Bei &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T(n)=n-1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Wachstum ist linear:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Worst Case ===&lt;br /&gt;
&lt;br /&gt;
Der schlechteste Fall liegt vor, wenn die Liste rückwärts sortiert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;[5,4,3,2,1]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann muss jedes neue Element an den Anfang verschoben werden.&lt;br /&gt;
&lt;br /&gt;
Die Anzahl der Vergleiche ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;1+2+3+\dots+(n-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit Gauß:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{n(n-1)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ergibt quadratisches Wachstum:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Average Case ===&lt;br /&gt;
&lt;br /&gt;
Bei zufälliger Reihenfolge liegt das neue Element im Durchschnitt ungefähr in der Mitte des bereits sortierten Bereichs.&lt;br /&gt;
&lt;br /&gt;
Daher braucht man im Mittel etwa halb so viele Vergleiche wie im Worst Case:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{2}\cdot\frac{n(n-1)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist ebenfalls proportional zu &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Deshalb gilt auch hier:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der konstante Faktor ist zwar kleiner als im Worst Case, die &#039;&#039;&#039;Komplexitätsklasse&#039;&#039;&#039; bleibt aber gleich.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Wichtige Erkenntnis ==&lt;br /&gt;
&lt;br /&gt;
Bei der Groß-O-Notation interessieren nur:&lt;br /&gt;
&lt;br /&gt;
* das grundsätzliche Wachstum,&lt;br /&gt;
* die höchste Potenz.&lt;br /&gt;
&lt;br /&gt;
Konstanten werden ignoriert.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;3n&amp;lt;/math&amp;gt; → &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;\frac{1}{2}n^2&amp;lt;/math&amp;gt; → &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deshalb können zwei Algorithmen unterschiedlich schnell sein, aber trotzdem dieselbe Komplexitätsklasse besitzen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2819</id>
		<title>Laufzeitanalyse</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2819"/>
		<updated>2026-04-20T07:15:31Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Einführung ==&lt;br /&gt;
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]]&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Laufzeitanalyse&#039;&#039;&#039; untersucht, wie viele Rechenschritte ein [[Algorithmus]] benötigt, um ein Problem zu lösen.&lt;br /&gt;
&lt;br /&gt;
Da die tatsächliche Laufzeit eines Programms stark vom Computer abhängt (z. B. Prozessor oder Arbeitsspeicher), betrachtet man nicht die Zeit in Sekunden, sondern die Anzahl der benötigten &#039;&#039;&#039;Operationen&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Diese Anzahl hängt von der Größe der Eingabe ab.  &lt;br /&gt;
Die Eingabegröße wird meist mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* Beim Sortieren ist &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; die Anzahl der zu sortierenden Werte.&lt;br /&gt;
* Beim Durchsuchen einer Liste ist &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; die Länge der Liste.&lt;br /&gt;
&lt;br /&gt;
Für kleine Eingaben spielt die Laufzeitanalyse oft kaum eine Rolle, weil selbst langsame Verfahren schnell genug sind.  &lt;br /&gt;
Wichtig wird sie bei großen Datenmengen.&lt;br /&gt;
&lt;br /&gt;
Die Frage lautet dann:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wie wächst der Aufwand, wenn die Eingabe immer größer wird?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Genau das beschreibt die &#039;&#039;&#039;asymptotische Laufzeit&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wenn sich die Laufzeit eines Algorithmus durch eine Funktion beschreiben lässt, z. B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;f(x)=\frac{1}{x}+x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dann erkennt man für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;, dass der Anteil &amp;lt;math&amp;gt;\frac{1}{x}&amp;lt;/math&amp;gt; immer kleiner wird.  &lt;br /&gt;
Das Verhalten der Funktion wird dann fast nur noch durch &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; bestimmt.&lt;br /&gt;
&lt;br /&gt;
Deshalb betrachtet man bei der Laufzeitanalyse nur das Verhalten für große Werte von &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.  &lt;br /&gt;
Dieses Verhalten beschreibt man mit der &#039;&#039;&#039;Landau-Notation&#039;&#039;&#039; (Groß-O-Notation), z. B.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit kann man Algorithmen vergleichen, ohne von einer bestimmten Hardware abhängig zu sein.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige Abgrenzung ===&lt;br /&gt;
Die asymptotische Laufzeitanalyse gibt &#039;&#039;&#039;nicht&#039;&#039;&#039; an, wie viele Sekunden ein Programm wirklich benötigt.&lt;br /&gt;
&lt;br /&gt;
Sie beschreibt nur, &#039;&#039;&#039;wie stark der Aufwand wächst&#039;&#039;&#039;, wenn die Eingabemenge größer wird.&lt;br /&gt;
Das macht verschiedene Algorithmen vergleichbar.&lt;br /&gt;
&lt;br /&gt;
== Szenarien der Laufzeitabschätzung ==&lt;br /&gt;
&lt;br /&gt;
Bei der Laufzeitanalyse betrachtet man meist drei Fälle:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Best Case&#039;&#039;&#039; (bester Fall):  &lt;br /&gt;
  Der Algorithmus arbeitet unter idealen Bedingungen und benötigt minimalen Aufwand.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Worst Case&#039;&#039;&#039; (schlechtester Fall):  &lt;br /&gt;
  Der Algorithmus trifft auf die ungünstigste Eingabe und benötigt maximalen Aufwand.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Average Case&#039;&#039;&#039; (durchschnittlicher Fall):  &lt;br /&gt;
  Man betrachtet den durchschnittlichen Aufwand bei zufälligen Eingaben.&lt;br /&gt;
&lt;br /&gt;
Diese drei Fälle helfen dabei, das Verhalten eines Algorithmus besser einzuschätzen.&lt;br /&gt;
&lt;br /&gt;
== Average-Case und Erwartungswert ==&lt;br /&gt;
&lt;br /&gt;
Der Average-Case beschreibt die &#039;&#039;&#039;durchschnittliche Laufzeit&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Mathematisch wird diese mit dem &#039;&#039;&#039;Erwartungswert&#039;&#039;&#039; berechnet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T)=\sum_{i=1}^{|E|} p_i \cdot t_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei gilt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;: Laufzeit bei einer bestimmten Eingabe&lt;br /&gt;
* &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt;: Wahrscheinlichkeit dieser Eingabe&lt;br /&gt;
&lt;br /&gt;
Der Erwartungswert ist also:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Laufzeit × Wahrscheinlichkeit&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
für alle möglichen Eingaben zusammengezählt.&lt;br /&gt;
&lt;br /&gt;
Oft nimmt man an, dass alle Eingaben gleich wahrscheinlich sind.  &lt;br /&gt;
Dann spricht man von einer &#039;&#039;&#039;Gleichverteilung&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: Lineare Suche ===&lt;br /&gt;
&lt;br /&gt;
Bei der linearen Suche wird eine Liste von vorne nach hinten durchsucht.&lt;br /&gt;
&lt;br /&gt;
Steht das gesuchte Element:&lt;br /&gt;
&lt;br /&gt;
* an Position 1 → 1 Vergleich&lt;br /&gt;
* an Position 2 → 2 Vergleiche&lt;br /&gt;
* ...&lt;br /&gt;
* an Position &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; → &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Vergleiche&lt;br /&gt;
&lt;br /&gt;
Wenn jede Position gleich wahrscheinlich ist, ergibt sich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T)=\frac{1}{n}\sum_{i=1}^{n} i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit der Gaußschen Summenformel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_{i=1}^{n} i = \frac{n(n+1)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T)=\frac{1}{n}\cdot\frac{n(n+1)}{2}=\frac{n+1}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Durchschnitt benötigt die lineare Suche also etwa halb so viele Vergleiche wie im schlechtesten Fall.&lt;br /&gt;
&lt;br /&gt;
Für große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; dominiert der Term &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, daher gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Laufzeitanalyse von Insertion Sort ==&lt;br /&gt;
&lt;br /&gt;
Beim [[Insertion-sort|Insertion Sort]] wird jedes neue Element an der richtigen Stelle in den bereits sortierten Teil eingefügt.&lt;br /&gt;
&lt;br /&gt;
=== Best Case ===&lt;br /&gt;
&lt;br /&gt;
Der beste Fall liegt vor, wenn die Liste bereits sortiert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;[1,2,3,4,5]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann genügt pro Element ein Vergleich.&lt;br /&gt;
&lt;br /&gt;
Bei &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T(n)=n-1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Wachstum ist linear:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Worst Case ===&lt;br /&gt;
&lt;br /&gt;
Der schlechteste Fall liegt vor, wenn die Liste rückwärts sortiert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;[5,4,3,2,1]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann muss jedes neue Element an den Anfang verschoben werden.&lt;br /&gt;
&lt;br /&gt;
Die Anzahl der Vergleiche ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;1+2+3+\dots+(n-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit Gauß:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{n(n-1)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ergibt quadratisches Wachstum:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Average Case ===&lt;br /&gt;
&lt;br /&gt;
Bei zufälliger Reihenfolge liegt das neue Element im Durchschnitt ungefähr in der Mitte des bereits sortierten Bereichs.&lt;br /&gt;
&lt;br /&gt;
Daher braucht man im Mittel etwa halb so viele Vergleiche wie im Worst Case:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{2}\cdot\frac{n(n-1)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist ebenfalls proportional zu &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Deshalb gilt auch hier:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der konstante Faktor ist zwar kleiner als im Worst Case, die &#039;&#039;&#039;Komplexitätsklasse&#039;&#039;&#039; bleibt aber gleich.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Wichtige Erkenntnis ==&lt;br /&gt;
&lt;br /&gt;
Bei der Groß-O-Notation interessieren nur:&lt;br /&gt;
&lt;br /&gt;
* das grundsätzliche Wachstum,&lt;br /&gt;
* die höchste Potenz.&lt;br /&gt;
&lt;br /&gt;
Konstanten werden ignoriert.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;3n&amp;lt;/math&amp;gt; → &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;\frac{1}{2}n^2&amp;lt;/math&amp;gt; → &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deshalb können zwei Algorithmen unterschiedlich schnell sein, aber trotzdem dieselbe Komplexitätsklasse besitzen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2818</id>
		<title>Laufzeitanalyse</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2818"/>
		<updated>2026-04-20T06:45:57Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: /* Vorbereitung auf Quicksort: Indikatorvariablen und die Harmonische Reihe */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]]&lt;br /&gt;
Die Laufzeitanalyse untersucht die Zeitkomplexität eines [[Algorithmus]]. Da die reale Ausführungszeit stark von der Hardwareausstattung des Zielsystems abhängt, wird für eine allgemeine, hardwareunabhängige Betrachtung die Anzahl der elementaren [[Anweisung|Befehle]] (Operationen) analysiert, die ein Algorithmus zur Lösung eines Problems benötigt. Diese Anzahl ist abhängig von der Größe der Eingabemenge, deren Umfang auch als &#039;&#039;&#039;Problemgröße&#039;&#039;&#039; (oft &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) bezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
Für kleine Eingabemengen ist die Analyse häufig von geringerer Bedeutung, da auch ineffiziente [[Algorithmus|Algorithmen]] hier oft ausreichend schnelle Ergebnisse liefern. Entscheidend ist die Betrachtung für sehr große Problemgrößen, insbesondere unter ungünstigen Startbedingungen. Hier spricht man von der asymptotischen [[Programmausführung|Laufzeit]]. In Anlehnung an eine mathematische Asymptote beschreibt sie das Zeitverhalten des Algorithmus für eine potenziell unendlich wachsende Eingabemenge (z. B. eine theoretisch unendlich lange Zahlenfolge beim [[Sortieren]]).&lt;br /&gt;
&lt;br /&gt;
Lässt sich die Laufzeit eines Algorithmus mathematisch durch eine Funktion beschreiben, wie beispielsweise:&lt;br /&gt;
&amp;lt;math&amp;gt;f(x) = \frac{1}{x} + x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So nähert sich die rot dargestellte Funktion &amp;lt;math&amp;gt;\color{red}{f(x)}&amp;lt;/math&amp;gt; für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; der grün dargestellten Asymptote &amp;lt;math&amp;gt;\color{green}{g(x) = x}&amp;lt;/math&amp;gt; an.&lt;br /&gt;
&lt;br /&gt;
Die Laufzeit wird in Abhängigkeit von der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; der Eingabe angegeben und für immer größer werdende &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; asymptotisch unter Verwendung der Landau-Notation (Groß-O-Notation) abgeschätzt.&lt;br /&gt;
&lt;br /&gt;
In der Praxis ist die Laufzeitanalyse essenziell, um entscheiden zu können, ob ein Programm bei stark anwachsenden Datenmengen noch performant und wirtschaftlich betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
=== Abgrenzung ===&lt;br /&gt;
Es ist wichtig zu betonen, was die asymptotische Laufzeitanalyse &#039;&#039;&#039;nicht&#039;&#039;&#039; leistet:&lt;br /&gt;
Sie misst nicht den tatsächlichen Zeitaufwand (in Sekunden) eines implementierten Algorithmus auf einem spezifischen Computer für eine konkrete, endliche Eingabemenge.&lt;br /&gt;
&lt;br /&gt;
== Szenarien der Laufzeitabschätzung ==&lt;br /&gt;
Man unterscheidet bei der Laufzeitabschätzung klassischerweise drei Varianten:&lt;br /&gt;
&lt;br /&gt;
* Das &#039;&#039;&#039;Worst-Case-Szenario&#039;&#039;&#039; (schlechtester Fall): Beschreibt diejenige Datenanordnung, die zu einer maximalen Durchlaufzeit des Algorithmus führt. Dies entspricht der oberen Schranke des Problems.&lt;br /&gt;
* Das &#039;&#039;&#039;Average-Case-Szenario&#039;&#039;&#039; (durchschnittlicher Fall): Beschreibt eine zufällige Problemgröße und Datenanordnung, die zu einer mittleren (erwarteten) Durchlaufzeit führt.&lt;br /&gt;
* Das &#039;&#039;&#039;Best-Case-Szenario&#039;&#039;&#039; (bester Fall): Beschreibt diejenige Datenanordnung, die zu einer minimalen Durchlaufzeit führt (z. B. eine bereits vollständig sortierte Liste). Dies entspricht der unteren Schranke.&lt;br /&gt;
&lt;br /&gt;
== Mathematische Berechnung des Average-Case (Erwartungswert) ==&lt;br /&gt;
Im Gegensatz zum Worst-Case oder Best-Case, die lediglich Extremfälle betrachten, ist die Berechnung des Average-Case (durchschnittlicher Fall) mathematisch anspruchsvoller. Er entspricht in der Stochastik dem &#039;&#039;&#039;Erwartungswert&#039;&#039;&#039; der Laufzeit. &lt;br /&gt;
&lt;br /&gt;
Um diesen zu berechnen, müssen alle möglichen Eingaben der Größe &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; sowie deren Eintrittswahrscheinlichkeiten berücksichtigt werden. Die allgemeine Formel für den Erwartungswert &amp;lt;math&amp;gt;E(T)&amp;lt;/math&amp;gt; der Laufzeit lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \sum_{i=1}^{|E|} p_i \cdot t_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei ist:&lt;br /&gt;
* &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; die Menge aller möglichen Eingaben der Größe &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; die Wahrscheinlichkeit, dass genau die Eingabe &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; auftritt.&lt;br /&gt;
* &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; die Anzahl der elementaren Rechenschritte (Laufzeit), die der Algorithmus für die Eingabe &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; benötigt.&lt;br /&gt;
&lt;br /&gt;
Häufig wird der Einfachheit halber eine &#039;&#039;&#039;Gleichverteilung&#039;&#039;&#039; (Laplace-Annahme) vorausgesetzt. Das bedeutet, man geht davon aus, dass jede mögliche Eingabekonfiguration gleich wahrscheinlich ist.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: Average-Case der Linearen Suche ===&lt;br /&gt;
Um das Prinzip zu verdeutlichen, betrachten wir den Algorithmus der linearen Suche: Ein Array der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; wird von vorne nach hinten durchsucht, bis ein gesuchtes Element &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; gefunden wird. &lt;br /&gt;
&lt;br /&gt;
Wir treffen für diese Analyse folgende Annahmen:&lt;br /&gt;
# Das gesuchte Element &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; befindet sich definitiv im Array.&lt;br /&gt;
# Jede Position &amp;lt;math&amp;gt;1, 2, \dots, n&amp;lt;/math&amp;gt; im Array ist für das Element &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; gleich wahrscheinlich. Die Wahrscheinlichkeit &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;, dass es an einer bestimmten Stelle steht, ist somit &amp;lt;math&amp;gt;p = \frac{1}{n}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Wenn das Element an der ersten Stelle steht, benötigen wir 1 Vergleich. Steht es an der zweiten Stelle, benötigen wir 2 Vergleiche, und so weiter. Steht es an der letzten Stelle, benötigen wir &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Vergleiche. Wir setzen diese Werte in die Formel für den Erwartungswert ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \sum_{i=1}^{n} \left( \frac{1}{n} \cdot i \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da &amp;lt;math&amp;gt;\frac{1}{n}&amp;lt;/math&amp;gt; ein konstanter Faktor unabhängig vom Laufindex &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; ist, können wir ihn vor die Summe ziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \sum_{i=1}^{n} i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der hintere Teil der Gleichung ist die bekannte &#039;&#039;&#039;Gaußsche Summenformel&#039;&#039;&#039; (arithmetische Reihe), die sich auflösen lässt zu &amp;lt;math&amp;gt;\frac{n(n+1)}{2}&amp;lt;/math&amp;gt;. Wir setzen dies ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \frac{n(n+1)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch Kürzen des &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; erhalten wir das finale Ergebnis:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{n+1}{2} = \frac{1}{2}n + \frac{1}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fazit:&#039;&#039;&#039; Im Durchschnitt benötigt die lineare Suche &amp;lt;math&amp;gt;\frac{n+1}{2}&amp;lt;/math&amp;gt; Vergleiche. Für die asymptotische Laufzeitanalyse ignorieren wir Konstanten und niederwertige Terme, sodass auch der Average-Case der linearen Suche in der Komplexitätsklasse &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt; liegt. &lt;br /&gt;
&lt;br /&gt;
=== Indikatorvariablen und die Harmonische Reihe ===&lt;br /&gt;
Bei einfachen Algorithmen reicht eine Gleichverteilung zur Herleitung oft aus. Bei komplexeren, teile-und-herrsche-basierten Algorithmen wie [[Quicksort]] reicht dies nicht mehr. Um den Average-Case von Quicksort (&amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt;) selbstständig ermitteln zu können, benötigt man zwei mathematische Werkzeuge: &#039;&#039;&#039;Indikatorzufallsvariablen&#039;&#039;&#039; und die &#039;&#039;&#039;Harmonische Reihe&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Um diese Konzepte zu erlernen, betrachten wir als vorbereitendes Beispiel die Bestimmung des Maximums in einem unsortierten Array:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public int findMax(int[] A) {&lt;br /&gt;
    int max = A[0];&lt;br /&gt;
    for (int i = 1; i &amp;lt; A.length; i++) {&lt;br /&gt;
        if (A[i] &amp;gt; max) {&lt;br /&gt;
            max = A[i]; // Update-Operation&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return max;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die grundlegenden Vergleiche (&amp;lt;code&amp;gt;A[i] &amp;gt; max&amp;lt;/code&amp;gt;) finden in jedem Fall &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Mal statt. Die spannende Frage für die Laufzeitanalyse (insbesondere beim Schreiben in den Speicher) ist jedoch: &#039;&#039;&#039;Wie oft wird die Update-Operation &amp;lt;code&amp;gt;max = A[i]&amp;lt;/code&amp;gt; im Durchschnitt ausgeführt?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wir gehen von einer zufälligen Permutation (Anordnung) der Zahlen aus.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 1: Indikatorvariablen definieren&#039;&#039;&#039;&lt;br /&gt;
Wir definieren eine Indikatorzufallsvariable &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt;. Diese nimmt den Wert &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; an, wenn an der Stelle &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; ein neues Maximum gefunden wird, und &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;, wenn nicht. Die Gesamtzahl der Updates &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; ist die Summe aller &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt;: &amp;lt;math&amp;gt;X = \sum_{i=1}^{n} X_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schritt 2: Wahrscheinlichkeit ermitteln&#039;&#039;&#039;&lt;br /&gt;
Damit an der Stelle &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; ein neues Maximum gefunden wird, muss die Zahl &amp;lt;math&amp;gt;A[i]&amp;lt;/math&amp;gt; die größte unter den ersten &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; Zahlen sein. Da wir von einer zufälligen Anordnung ausgehen, ist jede der ersten &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; Zahlen mit gleicher Wahrscheinlichkeit die größte. Die Wahrscheinlichkeit ist also:&lt;br /&gt;
&amp;lt;math&amp;gt;P(X_i = 1) = \frac{1}{i}&amp;lt;/math&amp;gt;&lt;br /&gt;
Der Erwartungswert &amp;lt;math&amp;gt;E[X_i]&amp;lt;/math&amp;gt; ist bei Indikatorvariablen identisch mit ihrer Wahrscheinlichkeit: &amp;lt;math&amp;gt;E[X_i] = \frac{1}{i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schritt 3: Erwartungswert der Summe berechnen&#039;&#039;&#039;&lt;br /&gt;
Dank der Linearität des Erwartungswertes (Erwartungswerte von Summen dürfen als Summe der Erwartungswerte geschrieben werden) erhalten wir:&lt;br /&gt;
&amp;lt;math&amp;gt;E[X] = E\left[\sum_{i=1}^{n} X_i\right] = \sum_{i=1}^{n} E[X_i] = \sum_{i=1}^{n} \frac{1}{i} = 1 + \frac{1}{2} + \frac{1}{3} + \dots + \frac{1}{n}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Summe ist in der Mathematik als die &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-te Partialsumme der &#039;&#039;&#039;Harmonischen Reihe&#039;&#039;&#039; (&amp;lt;math&amp;gt;H_n&amp;lt;/math&amp;gt;) bekannt. Für große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; wächst die harmonische Reihe logarithmisch, sie lässt sich annähern durch den natürlichen Logarithmus: &amp;lt;math&amp;gt;H_n \approx \ln(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Erkenntnis für Quicksort:&#039;&#039;&#039;&lt;br /&gt;
Die Update-Operation wird im Average-Case also nur &amp;lt;math&amp;gt;\mathcal{O}(\log n)&amp;lt;/math&amp;gt; Mal ausgeführt, obwohl das Array &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elemente hat. Genau dieses Prinzip – das Aufsummieren von stochastischen Wahrscheinlichkeiten der einzelnen Array-Elemente, die zu einer harmonischen Reihe und damit zu einem logarithmischen Faktor führen – ist der mathematische Schlüssel, um die Average-Case-Laufzeit von &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt; bei Quicksort herzuleiten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Laufzeitanalyse von Insertion Sort ==&lt;br /&gt;
Im Folgenden wird die Laufzeitanalyse beispielhaft anhand des [[Insertion-sort|Insertion Sorts]] (Sortieren durch Einfügen) durchgeführt. Wir betrachten eine mögliche Implementierung in der Programmiersprache [[Java]]. Der Algorithmus arbeitet mit einem [[Array]] als Datenstruktur und verwendet als [[Kontrollstruktur|Kontrollstrukturen]] [[Verzweigung|Verzweigungen]] und [[Schleife|Schleifen]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void sortierenEinfuegen_MC() {&lt;br /&gt;
    int v, i, j;&lt;br /&gt;
    for (i = 1; i &amp;lt; zSortfeld.length; i++) {&lt;br /&gt;
        c++; // Zähler für Vergleiche&lt;br /&gt;
        if (zSortfeld[i] &amp;lt; zSortfeld[i - 1]) {&lt;br /&gt;
            v = zSortfeld[i];&lt;br /&gt;
            j = i;&lt;br /&gt;
            do {&lt;br /&gt;
                zSortfeld[j] = zSortfeld[j - 1];&lt;br /&gt;
                j--;&lt;br /&gt;
            } while ((j &amp;gt; 0) &amp;amp;&amp;amp; (zSortfeld[j - 1] &amp;gt; v));&lt;br /&gt;
            zSortfeld[j] = v; &lt;br /&gt;
        }&lt;br /&gt;
        printAnalyse(i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir analysieren nun die drei Szenarien für eine Liste mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen und betrachten zunächst den Zeitverbrauch für die durchzuführenden Vergleichsoperationen. Den konstanten Zeitverbrauch für einen einzelnen Vergleich bezeichnen wir allgemein mit &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 1. Best-Case-Szenario (Bester Fall) ===&lt;br /&gt;
Ein Best-Case-Szenario liegt vor, wenn die Liste bereits aufsteigend sortiert ist, beispielsweise:&lt;br /&gt;
&amp;lt;code&amp;gt;[0, 2, 3, 5, 7, 11]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die äußere &amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;-Schleife durchläuft das Array &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Mal. Da das aktuell betrachtete Element stets größer oder gleich seinem Vorgänger ist, ist die Bedingung der &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Anweisung (&amp;lt;code&amp;gt;zSortfeld[i] &amp;lt; zSortfeld[i - 1]&amp;lt;/code&amp;gt;) &#039;&#039;&#039;immer falsch&#039;&#039;&#039;. Die innere &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife wird somit nie betreten.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Es findet pro Durchlauf der äußeren Schleife exakt ein Vergleich statt. Der Gesamtaufwand lautet &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Für sehr große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; wächst der Aufwand linear zur Eingabemenge. Der Insertion Sort hat im Best-Case somit eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 2. Worst-Case-Szenario (Schlechtester Fall) ===&lt;br /&gt;
Ein Worst-Case-Szenario liegt vor, wenn die Liste in genau umgekehrter (absteigender) Reihenfolge vorliegt, beispielsweise:&lt;br /&gt;
&amp;lt;code&amp;gt;[11, 7, 5, 3, 2, 0]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung ist hier &#039;&#039;&#039;immer wahr&#039;&#039;&#039;. Das aktuell betrachtete Element muss in der inneren &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife jedes Mal bis ganz an den Anfang des Arrays verschoben werden.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; &lt;br /&gt;
** Beim ersten Durchlauf vergleichen wir nur die 7 mit der 11 (1 Vergleich). Aufwand: &amp;lt;math&amp;gt;c \cdot 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
** Beim zweiten Durchlauf müssen wir das nächste Element mit den zwei bereits sortierten vergleichen. Aufwand: &amp;lt;math&amp;gt;c \cdot 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
** Dies setzt sich fort bis zum letzten Durchlauf, der &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Vergleiche erfordert. Aufwand: &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Der Gesamtaufwand für die Vergleiche entspricht einer [[Arithmetische-reihe|arithmetischen Reihe]] (Gaußsche Summenformel):&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot (1 + 2 + 3 + \dots + (n - 1)) = c \cdot \frac{(n - 1) \cdot n}{2} = \frac{c}{2}n^2 - \frac{c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Asymptote an quadratische Funktion2.png|mini|Quadratisches Wachstum]]&lt;br /&gt;
Für die asymptotische Betrachtung extrem großer Werte von &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; verwerfen wir den langsamer wachsenden (niederwertigen) Term &amp;lt;math&amp;gt;\frac{c}{2}n&amp;lt;/math&amp;gt; sowie die Konstante &amp;lt;math&amp;gt;\frac{c}{2}&amp;lt;/math&amp;gt;. &lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Der Aufwand wächst quadratisch. Der Insertion Sort hat im Worst-Case somit eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 3. Average-Case-Szenario (Durchschnittlicher Fall) ===&lt;br /&gt;
Das Average-Case-Szenario geht von einer völlig zufälligen Verteilung der Werte im Array aus.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Im Durchschnitt ist das betrachtete Element kleiner als die Hälfte der bereits sortierten Elemente. Die innere Schleife muss das Element also nicht bis ganz an den Anfang, sondern im Mittel nur bis in die Mitte des bisher sortierten Teilbereichs verschieben.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Die Anzahl der Vergleiche und Verschiebungen halbiert sich im Vergleich zum Worst-Case in etwa. Der rechnerische Aufwand läge grob bei &amp;lt;math&amp;gt;\frac{1}{2} \cdot \frac{c}{2}n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Da in der asymptotischen Landau-Notation konstante Faktoren (wie die Halbierung) ignoriert werden, da primär die höchste Potenz das Wachstum dominiert, bleibt es auch hier bei einem quadratischen Wachstum. Die Zeitkomplexität im Average-Case lautet ebenfalls &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Einfluss weiterer Operationen ===&lt;br /&gt;
Wie beeinflussen Lese- und Schreiboperationen (das tatsächliche Verschieben der Werte im Array) die Zeitkomplexität? Für das Verschieben käme lediglich ein konstanter Aufwand je Vergleich hinzu. Nennen wir diesen Aufwand &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;. Die angepasste Worst-Case-Funktion lautet dann beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{v+c}{2}n^2 - \frac{v+c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier gilt: Für sehr große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; werden der niederwertige Term und die konstanten Faktoren ignoriert. Es bleibt bei der Erkenntnis, dass Lese- und Schreibzugriffe die fundamentale Komplexitätsklasse nicht verändern. Das Laufzeitverhalten bleibt asymptotisch in der Klasse &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2817</id>
		<title>Laufzeitanalyse</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2817"/>
		<updated>2026-04-20T06:43:52Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]]&lt;br /&gt;
Die Laufzeitanalyse untersucht die Zeitkomplexität eines [[Algorithmus]]. Da die reale Ausführungszeit stark von der Hardwareausstattung des Zielsystems abhängt, wird für eine allgemeine, hardwareunabhängige Betrachtung die Anzahl der elementaren [[Anweisung|Befehle]] (Operationen) analysiert, die ein Algorithmus zur Lösung eines Problems benötigt. Diese Anzahl ist abhängig von der Größe der Eingabemenge, deren Umfang auch als &#039;&#039;&#039;Problemgröße&#039;&#039;&#039; (oft &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) bezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
Für kleine Eingabemengen ist die Analyse häufig von geringerer Bedeutung, da auch ineffiziente [[Algorithmus|Algorithmen]] hier oft ausreichend schnelle Ergebnisse liefern. Entscheidend ist die Betrachtung für sehr große Problemgrößen, insbesondere unter ungünstigen Startbedingungen. Hier spricht man von der asymptotischen [[Programmausführung|Laufzeit]]. In Anlehnung an eine mathematische Asymptote beschreibt sie das Zeitverhalten des Algorithmus für eine potenziell unendlich wachsende Eingabemenge (z. B. eine theoretisch unendlich lange Zahlenfolge beim [[Sortieren]]).&lt;br /&gt;
&lt;br /&gt;
Lässt sich die Laufzeit eines Algorithmus mathematisch durch eine Funktion beschreiben, wie beispielsweise:&lt;br /&gt;
&amp;lt;math&amp;gt;f(x) = \frac{1}{x} + x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So nähert sich die rot dargestellte Funktion &amp;lt;math&amp;gt;\color{red}{f(x)}&amp;lt;/math&amp;gt; für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; der grün dargestellten Asymptote &amp;lt;math&amp;gt;\color{green}{g(x) = x}&amp;lt;/math&amp;gt; an.&lt;br /&gt;
&lt;br /&gt;
Die Laufzeit wird in Abhängigkeit von der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; der Eingabe angegeben und für immer größer werdende &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; asymptotisch unter Verwendung der Landau-Notation (Groß-O-Notation) abgeschätzt.&lt;br /&gt;
&lt;br /&gt;
In der Praxis ist die Laufzeitanalyse essenziell, um entscheiden zu können, ob ein Programm bei stark anwachsenden Datenmengen noch performant und wirtschaftlich betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
=== Abgrenzung ===&lt;br /&gt;
Es ist wichtig zu betonen, was die asymptotische Laufzeitanalyse &#039;&#039;&#039;nicht&#039;&#039;&#039; leistet:&lt;br /&gt;
Sie misst nicht den tatsächlichen Zeitaufwand (in Sekunden) eines implementierten Algorithmus auf einem spezifischen Computer für eine konkrete, endliche Eingabemenge.&lt;br /&gt;
&lt;br /&gt;
== Szenarien der Laufzeitabschätzung ==&lt;br /&gt;
Man unterscheidet bei der Laufzeitabschätzung klassischerweise drei Varianten:&lt;br /&gt;
&lt;br /&gt;
* Das &#039;&#039;&#039;Worst-Case-Szenario&#039;&#039;&#039; (schlechtester Fall): Beschreibt diejenige Datenanordnung, die zu einer maximalen Durchlaufzeit des Algorithmus führt. Dies entspricht der oberen Schranke des Problems.&lt;br /&gt;
* Das &#039;&#039;&#039;Average-Case-Szenario&#039;&#039;&#039; (durchschnittlicher Fall): Beschreibt eine zufällige Problemgröße und Datenanordnung, die zu einer mittleren (erwarteten) Durchlaufzeit führt.&lt;br /&gt;
* Das &#039;&#039;&#039;Best-Case-Szenario&#039;&#039;&#039; (bester Fall): Beschreibt diejenige Datenanordnung, die zu einer minimalen Durchlaufzeit führt (z. B. eine bereits vollständig sortierte Liste). Dies entspricht der unteren Schranke.&lt;br /&gt;
&lt;br /&gt;
== Mathematische Berechnung des Average-Case (Erwartungswert) ==&lt;br /&gt;
Im Gegensatz zum Worst-Case oder Best-Case, die lediglich Extremfälle betrachten, ist die Berechnung des Average-Case (durchschnittlicher Fall) mathematisch anspruchsvoller. Er entspricht in der Stochastik dem &#039;&#039;&#039;Erwartungswert&#039;&#039;&#039; der Laufzeit. &lt;br /&gt;
&lt;br /&gt;
Um diesen zu berechnen, müssen alle möglichen Eingaben der Größe &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; sowie deren Eintrittswahrscheinlichkeiten berücksichtigt werden. Die allgemeine Formel für den Erwartungswert &amp;lt;math&amp;gt;E(T)&amp;lt;/math&amp;gt; der Laufzeit lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \sum_{i=1}^{|E|} p_i \cdot t_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei ist:&lt;br /&gt;
* &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; die Menge aller möglichen Eingaben der Größe &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; die Wahrscheinlichkeit, dass genau die Eingabe &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; auftritt.&lt;br /&gt;
* &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt; die Anzahl der elementaren Rechenschritte (Laufzeit), die der Algorithmus für die Eingabe &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; benötigt.&lt;br /&gt;
&lt;br /&gt;
Häufig wird der Einfachheit halber eine &#039;&#039;&#039;Gleichverteilung&#039;&#039;&#039; (Laplace-Annahme) vorausgesetzt. Das bedeutet, man geht davon aus, dass jede mögliche Eingabekonfiguration gleich wahrscheinlich ist.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: Average-Case der Linearen Suche ===&lt;br /&gt;
Um das Prinzip zu verdeutlichen, betrachten wir den Algorithmus der linearen Suche: Ein Array der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; wird von vorne nach hinten durchsucht, bis ein gesuchtes Element &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; gefunden wird. &lt;br /&gt;
&lt;br /&gt;
Wir treffen für diese Analyse folgende Annahmen:&lt;br /&gt;
# Das gesuchte Element &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; befindet sich definitiv im Array.&lt;br /&gt;
# Jede Position &amp;lt;math&amp;gt;1, 2, \dots, n&amp;lt;/math&amp;gt; im Array ist für das Element &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; gleich wahrscheinlich. Die Wahrscheinlichkeit &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;, dass es an einer bestimmten Stelle steht, ist somit &amp;lt;math&amp;gt;p = \frac{1}{n}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Wenn das Element an der ersten Stelle steht, benötigen wir 1 Vergleich. Steht es an der zweiten Stelle, benötigen wir 2 Vergleiche, und so weiter. Steht es an der letzten Stelle, benötigen wir &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Vergleiche. Wir setzen diese Werte in die Formel für den Erwartungswert ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \sum_{i=1}^{n} \left( \frac{1}{n} \cdot i \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da &amp;lt;math&amp;gt;\frac{1}{n}&amp;lt;/math&amp;gt; ein konstanter Faktor unabhängig vom Laufindex &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; ist, können wir ihn vor die Summe ziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \sum_{i=1}^{n} i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der hintere Teil der Gleichung ist die bekannte &#039;&#039;&#039;Gaußsche Summenformel&#039;&#039;&#039; (arithmetische Reihe), die sich auflösen lässt zu &amp;lt;math&amp;gt;\frac{n(n+1)}{2}&amp;lt;/math&amp;gt;. Wir setzen dies ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{1}{n} \cdot \frac{n(n+1)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch Kürzen des &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; erhalten wir das finale Ergebnis:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E(T) = \frac{n+1}{2} = \frac{1}{2}n + \frac{1}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fazit:&#039;&#039;&#039; Im Durchschnitt benötigt die lineare Suche &amp;lt;math&amp;gt;\frac{n+1}{2}&amp;lt;/math&amp;gt; Vergleiche. Für die asymptotische Laufzeitanalyse ignorieren wir Konstanten und niederwertige Terme, sodass auch der Average-Case der linearen Suche in der Komplexitätsklasse &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt; liegt. &lt;br /&gt;
&lt;br /&gt;
=== Vorbereitung auf Quicksort: Indikatorvariablen und die Harmonische Reihe ===&lt;br /&gt;
Bei einfachen Algorithmen reicht eine Gleichverteilung zur Herleitung oft aus. Bei komplexeren, teile-und-herrsche-basierten Algorithmen wie [[Quicksort]] reicht dies nicht mehr. Um den Average-Case von Quicksort (&amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt;) selbstständig ermitteln zu können, benötigt man zwei mathematische Werkzeuge: &#039;&#039;&#039;Indikatorzufallsvariablen&#039;&#039;&#039; und die &#039;&#039;&#039;Harmonische Reihe&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Um diese Konzepte zu erlernen, betrachten wir als vorbereitendes Beispiel die Bestimmung des Maximums in einem unsortierten Array:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public int findMax(int[] A) {&lt;br /&gt;
    int max = A[0];&lt;br /&gt;
    for (int i = 1; i &amp;lt; A.length; i++) {&lt;br /&gt;
        if (A[i] &amp;gt; max) {&lt;br /&gt;
            max = A[i]; // Update-Operation&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return max;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die grundlegenden Vergleiche (&amp;lt;code&amp;gt;A[i] &amp;gt; max&amp;lt;/code&amp;gt;) finden in jedem Fall &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; Mal statt. Die spannende Frage für die Laufzeitanalyse (insbesondere beim Schreiben in den Speicher) ist jedoch: &#039;&#039;&#039;Wie oft wird die Update-Operation &amp;lt;code&amp;gt;max = A[i]&amp;lt;/code&amp;gt; im Durchschnitt ausgeführt?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wir gehen von einer zufälligen Permutation (Anordnung) der Zahlen aus.&lt;br /&gt;
* &#039;&#039;&#039;Schritt 1: Indikatorvariablen definieren&#039;&#039;&#039;&lt;br /&gt;
Wir definieren eine Indikatorzufallsvariable &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt;. Diese nimmt den Wert &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; an, wenn an der Stelle &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; ein neues Maximum gefunden wird, und &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;, wenn nicht. Die Gesamtzahl der Updates &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; ist die Summe aller &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt;: &amp;lt;math&amp;gt;X = \sum_{i=1}^{n} X_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schritt 2: Wahrscheinlichkeit ermitteln&#039;&#039;&#039;&lt;br /&gt;
Damit an der Stelle &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; ein neues Maximum gefunden wird, muss die Zahl &amp;lt;math&amp;gt;A[i]&amp;lt;/math&amp;gt; die größte unter den ersten &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; Zahlen sein. Da wir von einer zufälligen Anordnung ausgehen, ist jede der ersten &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; Zahlen mit gleicher Wahrscheinlichkeit die größte. Die Wahrscheinlichkeit ist also:&lt;br /&gt;
&amp;lt;math&amp;gt;P(X_i = 1) = \frac{1}{i}&amp;lt;/math&amp;gt;&lt;br /&gt;
Der Erwartungswert &amp;lt;math&amp;gt;E[X_i]&amp;lt;/math&amp;gt; ist bei Indikatorvariablen identisch mit ihrer Wahrscheinlichkeit: &amp;lt;math&amp;gt;E[X_i] = \frac{1}{i}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schritt 3: Erwartungswert der Summe berechnen&#039;&#039;&#039;&lt;br /&gt;
Dank der Linearität des Erwartungswertes (Erwartungswerte von Summen dürfen als Summe der Erwartungswerte geschrieben werden) erhalten wir:&lt;br /&gt;
&amp;lt;math&amp;gt;E[X] = E\left[\sum_{i=1}^{n} X_i\right] = \sum_{i=1}^{n} E[X_i] = \sum_{i=1}^{n} \frac{1}{i} = 1 + \frac{1}{2} + \frac{1}{3} + \dots + \frac{1}{n}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Summe ist in der Mathematik als die &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-te Partialsumme der &#039;&#039;&#039;Harmonischen Reihe&#039;&#039;&#039; (&amp;lt;math&amp;gt;H_n&amp;lt;/math&amp;gt;) bekannt. Für große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; wächst die harmonische Reihe logarithmisch, sie lässt sich annähern durch den natürlichen Logarithmus: &amp;lt;math&amp;gt;H_n \approx \ln(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Erkenntnis für Quicksort:&#039;&#039;&#039;&lt;br /&gt;
Die Update-Operation wird im Average-Case also nur &amp;lt;math&amp;gt;\mathcal{O}(\log n)&amp;lt;/math&amp;gt; Mal ausgeführt, obwohl das Array &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elemente hat. Genau dieses Prinzip – das Aufsummieren von stochastischen Wahrscheinlichkeiten der einzelnen Array-Elemente, die zu einer harmonischen Reihe und damit zu einem logarithmischen Faktor führen – ist der mathematische Schlüssel, um die Average-Case-Laufzeit von &amp;lt;math&amp;gt;\mathcal{O}(n \log n)&amp;lt;/math&amp;gt; bei Quicksort herzuleiten.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Laufzeitanalyse von Insertion Sort ==&lt;br /&gt;
Im Folgenden wird die Laufzeitanalyse beispielhaft anhand des [[Insertion-sort|Insertion Sorts]] (Sortieren durch Einfügen) durchgeführt. Wir betrachten eine mögliche Implementierung in der Programmiersprache [[Java]]. Der Algorithmus arbeitet mit einem [[Array]] als Datenstruktur und verwendet als [[Kontrollstruktur|Kontrollstrukturen]] [[Verzweigung|Verzweigungen]] und [[Schleife|Schleifen]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void sortierenEinfuegen_MC() {&lt;br /&gt;
    int v, i, j;&lt;br /&gt;
    for (i = 1; i &amp;lt; zSortfeld.length; i++) {&lt;br /&gt;
        c++; // Zähler für Vergleiche&lt;br /&gt;
        if (zSortfeld[i] &amp;lt; zSortfeld[i - 1]) {&lt;br /&gt;
            v = zSortfeld[i];&lt;br /&gt;
            j = i;&lt;br /&gt;
            do {&lt;br /&gt;
                zSortfeld[j] = zSortfeld[j - 1];&lt;br /&gt;
                j--;&lt;br /&gt;
            } while ((j &amp;gt; 0) &amp;amp;&amp;amp; (zSortfeld[j - 1] &amp;gt; v));&lt;br /&gt;
            zSortfeld[j] = v; &lt;br /&gt;
        }&lt;br /&gt;
        printAnalyse(i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir analysieren nun die drei Szenarien für eine Liste mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen und betrachten zunächst den Zeitverbrauch für die durchzuführenden Vergleichsoperationen. Den konstanten Zeitverbrauch für einen einzelnen Vergleich bezeichnen wir allgemein mit &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 1. Best-Case-Szenario (Bester Fall) ===&lt;br /&gt;
Ein Best-Case-Szenario liegt vor, wenn die Liste bereits aufsteigend sortiert ist, beispielsweise:&lt;br /&gt;
&amp;lt;code&amp;gt;[0, 2, 3, 5, 7, 11]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die äußere &amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;-Schleife durchläuft das Array &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Mal. Da das aktuell betrachtete Element stets größer oder gleich seinem Vorgänger ist, ist die Bedingung der &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Anweisung (&amp;lt;code&amp;gt;zSortfeld[i] &amp;lt; zSortfeld[i - 1]&amp;lt;/code&amp;gt;) &#039;&#039;&#039;immer falsch&#039;&#039;&#039;. Die innere &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife wird somit nie betreten.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Es findet pro Durchlauf der äußeren Schleife exakt ein Vergleich statt. Der Gesamtaufwand lautet &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Für sehr große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; wächst der Aufwand linear zur Eingabemenge. Der Insertion Sort hat im Best-Case somit eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 2. Worst-Case-Szenario (Schlechtester Fall) ===&lt;br /&gt;
Ein Worst-Case-Szenario liegt vor, wenn die Liste in genau umgekehrter (absteigender) Reihenfolge vorliegt, beispielsweise:&lt;br /&gt;
&amp;lt;code&amp;gt;[11, 7, 5, 3, 2, 0]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung ist hier &#039;&#039;&#039;immer wahr&#039;&#039;&#039;. Das aktuell betrachtete Element muss in der inneren &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife jedes Mal bis ganz an den Anfang des Arrays verschoben werden.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; &lt;br /&gt;
** Beim ersten Durchlauf vergleichen wir nur die 7 mit der 11 (1 Vergleich). Aufwand: &amp;lt;math&amp;gt;c \cdot 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
** Beim zweiten Durchlauf müssen wir das nächste Element mit den zwei bereits sortierten vergleichen. Aufwand: &amp;lt;math&amp;gt;c \cdot 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
** Dies setzt sich fort bis zum letzten Durchlauf, der &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Vergleiche erfordert. Aufwand: &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Der Gesamtaufwand für die Vergleiche entspricht einer [[Arithmetische-reihe|arithmetischen Reihe]] (Gaußsche Summenformel):&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot (1 + 2 + 3 + \dots + (n - 1)) = c \cdot \frac{(n - 1) \cdot n}{2} = \frac{c}{2}n^2 - \frac{c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Asymptote an quadratische Funktion2.png|mini|Quadratisches Wachstum]]&lt;br /&gt;
Für die asymptotische Betrachtung extrem großer Werte von &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; verwerfen wir den langsamer wachsenden (niederwertigen) Term &amp;lt;math&amp;gt;\frac{c}{2}n&amp;lt;/math&amp;gt; sowie die Konstante &amp;lt;math&amp;gt;\frac{c}{2}&amp;lt;/math&amp;gt;. &lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Der Aufwand wächst quadratisch. Der Insertion Sort hat im Worst-Case somit eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 3. Average-Case-Szenario (Durchschnittlicher Fall) ===&lt;br /&gt;
Das Average-Case-Szenario geht von einer völlig zufälligen Verteilung der Werte im Array aus.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Im Durchschnitt ist das betrachtete Element kleiner als die Hälfte der bereits sortierten Elemente. Die innere Schleife muss das Element also nicht bis ganz an den Anfang, sondern im Mittel nur bis in die Mitte des bisher sortierten Teilbereichs verschieben.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Die Anzahl der Vergleiche und Verschiebungen halbiert sich im Vergleich zum Worst-Case in etwa. Der rechnerische Aufwand läge grob bei &amp;lt;math&amp;gt;\frac{1}{2} \cdot \frac{c}{2}n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Da in der asymptotischen Landau-Notation konstante Faktoren (wie die Halbierung) ignoriert werden, da primär die höchste Potenz das Wachstum dominiert, bleibt es auch hier bei einem quadratischen Wachstum. Die Zeitkomplexität im Average-Case lautet ebenfalls &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Einfluss weiterer Operationen ===&lt;br /&gt;
Wie beeinflussen Lese- und Schreiboperationen (das tatsächliche Verschieben der Werte im Array) die Zeitkomplexität? Für das Verschieben käme lediglich ein konstanter Aufwand je Vergleich hinzu. Nennen wir diesen Aufwand &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;. Die angepasste Worst-Case-Funktion lautet dann beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{v+c}{2}n^2 - \frac{v+c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier gilt: Für sehr große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; werden der niederwertige Term und die konstanten Faktoren ignoriert. Es bleibt bei der Erkenntnis, dass Lese- und Schreibzugriffe die fundamentale Komplexitätsklasse nicht verändern. Das Laufzeitverhalten bleibt asymptotisch in der Klasse &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2816</id>
		<title>Laufzeitanalyse</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2816"/>
		<updated>2026-04-20T06:28:21Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]]&lt;br /&gt;
Die Laufzeitanalyse untersucht die Zeitkomplexität eines [[Algorithmus]]. Da die reale Ausführungszeit stark von der Hardwareausstattung des Zielsystems abhängt, wird für eine allgemeine, hardwareunabhängige Betrachtung die Anzahl der elementaren [[Anweisung|Befehle]] (Operationen) analysiert, die ein Algorithmus zur Lösung eines Problems benötigt. Diese Anzahl ist abhängig von der Größe der Eingabemenge, deren Umfang auch als &#039;&#039;&#039;Problemgröße&#039;&#039;&#039; (oft &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) bezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
Für kleine Eingabemengen ist die Analyse häufig von geringerer Bedeutung, da auch ineffiziente [[Algorithmus|Algorithmen]] hier oft ausreichend schnelle Ergebnisse liefern. Entscheidend ist die Betrachtung für sehr große Problemgrößen, insbesondere unter ungünstigen Startbedingungen. Hier spricht man von der asymptotischen [[Programmausführung|Laufzeit]]. In Anlehnung an eine mathematische Asymptote beschreibt sie das Zeitverhalten des Algorithmus für eine potenziell unendlich wachsende Eingabemenge (z. B. eine theoretisch unendlich lange Zahlenfolge beim [[Sortieren]]).&lt;br /&gt;
&lt;br /&gt;
Lässt sich die Laufzeit eines Algorithmus mathematisch durch eine Funktion beschreiben, wie beispielsweise:&lt;br /&gt;
&amp;lt;math&amp;gt;f(x) = \frac{1}{x} + x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So nähert sich die rot dargestellte Funktion &amp;lt;math&amp;gt;\color{red}{f(x)}&amp;lt;/math&amp;gt; für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; der grün dargestellten Asymptote &amp;lt;math&amp;gt;\color{green}{g(x) = x}&amp;lt;/math&amp;gt; an.&lt;br /&gt;
&lt;br /&gt;
Die Laufzeit wird in Abhängigkeit von der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; der Eingabe angegeben und für immer größer werdende &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; asymptotisch unter Verwendung der Landau-Notation (Groß-O-Notation) abgeschätzt.&lt;br /&gt;
&lt;br /&gt;
In der Praxis ist die Laufzeitanalyse essenziell, um entscheiden zu können, ob ein Programm bei stark anwachsenden Datenmengen noch performant und wirtschaftlich betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
=== Abgrenzung ===&lt;br /&gt;
Es ist wichtig zu betonen, was die asymptotische Laufzeitanalyse &#039;&#039;&#039;nicht&#039;&#039;&#039; leistet:&lt;br /&gt;
Sie misst nicht den tatsächlichen Zeitaufwand (in Sekunden) eines implementierten Algorithmus auf einem spezifischen Computer für eine konkrete, endliche Eingabemenge.&lt;br /&gt;
&lt;br /&gt;
== Szenarien der Laufzeitabschätzung ==&lt;br /&gt;
Man unterscheidet bei der Laufzeitabschätzung klassischerweise drei Varianten:&lt;br /&gt;
&lt;br /&gt;
* Das &#039;&#039;&#039;Worst-Case-Szenario&#039;&#039;&#039; (schlechtester Fall): Beschreibt diejenige Datenanordnung, die zu einer maximalen Durchlaufzeit des Algorithmus führt. Dies entspricht der oberen Schranke des Problems.&lt;br /&gt;
* Das &#039;&#039;&#039;Average-Case-Szenario&#039;&#039;&#039; (durchschnittlicher Fall): Beschreibt eine zufällige Problemgröße und Datenanordnung, die zu einer mittleren (erwarteten) Durchlaufzeit führt.&lt;br /&gt;
* Das &#039;&#039;&#039;Best-Case-Szenario&#039;&#039;&#039; (bester Fall): Beschreibt diejenige Datenanordnung, die zu einer minimalen Durchlaufzeit führt (z. B. eine bereits vollständig sortierte Liste). Dies entspricht der unteren Schranke.&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Laufzeitanalyse von Insertion Sort ==&lt;br /&gt;
Im Folgenden wird die Laufzeitanalyse beispielhaft anhand des [[Insertion-sort|Insertion Sorts]] (Sortieren durch Einfügen) durchgeführt. Wir betrachten eine mögliche Implementierung in der Programmiersprache [[Java]]. Der Algorithmus arbeitet mit einem [[Array]] als Datenstruktur und verwendet als [[Kontrollstruktur|Kontrollstrukturen]] [[Verzweigung|Verzweigungen]] und [[Schleife|Schleifen]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void sortierenEinfuegen_MC() {&lt;br /&gt;
    int v, i, j;&lt;br /&gt;
    for (i = 1; i &amp;lt; zSortfeld.length; i++) {&lt;br /&gt;
        c++; // Zähler für Vergleiche&lt;br /&gt;
        if (zSortfeld[i] &amp;lt; zSortfeld[i - 1]) {&lt;br /&gt;
            v = zSortfeld[i];&lt;br /&gt;
            j = i;&lt;br /&gt;
            do {&lt;br /&gt;
                zSortfeld[j] = zSortfeld[j - 1];&lt;br /&gt;
                j--;&lt;br /&gt;
            } while ((j &amp;gt; 0) &amp;amp;&amp;amp; (zSortfeld[j - 1] &amp;gt; v));&lt;br /&gt;
            zSortfeld[j] = v; &lt;br /&gt;
        }&lt;br /&gt;
        printAnalyse(i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir analysieren nun die drei Szenarien für eine Liste mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen und betrachten zunächst den Zeitverbrauch für die durchzuführenden Vergleichsoperationen. Den konstanten Zeitverbrauch für einen einzelnen Vergleich bezeichnen wir allgemein mit &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 1. Best-Case-Szenario (Bester Fall) ===&lt;br /&gt;
Ein Best-Case-Szenario liegt vor, wenn die Liste bereits aufsteigend sortiert ist, beispielsweise:&lt;br /&gt;
&amp;lt;code&amp;gt;[0, 2, 3, 5, 7, 11]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die äußere &amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;-Schleife durchläuft das Array &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Mal. Da das aktuell betrachtete Element stets größer oder gleich seinem Vorgänger ist, ist die Bedingung der &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Anweisung (&amp;lt;code&amp;gt;zSortfeld[i] &amp;lt; zSortfeld[i - 1]&amp;lt;/code&amp;gt;) &#039;&#039;&#039;immer falsch&#039;&#039;&#039;. Die innere &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife wird somit nie betreten.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Es findet pro Durchlauf der äußeren Schleife exakt ein Vergleich statt. Der Gesamtaufwand lautet &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Für sehr große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; wächst der Aufwand linear zur Eingabemenge. Der Insertion Sort hat im Best-Case somit eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 2. Worst-Case-Szenario (Schlechtester Fall) ===&lt;br /&gt;
Ein Worst-Case-Szenario liegt vor, wenn die Liste in genau umgekehrter (absteigender) Reihenfolge vorliegt, beispielsweise:&lt;br /&gt;
&amp;lt;code&amp;gt;[11, 7, 5, 3, 2, 0]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung ist hier &#039;&#039;&#039;immer wahr&#039;&#039;&#039;. Das aktuell betrachtete Element muss in der inneren &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife jedes Mal bis ganz an den Anfang des Arrays verschoben werden.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; &lt;br /&gt;
** Beim ersten Durchlauf vergleichen wir nur die 7 mit der 11 (1 Vergleich). Aufwand: &amp;lt;math&amp;gt;c \cdot 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
** Beim zweiten Durchlauf müssen wir das nächste Element mit den zwei bereits sortierten vergleichen. Aufwand: &amp;lt;math&amp;gt;c \cdot 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
** Dies setzt sich fort bis zum letzten Durchlauf, der &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Vergleiche erfordert. Aufwand: &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Der Gesamtaufwand für die Vergleiche entspricht einer [[Arithmetische-reihe|arithmetischen Reihe]] (Gaußsche Summenformel):&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot (1 + 2 + 3 + \dots + (n - 1)) = c \cdot \frac{(n - 1) \cdot n}{2} = \frac{c}{2}n^2 - \frac{c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Asymptote an quadratische Funktion2.png|mini|Quadratisches Wachstum]]&lt;br /&gt;
Für die asymptotische Betrachtung extrem großer Werte von &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; verwerfen wir den langsamer wachsenden (niederwertigen) Term &amp;lt;math&amp;gt;\frac{c}{2}n&amp;lt;/math&amp;gt; sowie die Konstante &amp;lt;math&amp;gt;\frac{c}{2}&amp;lt;/math&amp;gt;. &lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Der Aufwand wächst quadratisch. Der Insertion Sort hat im Worst-Case somit eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 3. Average-Case-Szenario (Durchschnittlicher Fall) ===&lt;br /&gt;
Das Average-Case-Szenario geht von einer völlig zufälligen Verteilung der Werte im Array aus.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Im Durchschnitt ist das betrachtete Element kleiner als die Hälfte der bereits sortierten Elemente. Die innere Schleife muss das Element also nicht bis ganz an den Anfang, sondern im Mittel nur bis in die Mitte des bisher sortierten Teilbereichs verschieben.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Die Anzahl der Vergleiche und Verschiebungen halbiert sich im Vergleich zum Worst-Case in etwa. Der rechnerische Aufwand läge grob bei &amp;lt;math&amp;gt;\frac{1}{2} \cdot \frac{c}{2}n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Da in der asymptotischen Landau-Notation konstante Faktoren (wie die Halbierung) ignoriert werden, da primär die höchste Potenz das Wachstum dominiert, bleibt es auch hier bei einem quadratischen Wachstum. Die Zeitkomplexität im Average-Case lautet ebenfalls &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Einfluss weiterer Operationen ===&lt;br /&gt;
Wie beeinflussen Lese- und Schreiboperationen (das tatsächliche Verschieben der Werte im Array) die Zeitkomplexität? Für das Verschieben käme lediglich ein konstanter Aufwand je Vergleich hinzu. Nennen wir diesen Aufwand &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;. Die angepasste Worst-Case-Funktion lautet dann beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{v+c}{2}n^2 - \frac{v+c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier gilt: Für sehr große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; werden der niederwertige Term und die konstanten Faktoren ignoriert. Es bleibt bei der Erkenntnis, dass Lese- und Schreibzugriffe die fundamentale Komplexitätsklasse nicht verändern. Das Laufzeitverhalten bleibt asymptotisch in der Klasse &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2815</id>
		<title>Laufzeitanalyse</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2815"/>
		<updated>2026-04-20T06:27:43Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: /* 2. Worst-Case-Szenario (Schlechtester Fall) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]]&lt;br /&gt;
Die Laufzeitanalyse untersucht die Zeitkomplexität eines [[Algorithmus]]. Da die reale Ausführungszeit stark von der Hardwareausstattung des Zielsystems abhängt, wird für eine allgemeine, hardwareunabhängige Betrachtung die Anzahl der elementaren [[Anweisung|Befehle]] (Operationen) analysiert, die ein Algorithmus zur Lösung eines Problems benötigt. Diese Anzahl ist abhängig von der Größe der Eingabemenge, deren Umfang auch als &#039;&#039;&#039;Problemgröße&#039;&#039;&#039; (oft &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) bezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
Für kleine Eingabemengen ist die Analyse häufig von geringerer Bedeutung, da auch ineffiziente [[Algorithmus|Algorithmen]] hier oft ausreichend schnelle Ergebnisse liefern. Entscheidend ist die Betrachtung für sehr große Problemgrößen, insbesondere unter ungünstigen Startbedingungen. Hier spricht man von der asymptotischen [[Programmausführung|Laufzeit]]. In Anlehnung an eine mathematische Asymptote beschreibt sie das Zeitverhalten des Algorithmus für eine potenziell unendlich wachsende Eingabemenge (z. B. eine theoretisch unendlich lange Zahlenfolge beim [[Sortieren]]).&lt;br /&gt;
&lt;br /&gt;
Lässt sich die Laufzeit eines Algorithmus mathematisch durch eine Funktion beschreiben, wie beispielsweise:&lt;br /&gt;
&amp;lt;math&amp;gt;f(x) = \frac{1}{x} + x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So nähert sich die rot dargestellte Funktion &amp;lt;math&amp;gt;\color{red}{f(x)}&amp;lt;/math&amp;gt; für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; der grün dargestellten Asymptote &amp;lt;math&amp;gt;\color{green}{g(x) = x}&amp;lt;/math&amp;gt; an.&lt;br /&gt;
&lt;br /&gt;
Die Laufzeit wird in Abhängigkeit von der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; der Eingabe angegeben und für immer größer werdende &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; asymptotisch unter Verwendung der Landau-Notation (Groß-O-Notation) abgeschätzt.&lt;br /&gt;
&lt;br /&gt;
In der Praxis ist die Laufzeitanalyse essenziell, um entscheiden zu können, ob ein Programm bei stark anwachsenden Datenmengen noch performant und wirtschaftlich betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
=== Abgrenzung ===&lt;br /&gt;
Es ist wichtig zu betonen, was die asymptotische Laufzeitanalyse &#039;&#039;&#039;nicht&#039;&#039;&#039; leistet:&lt;br /&gt;
Sie misst nicht den tatsächlichen Zeitaufwand (in Sekunden) eines implementierten Algorithmus auf einem spezifischen Computer für eine konkrete, endliche Eingabemenge.&lt;br /&gt;
&lt;br /&gt;
== Szenarien der Laufzeitabschätzung ==&lt;br /&gt;
Man unterscheidet bei der Laufzeitabschätzung klassischerweise drei Varianten:&lt;br /&gt;
&lt;br /&gt;
* Das &#039;&#039;&#039;Worst-Case-Szenario&#039;&#039;&#039; (schlechtester Fall): Beschreibt diejenige Datenanordnung, die zu einer maximalen Durchlaufzeit des Algorithmus führt. Dies entspricht der oberen Schranke des Problems.&lt;br /&gt;
* Das &#039;&#039;&#039;Average-Case-Szenario&#039;&#039;&#039; (durchschnittlicher Fall): Beschreibt eine zufällige Problemgröße und Datenanordnung, die zu einer mittleren (erwarteten) Durchlaufzeit führt.&lt;br /&gt;
* Das &#039;&#039;&#039;Best-Case-Szenario&#039;&#039;&#039; (bester Fall): Beschreibt diejenige Datenanordnung, die zu einer minimalen Durchlaufzeit führt (z. B. eine bereits vollständig sortierte Liste). Dies entspricht der unteren Schranke.&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Laufzeitanalyse von Insertion Sort ==&lt;br /&gt;
Im Folgenden wird die Laufzeitanalyse beispielhaft anhand des [[Insertion-sort|Insertion Sorts]] (Sortieren durch Einfügen) durchgeführt. Wir betrachten eine mögliche Implementierung in der Programmiersprache [[Java]]. Der Algorithmus arbeitet mit einem [[Array]] als Datenstruktur und verwendet als [[Kontrollstruktur|Kontrollstrukturen]] [[Verzweigung|Verzweigungen]] und [[Schleife|Schleifen]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void sortierenEinfuegen_MC() {&lt;br /&gt;
    int v, i, j;&lt;br /&gt;
    for (i = 1; i &amp;lt; zSortfeld.length; i++) {&lt;br /&gt;
        c++; // Zähler für Vergleiche&lt;br /&gt;
        if (zSortfeld[i] &amp;lt; zSortfeld[i - 1]) {&lt;br /&gt;
            v = zSortfeld[i];&lt;br /&gt;
            j = i;&lt;br /&gt;
            do {&lt;br /&gt;
                zSortfeld[j] = zSortfeld[j - 1];&lt;br /&gt;
                j--;&lt;br /&gt;
            } while ((j &amp;gt; 0) &amp;amp;&amp;amp; (zSortfeld[j - 1] &amp;gt; v));&lt;br /&gt;
            zSortfeld[j] = v; &lt;br /&gt;
        }&lt;br /&gt;
        printAnalyse(i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir analysieren nun die drei Szenarien für eine Liste mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen und betrachten zunächst den Zeitverbrauch für die durchzuführenden Vergleichsoperationen. Den konstanten Zeitverbrauch für einen einzelnen Vergleich bezeichnen wir allgemein mit &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 1. Best-Case-Szenario (Bester Fall) ===&lt;br /&gt;
Ein Best-Case-Szenario liegt vor, wenn die Liste bereits aufsteigend sortiert ist, beispielsweise:&lt;br /&gt;
&amp;lt;code&amp;gt;[0, 2, 3, 5, 7, 11]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die äußere &amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;-Schleife durchläuft das Array &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Mal. Da das aktuell betrachtete Element stets größer oder gleich seinem Vorgänger ist, ist die Bedingung der &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Anweisung (&amp;lt;code&amp;gt;zSortfeld[i] &amp;lt; zSortfeld[i - 1]&amp;lt;/code&amp;gt;) &#039;&#039;&#039;immer falsch&#039;&#039;&#039;. Die innere &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife wird somit nie betreten.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Es findet pro Durchlauf der äußeren Schleife exakt ein Vergleich statt. Der Gesamtaufwand lautet &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Für sehr große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; wächst der Aufwand linear zur Eingabemenge. Der Insertion Sort hat im Best-Case somit eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Einführung ==&lt;br /&gt;
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]]&lt;br /&gt;
Die Laufzeitanalyse untersucht die Zeitkomplexität eines [[Algorithmus]]. Da die reale Ausführungszeit stark von der Hardwareausstattung des Zielsystems abhängt, wird für eine allgemeine, hardwareunabhängige Betrachtung die Anzahl der elementaren [[Anweisung|Befehle]] (Operationen) analysiert, die ein Algorithmus zur Lösung eines Problems benötigt. Diese Anzahl ist abhängig von der Größe der Eingabemenge, deren Umfang auch als &#039;&#039;&#039;Problemgröße&#039;&#039;&#039; (oft &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) bezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
Für kleine Eingabemengen ist die Analyse häufig von geringerer Bedeutung, da auch ineffiziente [[Algorithmus|Algorithmen]] hier oft ausreichend schnelle Ergebnisse liefern. Entscheidend ist die Betrachtung für sehr große Problemgrößen, insbesondere unter ungünstigen Startbedingungen. Hier spricht man von der asymptotischen [[Programmausführung|Laufzeit]]. In Anlehnung an eine mathematische Asymptote beschreibt sie das Zeitverhalten des Algorithmus für eine potenziell unendlich wachsende Eingabemenge (z. B. eine theoretisch unendlich lange Zahlenfolge beim [[Sortieren]]).&lt;br /&gt;
&lt;br /&gt;
Lässt sich die Laufzeit eines Algorithmus mathematisch durch eine Funktion beschreiben, wie beispielsweise:&lt;br /&gt;
&amp;lt;math&amp;gt;f(x) = \frac{1}{x} + x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So nähert sich die rot dargestellte Funktion &amp;lt;math&amp;gt;\color{red}{f(x)}&amp;lt;/math&amp;gt; für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; der grün dargestellten Asymptote &amp;lt;math&amp;gt;\color{green}{g(x) = x}&amp;lt;/math&amp;gt; an.&lt;br /&gt;
&lt;br /&gt;
Die Laufzeit wird in Abhängigkeit von der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; der Eingabe angegeben und für immer größer werdende &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; asymptotisch unter Verwendung der Landau-Notation (Groß-O-Notation) abgeschätzt.&lt;br /&gt;
&lt;br /&gt;
In der Praxis ist die Laufzeitanalyse essenziell, um entscheiden zu können, ob ein Programm bei stark anwachsenden Datenmengen noch performant und wirtschaftlich betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
=== Abgrenzung ===&lt;br /&gt;
Es ist wichtig zu betonen, was die asymptotische Laufzeitanalyse &#039;&#039;&#039;nicht&#039;&#039;&#039; leistet:&lt;br /&gt;
Sie misst nicht den tatsächlichen Zeitaufwand (in Sekunden) eines implementierten Algorithmus auf einem spezifischen Computer für eine konkrete, endliche Eingabemenge.&lt;br /&gt;
&lt;br /&gt;
== Szenarien der Laufzeitabschätzung ==&lt;br /&gt;
Man unterscheidet bei der Laufzeitabschätzung klassischerweise drei Varianten:&lt;br /&gt;
&lt;br /&gt;
* Das &#039;&#039;&#039;Worst-Case-Szenario&#039;&#039;&#039; (schlechtester Fall): Beschreibt diejenige Datenanordnung, die zu einer maximalen Durchlaufzeit des Algorithmus führt. Dies entspricht der oberen Schranke des Problems.&lt;br /&gt;
* Das &#039;&#039;&#039;Average-Case-Szenario&#039;&#039;&#039; (durchschnittlicher Fall): Beschreibt eine zufällige Problemgröße und Datenanordnung, die zu einer mittleren (erwarteten) Durchlaufzeit führt.&lt;br /&gt;
* Das &#039;&#039;&#039;Best-Case-Szenario&#039;&#039;&#039; (bester Fall): Beschreibt diejenige Datenanordnung, die zu einer minimalen Durchlaufzeit führt (z. B. eine bereits vollständig sortierte Liste). Dies entspricht der unteren Schranke.&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Laufzeitanalyse von Insertion Sort ==&lt;br /&gt;
Im Folgenden wird die Laufzeitanalyse beispielhaft anhand des [[Insertion-sort|Insertion Sorts]] (Sortieren durch Einfügen) durchgeführt. Wir betrachten eine mögliche Implementierung in der Programmiersprache [[Java]]. Der Algorithmus arbeitet mit einem [[Array]] als Datenstruktur und verwendet als [[Kontrollstruktur|Kontrollstrukturen]] [[Verzweigung|Verzweigungen]] und [[Schleife|Schleifen]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void sortierenEinfuegen_MC() {&lt;br /&gt;
    int v, i, j;&lt;br /&gt;
    for (i = 1; i &amp;lt; zSortfeld.length; i++) {&lt;br /&gt;
        c++; // Zähler für Vergleiche&lt;br /&gt;
        if (zSortfeld[i] &amp;lt; zSortfeld[i - 1]) {&lt;br /&gt;
            v = zSortfeld[i];&lt;br /&gt;
            j = i;&lt;br /&gt;
            do {&lt;br /&gt;
                zSortfeld[j] = zSortfeld[j - 1];&lt;br /&gt;
                j--;&lt;br /&gt;
            } while ((j &amp;gt; 0) &amp;amp;&amp;amp; (zSortfeld[j - 1] &amp;gt; v));&lt;br /&gt;
            zSortfeld[j] = v; &lt;br /&gt;
        }&lt;br /&gt;
        printAnalyse(i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir analysieren nun die drei Szenarien für eine Liste mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen und betrachten zunächst den Zeitverbrauch für die durchzuführenden Vergleichsoperationen. Den konstanten Zeitverbrauch für einen einzelnen Vergleich bezeichnen wir allgemein mit &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 1. Best-Case-Szenario (Bester Fall) ===&lt;br /&gt;
Ein Best-Case-Szenario liegt vor, wenn die Liste bereits aufsteigend sortiert ist, beispielsweise:&lt;br /&gt;
&amp;lt;code&amp;gt;[0, 2, 3, 5, 7, 11]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die äußere &amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;-Schleife durchläuft das Array &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Mal. Da das aktuell betrachtete Element stets größer oder gleich seinem Vorgänger ist, ist die Bedingung der &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Anweisung (&amp;lt;code&amp;gt;zSortfeld[i] &amp;lt; zSortfeld[i - 1]&amp;lt;/code&amp;gt;) &#039;&#039;&#039;immer falsch&#039;&#039;&#039;. Die innere &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife wird somit nie betreten.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Es findet pro Durchlauf der äußeren Schleife exakt ein Vergleich statt. Der Gesamtaufwand lautet &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Für sehr große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; wächst der Aufwand linear zur Eingabemenge. Der Insertion Sort hat im Best-Case somit eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 2. Worst-Case-Szenario (Schlechtester Fall) ===&lt;br /&gt;
Ein Worst-Case-Szenario liegt vor, wenn die Liste in genau umgekehrter (absteigender) Reihenfolge vorliegt, beispielsweise:&lt;br /&gt;
&amp;lt;code&amp;gt;[11, 7, 5, 3, 2, 0]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung ist hier &#039;&#039;&#039;immer wahr&#039;&#039;&#039;. Das aktuell betrachtete Element muss in der inneren &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife jedes Mal bis ganz an den Anfang des Arrays verschoben werden.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; &lt;br /&gt;
** Beim ersten Durchlauf vergleichen wir nur die 7 mit der 11 (1 Vergleich). Aufwand: &amp;lt;math&amp;gt;c \cdot 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
** Beim zweiten Durchlauf müssen wir das nächste Element mit den zwei bereits sortierten vergleichen. Aufwand: &amp;lt;math&amp;gt;c \cdot 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
** Dies setzt sich fort bis zum letzten Durchlauf, der &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Vergleiche erfordert. Aufwand: &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Der Gesamtaufwand für die Vergleiche entspricht einer [[Arithmetische-reihe|arithmetischen Reihe]] (Gaußsche Summenformel):&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot (1 + 2 + 3 + \dots + (n - 1)) = c \cdot \frac{(n - 1) \cdot n}{2} = \frac{c}{2}n^2 - \frac{c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Asymptote an quadratische Funktion2.png|mini|Quadratisches Wachstum]]&lt;br /&gt;
Für die asymptotische Betrachtung extrem großer Werte von &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; verwerfen wir den langsamer wachsenden (niederwertigen) Term &amp;lt;math&amp;gt;\frac{c}{2}n&amp;lt;/math&amp;gt; sowie die Konstante &amp;lt;math&amp;gt;\frac{c}{2}&amp;lt;/math&amp;gt;. &lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Der Aufwand wächst quadratisch. Der Insertion Sort hat im Worst-Case somit eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 3. Average-Case-Szenario (Durchschnittlicher Fall) ===&lt;br /&gt;
Das Average-Case-Szenario geht von einer völlig zufälligen Verteilung der Werte im Array aus.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Im Durchschnitt ist das betrachtete Element kleiner als die Hälfte der bereits sortierten Elemente. Die innere Schleife muss das Element also nicht bis ganz an den Anfang, sondern im Mittel nur bis in die Mitte des bisher sortierten Teilbereichs verschieben.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Die Anzahl der Vergleiche und Verschiebungen halbiert sich im Vergleich zum Worst-Case in etwa. Der rechnerische Aufwand läge grob bei &amp;lt;math&amp;gt;\frac{1}{2} \cdot \frac{c}{2}n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Da in der asymptotischen Landau-Notation konstante Faktoren (wie die Halbierung) ignoriert werden, da primär die höchste Potenz das Wachstum dominiert, bleibt es auch hier bei einem quadratischen Wachstum. Die Zeitkomplexität im Average-Case lautet ebenfalls &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Einfluss weiterer Operationen ===&lt;br /&gt;
Wie beeinflussen Lese- und Schreiboperationen (das tatsächliche Verschieben der Werte im Array) die Zeitkomplexität? Für das Verschieben käme lediglich ein konstanter Aufwand je Vergleich hinzu. Nennen wir diesen Aufwand &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;. Die angepasste Worst-Case-Funktion lautet dann beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{v+c}{2}n^2 - \frac{v+c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier gilt: Für sehr große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; werden der niederwertige Term und die konstanten Faktoren ignoriert. Es bleibt bei der Erkenntnis, dass Lese- und Schreibzugriffe die fundamentale Komplexitätsklasse nicht verändern. Das Laufzeitverhalten bleibt asymptotisch in der Klasse &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;br /&gt;
&lt;br /&gt;
=== 3. Average-Case-Szenario (Durchschnittlicher Fall) ===&lt;br /&gt;
Das Average-Case-Szenario geht von einer völlig zufälligen Verteilung der Werte im Array aus.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Im Durchschnitt ist das betrachtete Element kleiner als die Hälfte der bereits sortierten Elemente. Die innere Schleife muss das Element also nicht bis ganz an den Anfang, sondern im Mittel nur bis in die Mitte des bisher sortierten Teilbereichs verschieben.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Die Anzahl der Vergleiche und Verschiebungen halbiert sich im Vergleich zum Worst-Case in etwa. Der rechnerische Aufwand läge grob bei &amp;lt;math&amp;gt;\frac{1}{2} \cdot \frac{c}{2}n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Da in der asymptotischen Landau-Notation konstante Faktoren (wie die Halbierung) ignoriert werden, da primär die höchste Potenz das Wachstum dominiert, bleibt es auch hier bei einem quadratischen Wachstum. Die Zeitkomplexität im Average-Case lautet ebenfalls &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Einfluss weiterer Operationen ===&lt;br /&gt;
Wie beeinflussen Lese- und Schreiboperationen (das tatsächliche Verschieben der Werte im Array) die Zeitkomplexität? Für das Verschieben käme lediglich ein konstanter Aufwand je Vergleich hinzu. Nennen wir diesen Aufwand &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;. Die angepasste Worst-Case-Funktion lautet dann beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{v+c}{2}n^2 - \frac{v+c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier gilt: Für sehr große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; werden der niederwertige Term und die konstanten Faktoren ignoriert. Es bleibt bei der Erkenntnis, dass Lese- und Schreibzugriffe die fundamentale Komplexitätsklasse nicht verändern. Das Laufzeitverhalten bleibt asymptotisch in der Klasse &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2814</id>
		<title>Laufzeitanalyse</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Laufzeitanalyse&amp;diff=2814"/>
		<updated>2026-04-20T06:23:40Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
[[Datei:Laufzeitanalyse asymptotisch.png|mini|Asymptotische Annäherung einer Funktion]]&lt;br /&gt;
Die Laufzeitanalyse untersucht die Zeitkomplexität eines [[Algorithmus]]. Da die reale Ausführungszeit stark von der Hardwareausstattung des Zielsystems abhängt, wird für eine allgemeine, hardwareunabhängige Betrachtung die Anzahl der elementaren [[Anweisung|Befehle]] (Operationen) analysiert, die ein Algorithmus zur Lösung eines Problems benötigt. Diese Anzahl ist abhängig von der Größe der Eingabemenge, deren Umfang auch als &#039;&#039;&#039;Problemgröße&#039;&#039;&#039; (oft &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;) bezeichnet wird.&lt;br /&gt;
&lt;br /&gt;
Für kleine Eingabemengen ist die Analyse häufig von geringerer Bedeutung, da auch ineffiziente [[Algorithmus|Algorithmen]] hier oft ausreichend schnelle Ergebnisse liefern. Entscheidend ist die Betrachtung für sehr große Problemgrößen, insbesondere unter ungünstigen Startbedingungen. Hier spricht man von der asymptotischen [[Programmausführung|Laufzeit]]. In Anlehnung an eine mathematische Asymptote beschreibt sie das Zeitverhalten des Algorithmus für eine potenziell unendlich wachsende Eingabemenge (z. B. eine theoretisch unendlich lange Zahlenfolge beim [[Sortieren]]).&lt;br /&gt;
&lt;br /&gt;
Lässt sich die Laufzeit eines Algorithmus mathematisch durch eine Funktion beschreiben, wie beispielsweise:&lt;br /&gt;
&amp;lt;math&amp;gt;f(x) = \frac{1}{x} + x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So nähert sich die rot dargestellte Funktion &amp;lt;math&amp;gt;\color{red}{f(x)}&amp;lt;/math&amp;gt; für große &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; der grün dargestellten Asymptote &amp;lt;math&amp;gt;\color{green}{g(x) = x}&amp;lt;/math&amp;gt; an.&lt;br /&gt;
&lt;br /&gt;
Die Laufzeit wird in Abhängigkeit von der Länge &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; der Eingabe angegeben und für immer größer werdende &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; asymptotisch unter Verwendung der Landau-Notation (Groß-O-Notation) abgeschätzt.&lt;br /&gt;
&lt;br /&gt;
In der Praxis ist die Laufzeitanalyse essenziell, um entscheiden zu können, ob ein Programm bei stark anwachsenden Datenmengen noch performant und wirtschaftlich betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
=== Abgrenzung ===&lt;br /&gt;
Es ist wichtig zu betonen, was die asymptotische Laufzeitanalyse &#039;&#039;&#039;nicht&#039;&#039;&#039; leistet:&lt;br /&gt;
Sie misst nicht den tatsächlichen Zeitaufwand (in Sekunden) eines implementierten Algorithmus auf einem spezifischen Computer für eine konkrete, endliche Eingabemenge.&lt;br /&gt;
&lt;br /&gt;
== Szenarien der Laufzeitabschätzung ==&lt;br /&gt;
Man unterscheidet bei der Laufzeitabschätzung klassischerweise drei Varianten:&lt;br /&gt;
&lt;br /&gt;
* Das &#039;&#039;&#039;Worst-Case-Szenario&#039;&#039;&#039; (schlechtester Fall): Beschreibt diejenige Datenanordnung, die zu einer maximalen Durchlaufzeit des Algorithmus führt. Dies entspricht der oberen Schranke des Problems.&lt;br /&gt;
* Das &#039;&#039;&#039;Average-Case-Szenario&#039;&#039;&#039; (durchschnittlicher Fall): Beschreibt eine zufällige Problemgröße und Datenanordnung, die zu einer mittleren (erwarteten) Durchlaufzeit führt.&lt;br /&gt;
* Das &#039;&#039;&#039;Best-Case-Szenario&#039;&#039;&#039; (bester Fall): Beschreibt diejenige Datenanordnung, die zu einer minimalen Durchlaufzeit führt (z. B. eine bereits vollständig sortierte Liste). Dies entspricht der unteren Schranke.&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Laufzeitanalyse von Insertion Sort ==&lt;br /&gt;
Im Folgenden wird die Laufzeitanalyse beispielhaft anhand des [[Insertion-sort|Insertion Sorts]] (Sortieren durch Einfügen) durchgeführt. Wir betrachten eine mögliche Implementierung in der Programmiersprache [[Java]]. Der Algorithmus arbeitet mit einem [[Array]] als Datenstruktur und verwendet als [[Kontrollstruktur|Kontrollstrukturen]] [[Verzweigung|Verzweigungen]] und [[Schleife|Schleifen]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public void sortierenEinfuegen_MC() {&lt;br /&gt;
    int v, i, j;&lt;br /&gt;
    for (i = 1; i &amp;lt; zSortfeld.length; i++) {&lt;br /&gt;
        c++; // Zähler für Vergleiche&lt;br /&gt;
        if (zSortfeld[i] &amp;lt; zSortfeld[i - 1]) {&lt;br /&gt;
            v = zSortfeld[i];&lt;br /&gt;
            j = i;&lt;br /&gt;
            do {&lt;br /&gt;
                zSortfeld[j] = zSortfeld[j - 1];&lt;br /&gt;
                j--;&lt;br /&gt;
            } while ((j &amp;gt; 0) &amp;amp;&amp;amp; (zSortfeld[j - 1] &amp;gt; v));&lt;br /&gt;
            zSortfeld[j] = v; &lt;br /&gt;
        }&lt;br /&gt;
        printAnalyse(i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir analysieren nun die drei Szenarien für eine Liste mit &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; Elementen und betrachten zunächst den Zeitverbrauch für die durchzuführenden Vergleichsoperationen. Den konstanten Zeitverbrauch für einen einzelnen Vergleich bezeichnen wir allgemein mit &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 1. Best-Case-Szenario (Bester Fall) ===&lt;br /&gt;
Ein Best-Case-Szenario liegt vor, wenn die Liste bereits aufsteigend sortiert ist, beispielsweise:&lt;br /&gt;
&amp;lt;code&amp;gt;[0, 2, 3, 5, 7, 11]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die äußere &amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;-Schleife durchläuft das Array &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Mal. Da das aktuell betrachtete Element stets größer oder gleich seinem Vorgänger ist, ist die Bedingung der &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Anweisung (&amp;lt;code&amp;gt;zSortfeld[i] &amp;lt; zSortfeld[i - 1]&amp;lt;/code&amp;gt;) &#039;&#039;&#039;immer falsch&#039;&#039;&#039;. Die innere &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife wird somit nie betreten.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Es findet pro Durchlauf der äußeren Schleife exakt ein Vergleich statt. Der Gesamtaufwand lautet &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Für sehr große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; wächst der Aufwand linear zur Eingabemenge. Der Insertion Sort hat im Best-Case somit eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 2. Worst-Case-Szenario (Schlechtester Fall) ===&lt;br /&gt;
Ein Worst-Case-Szenario liegt vor, wenn die Liste in genau umgekehrter (absteigender) Reihenfolge vorliegt, beispielsweise:&lt;br /&gt;
&amp;lt;code&amp;gt;[11, 7, 5, 3, 2, 0]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Die &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;-Bedingung ist hier &#039;&#039;&#039;immer wahr&#039;&#039;&#039;. Das aktuell betrachtete Element muss in der inneren &amp;lt;code&amp;gt;do-while&amp;lt;/code&amp;gt;-Schleife jedes Mal bis ganz an den Anfang des Arrays verschoben werden.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; &lt;br /&gt;
  * Beim ersten Durchlauf vergleichen wir nur die 7 mit der 11 (1 Vergleich). Aufwand: &amp;lt;math&amp;gt;c \cdot 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
  * Beim zweiten Durchlauf müssen wir das nächste Element mit den zwei bereits sortierten vergleichen. Aufwand: &amp;lt;math&amp;gt;c \cdot 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
  * Dies setzt sich fort bis zum letzten Durchlauf, der &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; Vergleiche erfordert. Aufwand: &amp;lt;math&amp;gt;c \cdot (n - 1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Der Gesamtaufwand für die Vergleiche entspricht einer [[Arithmetische-reihe|arithmetischen Reihe]] (Gaußsche Summenformel):&lt;br /&gt;
&amp;lt;math&amp;gt;c \cdot (1 + 2 + 3 + \dots + (n - 1)) = c \cdot \frac{(n - 1) \cdot n}{2} = \frac{c}{2}n^2 - \frac{c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Asymptote an quadratische Funktion2.png|mini|Quadratisches Wachstum]]&lt;br /&gt;
Für die asymptotische Betrachtung extrem großer Werte von &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; verwerfen wir den langsamer wachsenden (niederwertigen) Term &amp;lt;math&amp;gt;\frac{c}{2}n&amp;lt;/math&amp;gt; sowie die Konstante &amp;lt;math&amp;gt;\frac{c}{2}&amp;lt;/math&amp;gt;. &lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Der Aufwand wächst quadratisch. Der Insertion Sort hat im Worst-Case somit eine Zeitkomplexität von &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== 3. Average-Case-Szenario (Durchschnittlicher Fall) ===&lt;br /&gt;
Das Average-Case-Szenario geht von einer völlig zufälligen Verteilung der Werte im Array aus.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verhalten:&#039;&#039;&#039; Im Durchschnitt ist das betrachtete Element kleiner als die Hälfte der bereits sortierten Elemente. Die innere Schleife muss das Element also nicht bis ganz an den Anfang, sondern im Mittel nur bis in die Mitte des bisher sortierten Teilbereichs verschieben.&lt;br /&gt;
* &#039;&#039;&#039;Aufwand:&#039;&#039;&#039; Die Anzahl der Vergleiche und Verschiebungen halbiert sich im Vergleich zum Worst-Case in etwa. Der rechnerische Aufwand läge grob bei &amp;lt;math&amp;gt;\frac{1}{2} \cdot \frac{c}{2}n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Komplexität:&#039;&#039;&#039; Da in der asymptotischen Landau-Notation konstante Faktoren (wie die Halbierung) ignoriert werden, da primär die höchste Potenz das Wachstum dominiert, bleibt es auch hier bei einem quadratischen Wachstum. Die Zeitkomplexität im Average-Case lautet ebenfalls &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Einfluss weiterer Operationen ===&lt;br /&gt;
Wie beeinflussen Lese- und Schreiboperationen (das tatsächliche Verschieben der Werte im Array) die Zeitkomplexität? Für das Verschieben käme lediglich ein konstanter Aufwand je Vergleich hinzu. Nennen wir diesen Aufwand &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;. Die angepasste Worst-Case-Funktion lautet dann beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{v+c}{2}n^2 - \frac{v+c}{2}n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier gilt: Für sehr große &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; werden der niederwertige Term und die konstanten Faktoren ignoriert. Es bleibt bei der Erkenntnis, dass Lese- und Schreibzugriffe die fundamentale Komplexitätsklasse nicht verändern. Das Laufzeitverhalten bleibt asymptotisch in der Klasse &amp;lt;math&amp;gt;\mathcal{O}(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Programmierung]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik_LK]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=DCL&amp;diff=2813</id>
		<title>DCL</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=DCL&amp;diff=2813"/>
		<updated>2026-04-17T09:40:09Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
Die &#039;&#039;&#039;Data Control Language&#039;&#039;&#039; (kurz &#039;&#039;&#039;DCL&#039;&#039;&#039;) ist die Datenüberwachungs- und Berechtigungssprache und ein Teilbereich der [[SQL|Structured Query Language (SQL)]]. Sie wird in relationalen Datenbanksystemen verwendet, um Zugriffsrechte, Rollen und Sicherheitsrichtlinien für Datenbankbenutzer zu verwalten (Berechtigungen vergeben oder entziehen).&lt;br /&gt;
&lt;br /&gt;
== Abgrenzung und Systemabhängigkeit ==&lt;br /&gt;
Die Implementierung der DCL existiert in unterschiedlichen Datenbanksystemen in verschiedenen Ausprägungen. Es gibt in der Datenbankwelt keine absolut einheitliche Trennung der SQL-Teilsprachen: Einige Datenbank-Hersteller verwenden den isolierten Begriff der DCL nicht und zählen die Berechtigungsbefehle stattdessen mit zur [[Data Definition Language]] (DDL) oder zur Systemverwaltung.&lt;br /&gt;
&lt;br /&gt;
== Die Kernbefehle der DCL ==&lt;br /&gt;
Die Zugriffssteuerung in SQL basiert primär auf zwei fundamentalen Befehlen: &#039;&#039;&#039;GRANT&#039;&#039;&#039; und &#039;&#039;&#039;REVOKE&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== GRANT (Rechte vergeben) ===&lt;br /&gt;
Mit dem Befehl `GRANT` werden einem Datenbankbenutzer oder einer Benutzerrolle spezifische Berechtigungen (Privilegien) für bestimmte Datenbankobjekte (wie Tabellen, Views oder die gesamte Datenbank) erteilt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
GRANT &amp;lt;Privilegien&amp;gt; ON &amp;lt;Datenbankobjekt&amp;gt; TO &amp;lt;Benutzer&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
Einem Benutzer namens &#039;&#039;mitarbeiter&#039;&#039; wird das Recht gegeben, Datensätze in der Tabelle `salon` der Datenbank `friseur` zu lesen (`SELECT`) und neue Datensätze hinzuzufügen (`INSERT`).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
GRANT SELECT, INSERT ON friseur.salon TO &#039;mitarbeiter&#039;@&#039;localhost&#039;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== REVOKE (Rechte entziehen) ===&lt;br /&gt;
Der Befehl `REVOKE` ist das Gegenstück zu `GRANT` und wird verwendet, um zuvor erteilte Berechtigungen wieder zu entziehen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
REVOKE &amp;lt;Privilegien&amp;gt; ON &amp;lt;Datenbankobjekt&amp;gt; FROM &amp;lt;Benutzer&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
Dem Benutzer &#039;&#039;mitarbeiter&#039;&#039; wird das Recht, Datensätze hinzuzufügen, wieder entzogen. Das Leserecht bleibt dabei bestehen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
REVOKE INSERT ON friseur.salon FROM &#039;mitarbeiter&#039;@&#039;localhost&#039;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Typische Berechtigungen (Privilegien) ==&lt;br /&gt;
Beim Vergeben oder Entziehen von Rechten können verschiedene Abstufungen vorgenommen werden. Zu den gängigsten Privilegien gehören:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Privileg !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;SELECT&#039;&#039;&#039; || Erlaubt das Abfragen/Lesen von Daten (DQL).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;INSERT&#039;&#039;&#039; || Erlaubt das Einfügen neuer Datensätze (DML).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UPDATE&#039;&#039;&#039; || Erlaubt das Ändern bestehender Datensätze (DML).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DELETE&#039;&#039;&#039; || Erlaubt das Löschen von Datensätzen (DML).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;ALL PRIVILEGES&#039;&#039;&#039; || Erteilt alle verfügbaren Rechte für das angegebene Objekt (oft Administratoren vorbehalten).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Herstellerspezifische Umsetzung (MySQL) ==&lt;br /&gt;
Da die genaue Syntax für die Benutzerverwaltung (z. B. das Anlegen von Benutzern mit Passwörtern) stark vom eingesetzten Datenbankmanagementsystem abhängt, weichen die Befehle in der Praxis oft leicht ab. Der exakte Befehlsumfang der DCL für das Produkt MySQL (von Oracle) kann im offiziellen MySQL-Manual nachgeschlagen werden.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Datenbanken]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik LK]]&lt;br /&gt;
[[Kategorie:FI_I_SDM]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Relationale_Algebra&amp;diff=2812</id>
		<title>Relationale Algebra</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Relationale_Algebra&amp;diff=2812"/>
		<updated>2026-04-17T09:38:59Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: /* Einführung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
Die &#039;&#039;&#039;relationale Algebra&#039;&#039;&#039; (auch Relationenalgebra genannt) bildet die theoretische Grundlage für Abfragesprachen in [[Relationale Datenbank|relationalen Datenbanken]]. In der Theorie der Datenbanken versteht man unter einer relationalen Algebra eine formale Sprache, mit der sich Abfragen über einem relationalen Schema formulieren lassen. Sie erlaubt es, Relationen miteinander zu verknüpfen und komplexere Informationen daraus herzuleiten.&lt;br /&gt;
&lt;br /&gt;
Die relationale Algebra definiert Operationen, die sich auf eine Menge von Relationen anwenden lassen. Damit lassen sich beispielsweise Relationen verknüpfen, filtern oder umbenennen. Die Ergebnisse dieser Operationen sind immer wieder neue Relationen.&lt;br /&gt;
&lt;br /&gt;
Als Erfinder der relationalen Algebra gilt &#039;&#039;&#039;Edgar F. Codd&#039;&#039;&#039;. In den 1970er Jahren hat er mit dieser mathematischen Grundlage die Datenbankwelt revolutioniert. Die Datenbanksprache SEQUEL, ein Vorläufer des heutigen [[SQL]], war eine der ersten praktischen Umsetzungen der Ideen des relationalen Modells und damit der relationalen Algebra.&lt;br /&gt;
&lt;br /&gt;
[[Datei:RelationMitarbeiter.png|mini]]&lt;br /&gt;
&lt;br /&gt;
== Mengenoperationen ==&lt;br /&gt;
Relationen bezeichnen Mengen im mathematischen Sinne. Eine Menge enthält sogenannte Tupel (Datensätze). Zwischen den Tupeln einer Menge bzw. Relation ist keine Ordnung (wie z. B. eine Reihenfolge) definiert. Auf Tabellen in der Praxis sind hingegen stets Ordnungen definiert, da es dort eine physische Reihenfolge der Datensätze gibt. &lt;br /&gt;
&lt;br /&gt;
Zudem kann ein Tupel in einer mathematischen Menge nicht mehrfach vorkommen – es gibt &#039;&#039;&#039;keine Duplikate&#039;&#039;&#039;. In einer reinen Datenbanktabelle wären Duplikate (ohne Primärschlüssel) hingegen technisch möglich. Auf diesen relationalen Mengen können unter gewissen Voraussetzungen die folgenden mathematischen Operationen ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Vereinigungsverträglichkeit (Typkompatibilität) ===&lt;br /&gt;
Um Mengenoperationen auf Relationen durchführen zu können, müssen diese miteinander kompatibel sein. Die Typkompatibilität (auch &#039;&#039;&#039;Vereinigungsverträglichkeit&#039;&#039;&#039; genannt) zweier Relationen ist gegeben, wenn:&lt;br /&gt;
# sie den gleichen Grad haben (die Anzahl der Attribute/Spalten ist identisch).&lt;br /&gt;
# der Wertebereich (Datentyp) der korrespondierenden Attribute identisch ist.&lt;br /&gt;
&lt;br /&gt;
=== Vereinigung (Union) ===&lt;br /&gt;
[[Datei:MengeVereinigungsmenge.png|mini|rechts|Vereinigungsmenge]]&lt;br /&gt;
Ist die Vereinigungsverträglichkeit gegeben, können zwei Mengen vereinigt werden. Bei der Vereinigung von zwei Relationen &amp;lt;math&amp;gt;R \cup S&amp;lt;/math&amp;gt; werden alle Tupel der Relation R mit allen Tupeln der Relation S zu einer einzigen neuen Relation vereint. Duplikate, die in beiden Relationen vorkommen, werden bei der Vereinigung gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
* Relation R enthält die Mitarbeiter {Müller, Meier}.&lt;br /&gt;
* Relation S enthält die Mitarbeiter {Meier, Schulze}.&lt;br /&gt;
* Die Vereinigung &amp;lt;math&amp;gt;R \cup S&amp;lt;/math&amp;gt; ergibt {Müller, Meier, Schulze}.&lt;br /&gt;
&lt;br /&gt;
=== Schnittmenge (Intersection) ===&lt;br /&gt;
[[Datei:MengeSchnittmenge.png|mini|rechts|Schnittmenge]]&lt;br /&gt;
Ist die Vereinigungsverträglichkeit gegeben, können zwei Mengen geschnitten werden. Die Schnittmenge &amp;lt;math&amp;gt;R \cap S&amp;lt;/math&amp;gt; beschreibt die Menge der Tupel, die sich exakt in &#039;&#039;&#039;beiden&#039;&#039;&#039; zu schneidenden Mengen wiederfinden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
* Die Schnittmenge aus R {Müller, Meier} und S {Meier, Schulze} ist &amp;lt;math&amp;gt;R \cap S&amp;lt;/math&amp;gt; = {Meier}.&lt;br /&gt;
&lt;br /&gt;
=== Differenz (Difference) ===&lt;br /&gt;
[[Datei:MengeDifferenzmenge.png|mini|rechts|Differenzmenge]]&lt;br /&gt;
Die Differenz zweier Mengen kann gebildet werden, falls Vereinigungsverträglichkeit herrscht. Es wird eine Untermenge gebildet, die nur Elemente der einen Menge enthält, abzüglich der Elemente, die auch in der zweiten Menge vorkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
* Die Differenz &amp;lt;math&amp;gt;R \setminus S&amp;lt;/math&amp;gt; (gesprochen: R ohne S) aus {Müller, Meier} und {Meier, Schulze} ergibt {Müller}.&lt;br /&gt;
&lt;br /&gt;
=== Symmetrische Differenz ===&lt;br /&gt;
Die symmetrische Differenz zweier Mengen kann gebildet werden, falls Vereinigungsverträglichkeit herrscht. Bei der symmetrischen Differenz handelt es sich um die Menge aller Tupel, die &#039;&#039;&#039;entweder&#039;&#039;&#039; in R &#039;&#039;&#039;oder&#039;&#039;&#039; in S, aber &#039;&#039;&#039;nicht in beiden gleichzeitig&#039;&#039;&#039; enthalten sind.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
* Die symmetrische Differenz aus R {Müller, Meier} und S {Meier, Schulze} ergibt {Müller, Schulze}.&lt;br /&gt;
&lt;br /&gt;
== Spezifische Relationenoperationen ==&lt;br /&gt;
&lt;br /&gt;
=== Kartesisches Produkt (Cross Product) ===&lt;br /&gt;
Ohne Voraussetzung bezüglich der Vereinigungsverträglichkeit lässt sich das Kartesische Produkt (&amp;lt;math&amp;gt;R \times S&amp;lt;/math&amp;gt;) zweier Mengen bilden. Das Resultat ist die Menge &#039;&#039;&#039;aller möglichen Kombinationen&#039;&#039;&#039; der Tupel aus R und S. Das heißt: Jedes Tupel der einen Relation wird mit jedem Tupel der anderen Relation kombiniert. &lt;br /&gt;
&lt;br /&gt;
Wenn alle Attribute verschieden sind, umfasst die Resultatsrelation die Summe der Attribute (Spalten) der Ausgangstabellen. Die Anzahl der Tupel (Zeilen) in der Resultatstabelle ist das Ergebnis der Multiplikation der Zeilenanzahlen der Ausgangstabellen (z. B. 3 Zeilen in R und 4 Zeilen in S ergeben 12 Zeilen im Kartesischen Produkt).&lt;br /&gt;
&lt;br /&gt;
=== Projektion ===&lt;br /&gt;
Die Projektion (&amp;lt;math&amp;gt;\pi&amp;lt;/math&amp;gt;) kann ohne Voraussetzungen durchgeführt werden. Sie extrahiert einzelne Attribute (Spalten) aus der ursprünglichen Attributmenge und ist somit als eine Art Filterung auf Spaltenebene zu verstehen. Die Projektion blendet also Spalten aus. Entstehen durch das Ausblenden von Spalten identische Zeilen, werden diese Duplikate in der Ergebnisrelation eliminiert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039; Aus einer Mitarbeitertabelle mit {Personalnummer, Name, Abteilung} wird durch Projektion nur die Spalte {Abteilung} extrahiert. Das Ergebnis ist eine Liste aller Abteilungen ohne Duplikate.&lt;br /&gt;
&lt;br /&gt;
=== Selektion (Selection) ===&lt;br /&gt;
Die Selektion (&amp;lt;math&amp;gt;\sigma&amp;lt;/math&amp;gt;) kann ebenfalls ohne Voraussetzungen zur Kompatibilität mit anderen Tabellen durchgeführt werden, da sie nur auf einer Relation operiert. Sie extrahiert einzelne Tupel (Datensätze), die eine bestimmte Bedingung erfüllen, aus der ursprünglichen Relation. Sie ist somit als eine Art Filterung auf Zeilenebene zu verstehen (das heißt, die Selektion blendet Zeilen aus, die das Kriterium nicht erfüllen).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039; Aus einer Mitarbeitertabelle werden durch Selektion nur die Tupel extrahiert, bei denen das Attribut &#039;&#039;Gehalt &amp;gt; 3000&#039;&#039; ist.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Datenbanken]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik LK]]&lt;br /&gt;
[[Kategorie:FI_I_SDM]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Relationale_Algebra&amp;diff=2811</id>
		<title>Relationale Algebra</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Relationale_Algebra&amp;diff=2811"/>
		<updated>2026-04-17T09:38:30Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: Die Seite wurde neu angelegt: „== Einführung == Die &amp;#039;&amp;#039;&amp;#039;relationale Algebra&amp;#039;&amp;#039;&amp;#039; (auch Relationenalgebra genannt) bildet die theoretische Grundlage für Abfragesprachen in relationalen Datenbanken. In der Theorie der Datenbanken versteht man unter einer relationalen Algebra eine formale Sprache, mit der sich Abfragen über einem relationalen Schema formulieren lassen. Sie erlaubt es, Relationen miteinander zu verknüpfen und komplexere Informationen daraus herzu…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
Die &#039;&#039;&#039;relationale Algebra&#039;&#039;&#039; (auch Relationenalgebra genannt) bildet die theoretische Grundlage für Abfragesprachen in [[Relationale Datenbank|relationalen Datenbanken]]. In der Theorie der Datenbanken versteht man unter einer relationalen Algebra eine formale Sprache, mit der sich Abfragen über einem relationalen Schema formulieren lassen. Sie erlaubt es, Relationen miteinander zu verknüpfen und komplexere Informationen daraus herzuleiten.&lt;br /&gt;
&lt;br /&gt;
Die relationale Algebra definiert Operationen, die sich auf eine Menge von Relationen anwenden lassen. Damit lassen sich beispielsweise Relationen verknüpfen, filtern oder umbenennen. Die Ergebnisse dieser Operationen sind immer wieder neue Relationen.&lt;br /&gt;
&lt;br /&gt;
Als Erfinder der relationalen Algebra gilt &#039;&#039;&#039;Edgar F. Codd&#039;&#039;&#039;. In den 1970er Jahren hat er mit dieser mathematischen Grundlage die Datenbankwelt revolutioniert. Die Datenbanksprache SEQUEL, ein Vorläufer des heutigen [[SQL]], war eine der ersten praktischen Umsetzungen der Ideen des relationalen Modells und damit der relationalen Algebra.&lt;br /&gt;
&lt;br /&gt;
== Mengenoperationen ==&lt;br /&gt;
Relationen bezeichnen Mengen im mathematischen Sinne. Eine Menge enthält sogenannte Tupel (Datensätze). Zwischen den Tupeln einer Menge bzw. Relation ist keine Ordnung (wie z. B. eine Reihenfolge) definiert. Auf Tabellen in der Praxis sind hingegen stets Ordnungen definiert, da es dort eine physische Reihenfolge der Datensätze gibt. &lt;br /&gt;
&lt;br /&gt;
Zudem kann ein Tupel in einer mathematischen Menge nicht mehrfach vorkommen – es gibt &#039;&#039;&#039;keine Duplikate&#039;&#039;&#039;. In einer reinen Datenbanktabelle wären Duplikate (ohne Primärschlüssel) hingegen technisch möglich. Auf diesen relationalen Mengen können unter gewissen Voraussetzungen die folgenden mathematischen Operationen ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Vereinigungsverträglichkeit (Typkompatibilität) ===&lt;br /&gt;
Um Mengenoperationen auf Relationen durchführen zu können, müssen diese miteinander kompatibel sein. Die Typkompatibilität (auch &#039;&#039;&#039;Vereinigungsverträglichkeit&#039;&#039;&#039; genannt) zweier Relationen ist gegeben, wenn:&lt;br /&gt;
# sie den gleichen Grad haben (die Anzahl der Attribute/Spalten ist identisch).&lt;br /&gt;
# der Wertebereich (Datentyp) der korrespondierenden Attribute identisch ist.&lt;br /&gt;
&lt;br /&gt;
=== Vereinigung (Union) ===&lt;br /&gt;
[[Datei:MengeVereinigungsmenge.png|mini|rechts|Vereinigungsmenge]]&lt;br /&gt;
Ist die Vereinigungsverträglichkeit gegeben, können zwei Mengen vereinigt werden. Bei der Vereinigung von zwei Relationen &amp;lt;math&amp;gt;R \cup S&amp;lt;/math&amp;gt; werden alle Tupel der Relation R mit allen Tupeln der Relation S zu einer einzigen neuen Relation vereint. Duplikate, die in beiden Relationen vorkommen, werden bei der Vereinigung gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
* Relation R enthält die Mitarbeiter {Müller, Meier}.&lt;br /&gt;
* Relation S enthält die Mitarbeiter {Meier, Schulze}.&lt;br /&gt;
* Die Vereinigung &amp;lt;math&amp;gt;R \cup S&amp;lt;/math&amp;gt; ergibt {Müller, Meier, Schulze}.&lt;br /&gt;
&lt;br /&gt;
=== Schnittmenge (Intersection) ===&lt;br /&gt;
[[Datei:MengeSchnittmenge.png|mini|rechts|Schnittmenge]]&lt;br /&gt;
Ist die Vereinigungsverträglichkeit gegeben, können zwei Mengen geschnitten werden. Die Schnittmenge &amp;lt;math&amp;gt;R \cap S&amp;lt;/math&amp;gt; beschreibt die Menge der Tupel, die sich exakt in &#039;&#039;&#039;beiden&#039;&#039;&#039; zu schneidenden Mengen wiederfinden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
* Die Schnittmenge aus R {Müller, Meier} und S {Meier, Schulze} ist &amp;lt;math&amp;gt;R \cap S&amp;lt;/math&amp;gt; = {Meier}.&lt;br /&gt;
&lt;br /&gt;
=== Differenz (Difference) ===&lt;br /&gt;
[[Datei:MengeDifferenzmenge.png|mini|rechts|Differenzmenge]]&lt;br /&gt;
Die Differenz zweier Mengen kann gebildet werden, falls Vereinigungsverträglichkeit herrscht. Es wird eine Untermenge gebildet, die nur Elemente der einen Menge enthält, abzüglich der Elemente, die auch in der zweiten Menge vorkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
* Die Differenz &amp;lt;math&amp;gt;R \setminus S&amp;lt;/math&amp;gt; (gesprochen: R ohne S) aus {Müller, Meier} und {Meier, Schulze} ergibt {Müller}.&lt;br /&gt;
&lt;br /&gt;
=== Symmetrische Differenz ===&lt;br /&gt;
Die symmetrische Differenz zweier Mengen kann gebildet werden, falls Vereinigungsverträglichkeit herrscht. Bei der symmetrischen Differenz handelt es sich um die Menge aller Tupel, die &#039;&#039;&#039;entweder&#039;&#039;&#039; in R &#039;&#039;&#039;oder&#039;&#039;&#039; in S, aber &#039;&#039;&#039;nicht in beiden gleichzeitig&#039;&#039;&#039; enthalten sind.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
* Die symmetrische Differenz aus R {Müller, Meier} und S {Meier, Schulze} ergibt {Müller, Schulze}.&lt;br /&gt;
&lt;br /&gt;
== Spezifische Relationenoperationen ==&lt;br /&gt;
&lt;br /&gt;
=== Kartesisches Produkt (Cross Product) ===&lt;br /&gt;
Ohne Voraussetzung bezüglich der Vereinigungsverträglichkeit lässt sich das Kartesische Produkt (&amp;lt;math&amp;gt;R \times S&amp;lt;/math&amp;gt;) zweier Mengen bilden. Das Resultat ist die Menge &#039;&#039;&#039;aller möglichen Kombinationen&#039;&#039;&#039; der Tupel aus R und S. Das heißt: Jedes Tupel der einen Relation wird mit jedem Tupel der anderen Relation kombiniert. &lt;br /&gt;
&lt;br /&gt;
Wenn alle Attribute verschieden sind, umfasst die Resultatsrelation die Summe der Attribute (Spalten) der Ausgangstabellen. Die Anzahl der Tupel (Zeilen) in der Resultatstabelle ist das Ergebnis der Multiplikation der Zeilenanzahlen der Ausgangstabellen (z. B. 3 Zeilen in R und 4 Zeilen in S ergeben 12 Zeilen im Kartesischen Produkt).&lt;br /&gt;
&lt;br /&gt;
=== Projektion ===&lt;br /&gt;
Die Projektion (&amp;lt;math&amp;gt;\pi&amp;lt;/math&amp;gt;) kann ohne Voraussetzungen durchgeführt werden. Sie extrahiert einzelne Attribute (Spalten) aus der ursprünglichen Attributmenge und ist somit als eine Art Filterung auf Spaltenebene zu verstehen. Die Projektion blendet also Spalten aus. Entstehen durch das Ausblenden von Spalten identische Zeilen, werden diese Duplikate in der Ergebnisrelation eliminiert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039; Aus einer Mitarbeitertabelle mit {Personalnummer, Name, Abteilung} wird durch Projektion nur die Spalte {Abteilung} extrahiert. Das Ergebnis ist eine Liste aller Abteilungen ohne Duplikate.&lt;br /&gt;
&lt;br /&gt;
=== Selektion (Selection) ===&lt;br /&gt;
Die Selektion (&amp;lt;math&amp;gt;\sigma&amp;lt;/math&amp;gt;) kann ebenfalls ohne Voraussetzungen zur Kompatibilität mit anderen Tabellen durchgeführt werden, da sie nur auf einer Relation operiert. Sie extrahiert einzelne Tupel (Datensätze), die eine bestimmte Bedingung erfüllen, aus der ursprünglichen Relation. Sie ist somit als eine Art Filterung auf Zeilenebene zu verstehen (das heißt, die Selektion blendet Zeilen aus, die das Kriterium nicht erfüllen).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039; Aus einer Mitarbeitertabelle werden durch Selektion nur die Tupel extrahiert, bei denen das Attribut &#039;&#039;Gehalt &amp;gt; 3000&#039;&#039; ist.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Datenbanken]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik LK]]&lt;br /&gt;
[[Kategorie:FI_I_SDM]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=DCL&amp;diff=2810</id>
		<title>DCL</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=DCL&amp;diff=2810"/>
		<updated>2026-04-17T09:33:33Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: Die Seite wurde neu angelegt: „== Einführung == Die &amp;#039;&amp;#039;&amp;#039;Data Control Language&amp;#039;&amp;#039;&amp;#039; (kurz &amp;#039;&amp;#039;&amp;#039;DCL&amp;#039;&amp;#039;&amp;#039;) ist die Datenüberwachungs- und Berechtigungssprache und ein Teilbereich der Structured Query Language (SQL). Sie wird in relationalen Datenbanksystemen verwendet, um Zugriffsrechte, Rollen und Sicherheitsrichtlinien für Datenbankbenutzer zu verwalten (Berechtigungen vergeben oder entziehen).  == Abgrenzung und Systemabhängigkeit == Die Implementierung der DCL existiert in unters…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
Die &#039;&#039;&#039;Data Control Language&#039;&#039;&#039; (kurz &#039;&#039;&#039;DCL&#039;&#039;&#039;) ist die Datenüberwachungs- und Berechtigungssprache und ein Teilbereich der [[SQL|Structured Query Language (SQL)]]. Sie wird in relationalen Datenbanksystemen verwendet, um Zugriffsrechte, Rollen und Sicherheitsrichtlinien für Datenbankbenutzer zu verwalten (Berechtigungen vergeben oder entziehen).&lt;br /&gt;
&lt;br /&gt;
== Abgrenzung und Systemabhängigkeit ==&lt;br /&gt;
Die Implementierung der DCL existiert in unterschiedlichen Datenbanksystemen in verschiedenen Ausprägungen. Es gibt in der Datenbankwelt keine absolut einheitliche Trennung der SQL-Teilsprachen: Einige Datenbank-Hersteller verwenden den isolierten Begriff der DCL nicht und zählen die Berechtigungsbefehle stattdessen mit zur [[Data Definition Language]] (DDL) oder zur Systemverwaltung.&lt;br /&gt;
&lt;br /&gt;
== Die Kernbefehle der DCL ==&lt;br /&gt;
Die Zugriffssteuerung in SQL basiert primär auf zwei fundamentalen Befehlen: &#039;&#039;&#039;GRANT&#039;&#039;&#039; und &#039;&#039;&#039;REVOKE&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== GRANT (Rechte vergeben) ===&lt;br /&gt;
Mit dem Befehl `GRANT` werden einem Datenbankbenutzer oder einer Benutzerrolle spezifische Berechtigungen (Privilegien) für bestimmte Datenbankobjekte (wie Tabellen, Views oder die gesamte Datenbank) erteilt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
GRANT &amp;lt;Privilegien&amp;gt; ON &amp;lt;Datenbankobjekt&amp;gt; TO &amp;lt;Benutzer&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
Einem Benutzer namens &#039;&#039;mitarbeiter&#039;&#039; wird das Recht gegeben, Datensätze in der Tabelle `salon` der Datenbank `friseur` zu lesen (`SELECT`) und neue Datensätze hinzuzufügen (`INSERT`).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
GRANT SELECT, INSERT ON friseur.salon TO &#039;mitarbeiter&#039;@&#039;localhost&#039;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== REVOKE (Rechte entziehen) ===&lt;br /&gt;
Der Befehl `REVOKE` ist das Gegenstück zu `GRANT` und wird verwendet, um zuvor erteilte Berechtigungen wieder zu entziehen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
REVOKE &amp;lt;Privilegien&amp;gt; ON &amp;lt;Datenbankobjekt&amp;gt; FROM &amp;lt;Benutzer&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
Dem Benutzer &#039;&#039;mitarbeiter&#039;&#039; wird das Recht, Datensätze hinzuzufügen, wieder entzogen. Das Leserecht bleibt dabei bestehen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
REVOKE INSERT ON friseur.salon FROM &#039;mitarbeiter&#039;@&#039;localhost&#039;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Typische Berechtigungen (Privilegien) ==&lt;br /&gt;
Beim Vergeben oder Entziehen von Rechten können verschiedene Abstufungen vorgenommen werden. Zu den gängigsten Privilegien gehören:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Privileg !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;SELECT&#039;&#039;&#039; || Erlaubt das Abfragen/Lesen von Daten (DQL).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;INSERT&#039;&#039;&#039; || Erlaubt das Einfügen neuer Datensätze (DML).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UPDATE&#039;&#039;&#039; || Erlaubt das Ändern bestehender Datensätze (DML).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DELETE&#039;&#039;&#039; || Erlaubt das Löschen von Datensätzen (DML).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;ALL PRIVILEGES&#039;&#039;&#039; || Erteilt alle verfügbaren Rechte für das angegebene Objekt (oft Administratoren vorbehalten).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Herstellerspezifische Umsetzung (MySQL) ==&lt;br /&gt;
Da die genaue Syntax für die Benutzerverwaltung (z. B. das Anlegen von Benutzern mit Passwörtern) stark vom eingesetzten Datenbankmanagementsystem abhängt, weichen die Befehle in der Praxis oft leicht ab. Der exakte Befehlsumfang der DCL für das Produkt MySQL (von Oracle) kann im offiziellen MySQL-Manual nachgeschlagen werden.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:SQL]]&lt;br /&gt;
[[Kategorie:Datenbanken]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik LK]]&lt;br /&gt;
[[Kategorie:FI_I_SDM]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=DDL&amp;diff=2809</id>
		<title>DDL</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=DDL&amp;diff=2809"/>
		<updated>2026-04-17T09:31:42Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einstieg ==&lt;br /&gt;
Die &#039;&#039;&#039;Data Definition Language&#039;&#039;&#039; (kurz &#039;&#039;&#039;DDL&#039;&#039;&#039;) ist ein fundamentaler Bestandteil von SQL. Die DDL wird eingesetzt, um das Datenschema in relationalen Datenbanken zu beschreiben, zu ändern oder zu entfernen. Das Erstellen von Tabellen mit Hilfe der DDL ist in der Regel der letzte strukturelle Schritt im Datenbankenentwicklungsprozess, bevor Daten eingefügt werden können.&lt;br /&gt;
[[Datei:ERM-Softwarentwicklungsprozess.png|mini]]&lt;br /&gt;
[[Datei:DBMS-MySQL.png|mini|Darstellung in der MySQL-Workbench]]&lt;br /&gt;
[[Datei:Schlüssel-SchlüsselInTabellen.png|mini]]&lt;br /&gt;
== CREATE SCHEMA ==&lt;br /&gt;
Alle Datenbank-Tabellen befinden sich in einem Schema. Dieses Schema repräsentiert die Datenbank an sich. Bevor Tabellen angelegt werden können, muss ein Schema vorhanden sein. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE SCHEMA &amp;lt;Schema-Name&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE SCHEMA friseur;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einem Datenbankmanagementsystem (DBMS) können mehrere Schemata verwaltet werden. Daher ist sicherzustellen, dass sich alle Anweisungen der SQL eindeutig auf ein Schema beziehen lassen, damit das DBMS die angesprochenen Tabellen identifizieren kann. Der Bezug zu einem Schema ergibt sich aus der Punktnotation:&lt;br /&gt;
&amp;lt;code&amp;gt;Schemaname.Tabellenname&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Schema `friseur` verwaltet alle Salons. Eine Tabelle `Salon` lässt sich also über `friseur.salon` ansprechen.&lt;br /&gt;
&lt;br /&gt;
== CREATE TABLE ==&lt;br /&gt;
Tabellen stellen die Grundstruktur für die Speicherung von Daten in der Datenbank dar. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE &amp;lt;Tabellen_Name&amp;gt; (&lt;br /&gt;
    &amp;lt;Spalte 1&amp;gt; &amp;lt;Datentyp_für_Spalte_1&amp;gt;, &lt;br /&gt;
    &amp;lt;Spalte 2&amp;gt; &amp;lt;Datentyp_für_Spalte_2&amp;gt;,&lt;br /&gt;
    ... &lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039;&lt;br /&gt;
Zur Erstellung einer einfachen Mitarbeitertabelle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur Erstellung einer simplen Salontabelle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE salon (&lt;br /&gt;
    idsalon INT, &lt;br /&gt;
    name VARCHAR(45)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CONSTRAINTs (Einschränkungen) ==&lt;br /&gt;
CONSTRAINT heißt übersetzt &amp;quot;Zwang&amp;quot; oder &amp;quot;Einschränkung&amp;quot;. Ein CONSTRAINT ist eine Bedingung, der ein Datensatz zwingend entsprechen muss. Wenn eine der aktuell gültigen Bedingungen verletzt wird, wird der betreffende Datensatz nicht gespeichert. Hierdurch wird die &#039;&#039;&#039;Datenkonsistenz&#039;&#039;&#039; gewährleistet. &lt;br /&gt;
&lt;br /&gt;
Die Befehle `PRIMARY KEY`, `FOREIGN KEY`, `NOT NULL` und `UNIQUE` fallen in die Kategorie CONSTRAINT. Nach der Attributliste wird das Schlüsselwort `CONSTRAINT` gesetzt (wobei das Wort `CONSTRAINT` selbst oft optional ist, wenn man die Standardnamen des DBMS akzeptiert).&lt;br /&gt;
&lt;br /&gt;
=== Primärschlüssel (PRIMARY KEY) ===&lt;br /&gt;
Es gibt drei Varianten, um die Entitätsintegrität (Eindeutigkeit) mit Hilfe eines Primärschlüssels zu definieren:&lt;br /&gt;
&lt;br /&gt;
1. &#039;&#039;&#039;Am Ende der Attributliste:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    PRIMARY KEY (idmitarbeiter)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. &#039;&#039;&#039;Direkt in der Spaltendefinition:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT PRIMARY KEY, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3. &#039;&#039;&#039;Mit explizitem CONSTRAINT-Namen:&#039;&#039;&#039; (Im Beispiel &amp;quot;pk&amp;quot; für primary key)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    fksalon INT, &lt;br /&gt;
    CONSTRAINT pk PRIMARY KEY (idmitarbeiter)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fremdschlüssel (FOREIGN KEY) ===&lt;br /&gt;
Mit einem Fremdschlüssel wird eine Spalte der einen Tabelle mit einer gleichartigen Spalte einer anderen Tabelle (meist dem Primärschlüssel) verknüpft, um referenzielle Integrität sicherzustellen. Beide Spalten müssen den exakt gleichen Datentyp besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Die referenzierte Tabelle (hier `salon`) muss bereits existieren, bevor der Fremdschlüssel in der referenzierenden Tabelle (`mitarbeiter`) angelegt werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    fksalon INT, &lt;br /&gt;
    CONSTRAINT pk PRIMARY KEY (idmitarbeiter), &lt;br /&gt;
    CONSTRAINT fk FOREIGN KEY (fksalon) REFERENCES salon(idsalon)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== FOREIGN KEY Optionen (Referenzielle Aktionen) ====&lt;br /&gt;
Diese Optionen bestimmen das Verhalten der Detailtabelle, wenn in der Primärtabelle Datensätze geändert (`ON UPDATE`) oder gelöscht (`ON DELETE`) werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;NO ACTION / RESTRICT&#039;&#039;&#039;: Die Änderung oder Löschung in der Primärtabelle wird verweigert und mit einem Fehler abgebrochen, falls abhängige Datensätze existieren.&lt;br /&gt;
* &#039;&#039;&#039;CASCADE&#039;&#039;&#039;: Die Änderung (z. B. eine neue ID) oder die Löschung wird &amp;quot;kaskadierend&amp;quot; an die Detailtabelle weitergegeben. Löscht man den Salon, werden alle Mitarbeiter dieses Salons ebenfalls gelöscht.&lt;br /&gt;
* &#039;&#039;&#039;SET NULL&#039;&#039;&#039;: Wird der Primärschlüssel gelöscht oder geändert, wird der Fremdschlüssel in der Detailtabelle auf `NULL` gesetzt (nur möglich, wenn die Spalte nicht als `NOT NULL` definiert ist).&lt;br /&gt;
&lt;br /&gt;
=== NOT NULL ===&lt;br /&gt;
Die Schlüsselwörter `NULL` bzw. `NOT NULL` legen fest, ob leere Werte in der Spalte zulässig sind. Der Standardwert ist &amp;quot;zulässig&amp;quot; (`NULL`). Primärschlüssel sollten grundsätzlich als `NOT NULL` deklariert werden.&lt;br /&gt;
&lt;br /&gt;
=== AUTO_INCREMENT / AUTOINCREMENT ===&lt;br /&gt;
Dieses Schlüsselwort legt fest, dass die Werte in der Spalte automatisch vom DBMS hochgezählt werden (z. B. für IDs).&lt;br /&gt;
* &#039;&#039;&#039;In MySQL:&#039;&#039;&#039; `idSalon INT AUTO_INCREMENT`&lt;br /&gt;
* &#039;&#039;&#039;In MS Access:&#039;&#039;&#039; `idSalon AUTOINCREMENT` (wird hier wie ein eigener Datentyp behandelt).&lt;br /&gt;
&lt;br /&gt;
=== DEFAULT ===&lt;br /&gt;
Legt einen Standardwert fest, der eingesetzt wird, wenn bei einem neuen Datensatz kein Wert für diese Spalte übergeben wird. (Achtung: Wird in MS Access in reinem DDL oft nicht nativ unterstützt).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
fksalon INT NOT NULL DEFAULT 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== UNIQUE ===&lt;br /&gt;
Sorgt dafür, dass innerhalb einer Spalte (oder Spaltenkombination) kein Wert doppelt auftreten darf.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Komplexes Beispiel (Zusammenführung aller Constraints):&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT NOT NULL AUTO_INCREMENT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    fksalon INT NOT NULL DEFAULT 1, &lt;br /&gt;
    CONSTRAINT pk_mitarbeiter PRIMARY KEY (idmitarbeiter), &lt;br /&gt;
    CONSTRAINT fk_salon FOREIGN KEY (fksalon) REFERENCES salon(idsalon), &lt;br /&gt;
    CONSTRAINT uq_nachname UNIQUE (nachname)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ALTER TABLE ==&lt;br /&gt;
`ALTER TABLE` ermöglicht das nachträgliche Ändern der Struktur einer vorhandenen Tabelle. &lt;br /&gt;
&lt;br /&gt;
Mögliche Änderungsoperatoren sind:&lt;br /&gt;
* &#039;&#039;&#039;ADD&#039;&#039;&#039;: Spalten/Constraints hinzufügen.&lt;br /&gt;
* &#039;&#039;&#039;DROP&#039;&#039;&#039;: Spalten/Constraints entfernen.&lt;br /&gt;
* &#039;&#039;&#039;MODIFY / ALTER COLUMN&#039;&#039;&#039;: Datentypen ändern.&lt;br /&gt;
* &#039;&#039;&#039;RENAME&#039;&#039;&#039;: Umbenennen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039;&lt;br /&gt;
Tabelle umbenennen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE Salon RENAME TO Filiale;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neue Spalte hinzufügen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE Mitarbeiter ADD (istWeiblich BOOLEAN);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DROP TABLE ==&lt;br /&gt;
Löscht eine vollständige Tabelle inklusive aller darin enthaltenen Daten unwiderruflich aus der Datenbank.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TABLE &amp;lt;Tabellen_Name&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CREATE VIEW ==&lt;br /&gt;
Abfragen (DQL) können als sogenannte &#039;&#039;&#039;Views&#039;&#039;&#039; (Sichten/virtuelle Tabellen) in der Datenbank gespeichert werden. Sie verhalten sich bei Abfragen wie echte Tabellen, speichern aber keine eigenen Daten, sondern führen die hinterlegte `SELECT`-Abfrage bei jedem Aufruf dynamisch aus.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW &amp;lt;View-Name&amp;gt; AS &amp;lt;Select-Ausdruck&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW weiblicheMitarbeiter AS &lt;br /&gt;
SELECT vorname, nachname &lt;br /&gt;
FROM mitarbeiter &lt;br /&gt;
WHERE istWeiblich = TRUE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Datenbanken]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik LK]]&lt;br /&gt;
[[Kategorie:FI_I_SDM]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=DDL&amp;diff=2808</id>
		<title>DDL</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=DDL&amp;diff=2808"/>
		<updated>2026-04-17T09:30:47Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einstieg ==&lt;br /&gt;
Die &#039;&#039;&#039;Data Definition Language&#039;&#039;&#039; (kurz &#039;&#039;&#039;DDL&#039;&#039;&#039;) ist ein fundamentaler Bestandteil von SQL. Die DDL wird eingesetzt, um das Datenschema in relationalen Datenbanken zu beschreiben, zu ändern oder zu entfernen. Das Erstellen von Tabellen mit Hilfe der DDL ist in der Regel der letzte strukturelle Schritt im Datenbankenentwicklungsprozess, bevor Daten eingefügt werden können.&lt;br /&gt;
[[Datei:ERM-Softwarentwicklungsprozess.png|mini]]&lt;br /&gt;
[[Datei:Schlüssel-SchlüsselInTabellen.png|mini]]&lt;br /&gt;
== CREATE SCHEMA ==&lt;br /&gt;
Alle Datenbank-Tabellen befinden sich in einem Schema. Dieses Schema repräsentiert die Datenbank an sich. Bevor Tabellen angelegt werden können, muss ein Schema vorhanden sein. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE SCHEMA &amp;lt;Schema-Name&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE SCHEMA friseur;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einem Datenbankmanagementsystem (DBMS) können mehrere Schemata verwaltet werden. Daher ist sicherzustellen, dass sich alle Anweisungen der SQL eindeutig auf ein Schema beziehen lassen, damit das DBMS die angesprochenen Tabellen identifizieren kann. Der Bezug zu einem Schema ergibt sich aus der Punktnotation:&lt;br /&gt;
&amp;lt;code&amp;gt;Schemaname.Tabellenname&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Schema `friseur` verwaltet alle Salons. Eine Tabelle `Salon` lässt sich also über `friseur.salon` ansprechen.&lt;br /&gt;
&lt;br /&gt;
== CREATE TABLE ==&lt;br /&gt;
Tabellen stellen die Grundstruktur für die Speicherung von Daten in der Datenbank dar. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE &amp;lt;Tabellen_Name&amp;gt; (&lt;br /&gt;
    &amp;lt;Spalte 1&amp;gt; &amp;lt;Datentyp_für_Spalte_1&amp;gt;, &lt;br /&gt;
    &amp;lt;Spalte 2&amp;gt; &amp;lt;Datentyp_für_Spalte_2&amp;gt;,&lt;br /&gt;
    ... &lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039;&lt;br /&gt;
Zur Erstellung einer einfachen Mitarbeitertabelle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur Erstellung einer simplen Salontabelle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE salon (&lt;br /&gt;
    idsalon INT, &lt;br /&gt;
    name VARCHAR(45)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CONSTRAINTs (Einschränkungen) ==&lt;br /&gt;
CONSTRAINT heißt übersetzt &amp;quot;Zwang&amp;quot; oder &amp;quot;Einschränkung&amp;quot;. Ein CONSTRAINT ist eine Bedingung, der ein Datensatz zwingend entsprechen muss. Wenn eine der aktuell gültigen Bedingungen verletzt wird, wird der betreffende Datensatz nicht gespeichert. Hierdurch wird die &#039;&#039;&#039;Datenkonsistenz&#039;&#039;&#039; gewährleistet. &lt;br /&gt;
&lt;br /&gt;
Die Befehle `PRIMARY KEY`, `FOREIGN KEY`, `NOT NULL` und `UNIQUE` fallen in die Kategorie CONSTRAINT. Nach der Attributliste wird das Schlüsselwort `CONSTRAINT` gesetzt (wobei das Wort `CONSTRAINT` selbst oft optional ist, wenn man die Standardnamen des DBMS akzeptiert).&lt;br /&gt;
&lt;br /&gt;
=== Primärschlüssel (PRIMARY KEY) ===&lt;br /&gt;
Es gibt drei Varianten, um die Entitätsintegrität (Eindeutigkeit) mit Hilfe eines Primärschlüssels zu definieren:&lt;br /&gt;
&lt;br /&gt;
1. &#039;&#039;&#039;Am Ende der Attributliste:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    PRIMARY KEY (idmitarbeiter)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. &#039;&#039;&#039;Direkt in der Spaltendefinition:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT PRIMARY KEY, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3. &#039;&#039;&#039;Mit explizitem CONSTRAINT-Namen:&#039;&#039;&#039; (Im Beispiel &amp;quot;pk&amp;quot; für primary key)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    fksalon INT, &lt;br /&gt;
    CONSTRAINT pk PRIMARY KEY (idmitarbeiter)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fremdschlüssel (FOREIGN KEY) ===&lt;br /&gt;
Mit einem Fremdschlüssel wird eine Spalte der einen Tabelle mit einer gleichartigen Spalte einer anderen Tabelle (meist dem Primärschlüssel) verknüpft, um referenzielle Integrität sicherzustellen. Beide Spalten müssen den exakt gleichen Datentyp besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Die referenzierte Tabelle (hier `salon`) muss bereits existieren, bevor der Fremdschlüssel in der referenzierenden Tabelle (`mitarbeiter`) angelegt werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    fksalon INT, &lt;br /&gt;
    CONSTRAINT pk PRIMARY KEY (idmitarbeiter), &lt;br /&gt;
    CONSTRAINT fk FOREIGN KEY (fksalon) REFERENCES salon(idsalon)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== FOREIGN KEY Optionen (Referenzielle Aktionen) ====&lt;br /&gt;
Diese Optionen bestimmen das Verhalten der Detailtabelle, wenn in der Primärtabelle Datensätze geändert (`ON UPDATE`) oder gelöscht (`ON DELETE`) werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;NO ACTION / RESTRICT&#039;&#039;&#039;: Die Änderung oder Löschung in der Primärtabelle wird verweigert und mit einem Fehler abgebrochen, falls abhängige Datensätze existieren.&lt;br /&gt;
* &#039;&#039;&#039;CASCADE&#039;&#039;&#039;: Die Änderung (z. B. eine neue ID) oder die Löschung wird &amp;quot;kaskadierend&amp;quot; an die Detailtabelle weitergegeben. Löscht man den Salon, werden alle Mitarbeiter dieses Salons ebenfalls gelöscht.&lt;br /&gt;
* &#039;&#039;&#039;SET NULL&#039;&#039;&#039;: Wird der Primärschlüssel gelöscht oder geändert, wird der Fremdschlüssel in der Detailtabelle auf `NULL` gesetzt (nur möglich, wenn die Spalte nicht als `NOT NULL` definiert ist).&lt;br /&gt;
&lt;br /&gt;
=== NOT NULL ===&lt;br /&gt;
Die Schlüsselwörter `NULL` bzw. `NOT NULL` legen fest, ob leere Werte in der Spalte zulässig sind. Der Standardwert ist &amp;quot;zulässig&amp;quot; (`NULL`). Primärschlüssel sollten grundsätzlich als `NOT NULL` deklariert werden.&lt;br /&gt;
&lt;br /&gt;
=== AUTO_INCREMENT / AUTOINCREMENT ===&lt;br /&gt;
Dieses Schlüsselwort legt fest, dass die Werte in der Spalte automatisch vom DBMS hochgezählt werden (z. B. für IDs).&lt;br /&gt;
* &#039;&#039;&#039;In MySQL:&#039;&#039;&#039; `idSalon INT AUTO_INCREMENT`&lt;br /&gt;
* &#039;&#039;&#039;In MS Access:&#039;&#039;&#039; `idSalon AUTOINCREMENT` (wird hier wie ein eigener Datentyp behandelt).&lt;br /&gt;
&lt;br /&gt;
=== DEFAULT ===&lt;br /&gt;
Legt einen Standardwert fest, der eingesetzt wird, wenn bei einem neuen Datensatz kein Wert für diese Spalte übergeben wird. (Achtung: Wird in MS Access in reinem DDL oft nicht nativ unterstützt).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
fksalon INT NOT NULL DEFAULT 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== UNIQUE ===&lt;br /&gt;
Sorgt dafür, dass innerhalb einer Spalte (oder Spaltenkombination) kein Wert doppelt auftreten darf.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Komplexes Beispiel (Zusammenführung aller Constraints):&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT NOT NULL AUTO_INCREMENT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    fksalon INT NOT NULL DEFAULT 1, &lt;br /&gt;
    CONSTRAINT pk_mitarbeiter PRIMARY KEY (idmitarbeiter), &lt;br /&gt;
    CONSTRAINT fk_salon FOREIGN KEY (fksalon) REFERENCES salon(idsalon), &lt;br /&gt;
    CONSTRAINT uq_nachname UNIQUE (nachname)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ALTER TABLE ==&lt;br /&gt;
`ALTER TABLE` ermöglicht das nachträgliche Ändern der Struktur einer vorhandenen Tabelle. &lt;br /&gt;
&lt;br /&gt;
Mögliche Änderungsoperatoren sind:&lt;br /&gt;
* &#039;&#039;&#039;ADD&#039;&#039;&#039;: Spalten/Constraints hinzufügen.&lt;br /&gt;
* &#039;&#039;&#039;DROP&#039;&#039;&#039;: Spalten/Constraints entfernen.&lt;br /&gt;
* &#039;&#039;&#039;MODIFY / ALTER COLUMN&#039;&#039;&#039;: Datentypen ändern.&lt;br /&gt;
* &#039;&#039;&#039;RENAME&#039;&#039;&#039;: Umbenennen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039;&lt;br /&gt;
Tabelle umbenennen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE Salon RENAME TO Filiale;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neue Spalte hinzufügen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE Mitarbeiter ADD (istWeiblich BOOLEAN);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DROP TABLE ==&lt;br /&gt;
Löscht eine vollständige Tabelle inklusive aller darin enthaltenen Daten unwiderruflich aus der Datenbank.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TABLE &amp;lt;Tabellen_Name&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CREATE VIEW ==&lt;br /&gt;
Abfragen (DQL) können als sogenannte &#039;&#039;&#039;Views&#039;&#039;&#039; (Sichten/virtuelle Tabellen) in der Datenbank gespeichert werden. Sie verhalten sich bei Abfragen wie echte Tabellen, speichern aber keine eigenen Daten, sondern führen die hinterlegte `SELECT`-Abfrage bei jedem Aufruf dynamisch aus.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW &amp;lt;View-Name&amp;gt; AS &amp;lt;Select-Ausdruck&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW weiblicheMitarbeiter AS &lt;br /&gt;
SELECT vorname, nachname &lt;br /&gt;
FROM mitarbeiter &lt;br /&gt;
WHERE istWeiblich = TRUE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Datenbanken]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik LK]]&lt;br /&gt;
[[Kategorie:FI_I_SDM]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=DDL&amp;diff=2807</id>
		<title>DDL</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=DDL&amp;diff=2807"/>
		<updated>2026-04-17T09:28:49Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einstieg ==&lt;br /&gt;
Die &#039;&#039;&#039;Data Definition Language&#039;&#039;&#039; (kurz &#039;&#039;&#039;DDL&#039;&#039;&#039;) ist ein fundamentaler Bestandteil von SQL. Die DDL wird eingesetzt, um das Datenschema in relationalen Datenbanken zu beschreiben, zu ändern oder zu entfernen. Das Erstellen von Tabellen mit Hilfe der DDL ist in der Regel der letzte strukturelle Schritt im Datenbankenentwicklungsprozess, bevor Daten eingefügt werden können.&lt;br /&gt;
[[Datei:ERM-Softwarentwicklungsprozess.png|mini]]&lt;br /&gt;
== CREATE SCHEMA ==&lt;br /&gt;
Alle Datenbank-Tabellen befinden sich in einem Schema. Dieses Schema repräsentiert die Datenbank an sich. Bevor Tabellen angelegt werden können, muss ein Schema vorhanden sein. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE SCHEMA &amp;lt;Schema-Name&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE SCHEMA friseur;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einem Datenbankmanagementsystem (DBMS) können mehrere Schemata verwaltet werden. Daher ist sicherzustellen, dass sich alle Anweisungen der SQL eindeutig auf ein Schema beziehen lassen, damit das DBMS die angesprochenen Tabellen identifizieren kann. Der Bezug zu einem Schema ergibt sich aus der Punktnotation:&lt;br /&gt;
&amp;lt;code&amp;gt;Schemaname.Tabellenname&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Schema `friseur` verwaltet alle Salons. Eine Tabelle `Salon` lässt sich also über `friseur.salon` ansprechen.&lt;br /&gt;
&lt;br /&gt;
== CREATE TABLE ==&lt;br /&gt;
Tabellen stellen die Grundstruktur für die Speicherung von Daten in der Datenbank dar. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE &amp;lt;Tabellen_Name&amp;gt; (&lt;br /&gt;
    &amp;lt;Spalte 1&amp;gt; &amp;lt;Datentyp_für_Spalte_1&amp;gt;, &lt;br /&gt;
    &amp;lt;Spalte 2&amp;gt; &amp;lt;Datentyp_für_Spalte_2&amp;gt;,&lt;br /&gt;
    ... &lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039;&lt;br /&gt;
Zur Erstellung einer einfachen Mitarbeitertabelle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur Erstellung einer simplen Salontabelle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE salon (&lt;br /&gt;
    idsalon INT, &lt;br /&gt;
    name VARCHAR(45)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CONSTRAINTs (Einschränkungen) ==&lt;br /&gt;
CONSTRAINT heißt übersetzt &amp;quot;Zwang&amp;quot; oder &amp;quot;Einschränkung&amp;quot;. Ein CONSTRAINT ist eine Bedingung, der ein Datensatz zwingend entsprechen muss. Wenn eine der aktuell gültigen Bedingungen verletzt wird, wird der betreffende Datensatz nicht gespeichert. Hierdurch wird die &#039;&#039;&#039;Datenkonsistenz&#039;&#039;&#039; gewährleistet. &lt;br /&gt;
&lt;br /&gt;
Die Befehle `PRIMARY KEY`, `FOREIGN KEY`, `NOT NULL` und `UNIQUE` fallen in die Kategorie CONSTRAINT. Nach der Attributliste wird das Schlüsselwort `CONSTRAINT` gesetzt (wobei das Wort `CONSTRAINT` selbst oft optional ist, wenn man die Standardnamen des DBMS akzeptiert).&lt;br /&gt;
&lt;br /&gt;
=== Primärschlüssel (PRIMARY KEY) ===&lt;br /&gt;
Es gibt drei Varianten, um die Entitätsintegrität (Eindeutigkeit) mit Hilfe eines Primärschlüssels zu definieren:&lt;br /&gt;
&lt;br /&gt;
1. &#039;&#039;&#039;Am Ende der Attributliste:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    PRIMARY KEY (idmitarbeiter)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. &#039;&#039;&#039;Direkt in der Spaltendefinition:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT PRIMARY KEY, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3. &#039;&#039;&#039;Mit explizitem CONSTRAINT-Namen:&#039;&#039;&#039; (Im Beispiel &amp;quot;pk&amp;quot; für primary key)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    fksalon INT, &lt;br /&gt;
    CONSTRAINT pk PRIMARY KEY (idmitarbeiter)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fremdschlüssel (FOREIGN KEY) ===&lt;br /&gt;
Mit einem Fremdschlüssel wird eine Spalte der einen Tabelle mit einer gleichartigen Spalte einer anderen Tabelle (meist dem Primärschlüssel) verknüpft, um referenzielle Integrität sicherzustellen. Beide Spalten müssen den exakt gleichen Datentyp besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Die referenzierte Tabelle (hier `salon`) muss bereits existieren, bevor der Fremdschlüssel in der referenzierenden Tabelle (`mitarbeiter`) angelegt werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    fksalon INT, &lt;br /&gt;
    CONSTRAINT pk PRIMARY KEY (idmitarbeiter), &lt;br /&gt;
    CONSTRAINT fk FOREIGN KEY (fksalon) REFERENCES salon(idsalon)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== FOREIGN KEY Optionen (Referenzielle Aktionen) ====&lt;br /&gt;
Diese Optionen bestimmen das Verhalten der Detailtabelle, wenn in der Primärtabelle Datensätze geändert (`ON UPDATE`) oder gelöscht (`ON DELETE`) werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;NO ACTION / RESTRICT&#039;&#039;&#039;: Die Änderung oder Löschung in der Primärtabelle wird verweigert und mit einem Fehler abgebrochen, falls abhängige Datensätze existieren.&lt;br /&gt;
* &#039;&#039;&#039;CASCADE&#039;&#039;&#039;: Die Änderung (z. B. eine neue ID) oder die Löschung wird &amp;quot;kaskadierend&amp;quot; an die Detailtabelle weitergegeben. Löscht man den Salon, werden alle Mitarbeiter dieses Salons ebenfalls gelöscht.&lt;br /&gt;
* &#039;&#039;&#039;SET NULL&#039;&#039;&#039;: Wird der Primärschlüssel gelöscht oder geändert, wird der Fremdschlüssel in der Detailtabelle auf `NULL` gesetzt (nur möglich, wenn die Spalte nicht als `NOT NULL` definiert ist).&lt;br /&gt;
&lt;br /&gt;
=== NOT NULL ===&lt;br /&gt;
Die Schlüsselwörter `NULL` bzw. `NOT NULL` legen fest, ob leere Werte in der Spalte zulässig sind. Der Standardwert ist &amp;quot;zulässig&amp;quot; (`NULL`). Primärschlüssel sollten grundsätzlich als `NOT NULL` deklariert werden.&lt;br /&gt;
&lt;br /&gt;
=== AUTO_INCREMENT / AUTOINCREMENT ===&lt;br /&gt;
Dieses Schlüsselwort legt fest, dass die Werte in der Spalte automatisch vom DBMS hochgezählt werden (z. B. für IDs).&lt;br /&gt;
* &#039;&#039;&#039;In MySQL:&#039;&#039;&#039; `idSalon INT AUTO_INCREMENT`&lt;br /&gt;
* &#039;&#039;&#039;In MS Access:&#039;&#039;&#039; `idSalon AUTOINCREMENT` (wird hier wie ein eigener Datentyp behandelt).&lt;br /&gt;
&lt;br /&gt;
=== DEFAULT ===&lt;br /&gt;
Legt einen Standardwert fest, der eingesetzt wird, wenn bei einem neuen Datensatz kein Wert für diese Spalte übergeben wird. (Achtung: Wird in MS Access in reinem DDL oft nicht nativ unterstützt).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
fksalon INT NOT NULL DEFAULT 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== UNIQUE ===&lt;br /&gt;
Sorgt dafür, dass innerhalb einer Spalte (oder Spaltenkombination) kein Wert doppelt auftreten darf.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Komplexes Beispiel (Zusammenführung aller Constraints):&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT NOT NULL AUTO_INCREMENT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    fksalon INT NOT NULL DEFAULT 1, &lt;br /&gt;
    CONSTRAINT pk_mitarbeiter PRIMARY KEY (idmitarbeiter), &lt;br /&gt;
    CONSTRAINT fk_salon FOREIGN KEY (fksalon) REFERENCES salon(idsalon), &lt;br /&gt;
    CONSTRAINT uq_nachname UNIQUE (nachname)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ALTER TABLE ==&lt;br /&gt;
`ALTER TABLE` ermöglicht das nachträgliche Ändern der Struktur einer vorhandenen Tabelle. &lt;br /&gt;
&lt;br /&gt;
Mögliche Änderungsoperatoren sind:&lt;br /&gt;
* &#039;&#039;&#039;ADD&#039;&#039;&#039;: Spalten/Constraints hinzufügen.&lt;br /&gt;
* &#039;&#039;&#039;DROP&#039;&#039;&#039;: Spalten/Constraints entfernen.&lt;br /&gt;
* &#039;&#039;&#039;MODIFY / ALTER COLUMN&#039;&#039;&#039;: Datentypen ändern.&lt;br /&gt;
* &#039;&#039;&#039;RENAME&#039;&#039;&#039;: Umbenennen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039;&lt;br /&gt;
Tabelle umbenennen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE Salon RENAME TO Filiale;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neue Spalte hinzufügen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE Mitarbeiter ADD (istWeiblich BOOLEAN);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DROP TABLE ==&lt;br /&gt;
Löscht eine vollständige Tabelle inklusive aller darin enthaltenen Daten unwiderruflich aus der Datenbank.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TABLE &amp;lt;Tabellen_Name&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CREATE VIEW ==&lt;br /&gt;
Abfragen (DQL) können als sogenannte &#039;&#039;&#039;Views&#039;&#039;&#039; (Sichten/virtuelle Tabellen) in der Datenbank gespeichert werden. Sie verhalten sich bei Abfragen wie echte Tabellen, speichern aber keine eigenen Daten, sondern führen die hinterlegte `SELECT`-Abfrage bei jedem Aufruf dynamisch aus.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW &amp;lt;View-Name&amp;gt; AS &amp;lt;Select-Ausdruck&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW weiblicheMitarbeiter AS &lt;br /&gt;
SELECT vorname, nachname &lt;br /&gt;
FROM mitarbeiter &lt;br /&gt;
WHERE istWeiblich = TRUE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Datenbanken]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik LK]]&lt;br /&gt;
[[Kategorie:FI_I_SDM]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=DDL&amp;diff=2806</id>
		<title>DDL</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=DDL&amp;diff=2806"/>
		<updated>2026-04-17T09:26:33Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einstieg ==&lt;br /&gt;
Die &#039;&#039;&#039;Data Definition Language&#039;&#039;&#039; (kurz &#039;&#039;&#039;DDL&#039;&#039;&#039;) ist ein fundamentaler Bestandteil von SQL. Die DDL wird eingesetzt, um das Datenschema in relationalen Datenbanken zu beschreiben, zu ändern oder zu entfernen. Das Erstellen von Tabellen mit Hilfe der DDL ist in der Regel der letzte strukturelle Schritt im Datenbankenentwicklungsprozess, bevor Daten eingefügt werden können.&lt;br /&gt;
&lt;br /&gt;
== CREATE SCHEMA ==&lt;br /&gt;
Alle Datenbank-Tabellen befinden sich in einem Schema. Dieses Schema repräsentiert die Datenbank an sich. Bevor Tabellen angelegt werden können, muss ein Schema vorhanden sein. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE SCHEMA &amp;lt;Schema-Name&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE SCHEMA friseur;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einem Datenbankmanagementsystem (DBMS) können mehrere Schemata verwaltet werden. Daher ist sicherzustellen, dass sich alle Anweisungen der SQL eindeutig auf ein Schema beziehen lassen, damit das DBMS die angesprochenen Tabellen identifizieren kann. Der Bezug zu einem Schema ergibt sich aus der Punktnotation:&lt;br /&gt;
&amp;lt;code&amp;gt;Schemaname.Tabellenname&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Schema `friseur` verwaltet alle Salons. Eine Tabelle `Salon` lässt sich also über `friseur.salon` ansprechen.&lt;br /&gt;
&lt;br /&gt;
== CREATE TABLE ==&lt;br /&gt;
Tabellen stellen die Grundstruktur für die Speicherung von Daten in der Datenbank dar. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE &amp;lt;Tabellen_Name&amp;gt; (&lt;br /&gt;
    &amp;lt;Spalte 1&amp;gt; &amp;lt;Datentyp_für_Spalte_1&amp;gt;, &lt;br /&gt;
    &amp;lt;Spalte 2&amp;gt; &amp;lt;Datentyp_für_Spalte_2&amp;gt;,&lt;br /&gt;
    ... &lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039;&lt;br /&gt;
Zur Erstellung einer einfachen Mitarbeitertabelle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur Erstellung einer simplen Salontabelle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE salon (&lt;br /&gt;
    idsalon INT, &lt;br /&gt;
    name VARCHAR(45)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CONSTRAINTs (Einschränkungen) ==&lt;br /&gt;
CONSTRAINT heißt übersetzt &amp;quot;Zwang&amp;quot; oder &amp;quot;Einschränkung&amp;quot;. Ein CONSTRAINT ist eine Bedingung, der ein Datensatz zwingend entsprechen muss. Wenn eine der aktuell gültigen Bedingungen verletzt wird, wird der betreffende Datensatz nicht gespeichert. Hierdurch wird die &#039;&#039;&#039;Datenkonsistenz&#039;&#039;&#039; gewährleistet. &lt;br /&gt;
&lt;br /&gt;
Die Befehle `PRIMARY KEY`, `FOREIGN KEY`, `NOT NULL` und `UNIQUE` fallen in die Kategorie CONSTRAINT. Nach der Attributliste wird das Schlüsselwort `CONSTRAINT` gesetzt (wobei das Wort `CONSTRAINT` selbst oft optional ist, wenn man die Standardnamen des DBMS akzeptiert).&lt;br /&gt;
&lt;br /&gt;
=== Primärschlüssel (PRIMARY KEY) ===&lt;br /&gt;
Es gibt drei Varianten, um die Entitätsintegrität (Eindeutigkeit) mit Hilfe eines Primärschlüssels zu definieren:&lt;br /&gt;
&lt;br /&gt;
1. &#039;&#039;&#039;Am Ende der Attributliste:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    PRIMARY KEY (idmitarbeiter)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. &#039;&#039;&#039;Direkt in der Spaltendefinition:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT PRIMARY KEY, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3. &#039;&#039;&#039;Mit explizitem CONSTRAINT-Namen:&#039;&#039;&#039; (Im Beispiel &amp;quot;pk&amp;quot; für primary key)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    fksalon INT, &lt;br /&gt;
    CONSTRAINT pk PRIMARY KEY (idmitarbeiter)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fremdschlüssel (FOREIGN KEY) ===&lt;br /&gt;
Mit einem Fremdschlüssel wird eine Spalte der einen Tabelle mit einer gleichartigen Spalte einer anderen Tabelle (meist dem Primärschlüssel) verknüpft, um referenzielle Integrität sicherzustellen. Beide Spalten müssen den exakt gleichen Datentyp besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Die referenzierte Tabelle (hier `salon`) muss bereits existieren, bevor der Fremdschlüssel in der referenzierenden Tabelle (`mitarbeiter`) angelegt werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    fksalon INT, &lt;br /&gt;
    CONSTRAINT pk PRIMARY KEY (idmitarbeiter), &lt;br /&gt;
    CONSTRAINT fk FOREIGN KEY (fksalon) REFERENCES salon(idsalon)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== FOREIGN KEY Optionen (Referenzielle Aktionen) ====&lt;br /&gt;
Diese Optionen bestimmen das Verhalten der Detailtabelle, wenn in der Primärtabelle Datensätze geändert (`ON UPDATE`) oder gelöscht (`ON DELETE`) werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;NO ACTION / RESTRICT&#039;&#039;&#039;: Die Änderung oder Löschung in der Primärtabelle wird verweigert und mit einem Fehler abgebrochen, falls abhängige Datensätze existieren.&lt;br /&gt;
* &#039;&#039;&#039;CASCADE&#039;&#039;&#039;: Die Änderung (z. B. eine neue ID) oder die Löschung wird &amp;quot;kaskadierend&amp;quot; an die Detailtabelle weitergegeben. Löscht man den Salon, werden alle Mitarbeiter dieses Salons ebenfalls gelöscht.&lt;br /&gt;
* &#039;&#039;&#039;SET NULL&#039;&#039;&#039;: Wird der Primärschlüssel gelöscht oder geändert, wird der Fremdschlüssel in der Detailtabelle auf `NULL` gesetzt (nur möglich, wenn die Spalte nicht als `NOT NULL` definiert ist).&lt;br /&gt;
&lt;br /&gt;
=== NOT NULL ===&lt;br /&gt;
Die Schlüsselwörter `NULL` bzw. `NOT NULL` legen fest, ob leere Werte in der Spalte zulässig sind. Der Standardwert ist &amp;quot;zulässig&amp;quot; (`NULL`). Primärschlüssel sollten grundsätzlich als `NOT NULL` deklariert werden.&lt;br /&gt;
&lt;br /&gt;
=== AUTO_INCREMENT / AUTOINCREMENT ===&lt;br /&gt;
Dieses Schlüsselwort legt fest, dass die Werte in der Spalte automatisch vom DBMS hochgezählt werden (z. B. für IDs).&lt;br /&gt;
* &#039;&#039;&#039;In MySQL:&#039;&#039;&#039; `idSalon INT AUTO_INCREMENT`&lt;br /&gt;
* &#039;&#039;&#039;In MS Access:&#039;&#039;&#039; `idSalon AUTOINCREMENT` (wird hier wie ein eigener Datentyp behandelt).&lt;br /&gt;
&lt;br /&gt;
=== DEFAULT ===&lt;br /&gt;
Legt einen Standardwert fest, der eingesetzt wird, wenn bei einem neuen Datensatz kein Wert für diese Spalte übergeben wird. (Achtung: Wird in MS Access in reinem DDL oft nicht nativ unterstützt).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
fksalon INT NOT NULL DEFAULT 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== UNIQUE ===&lt;br /&gt;
Sorgt dafür, dass innerhalb einer Spalte (oder Spaltenkombination) kein Wert doppelt auftreten darf.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Komplexes Beispiel (Zusammenführung aller Constraints):&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT NOT NULL AUTO_INCREMENT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    fksalon INT NOT NULL DEFAULT 1, &lt;br /&gt;
    CONSTRAINT pk_mitarbeiter PRIMARY KEY (idmitarbeiter), &lt;br /&gt;
    CONSTRAINT fk_salon FOREIGN KEY (fksalon) REFERENCES salon(idsalon), &lt;br /&gt;
    CONSTRAINT uq_nachname UNIQUE (nachname)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ALTER TABLE ==&lt;br /&gt;
`ALTER TABLE` ermöglicht das nachträgliche Ändern der Struktur einer vorhandenen Tabelle. &lt;br /&gt;
&lt;br /&gt;
Mögliche Änderungsoperatoren sind:&lt;br /&gt;
* &#039;&#039;&#039;ADD&#039;&#039;&#039;: Spalten/Constraints hinzufügen.&lt;br /&gt;
* &#039;&#039;&#039;DROP&#039;&#039;&#039;: Spalten/Constraints entfernen.&lt;br /&gt;
* &#039;&#039;&#039;MODIFY / ALTER COLUMN&#039;&#039;&#039;: Datentypen ändern.&lt;br /&gt;
* &#039;&#039;&#039;RENAME&#039;&#039;&#039;: Umbenennen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039;&lt;br /&gt;
Tabelle umbenennen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE Salon RENAME TO Filiale;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neue Spalte hinzufügen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE Mitarbeiter ADD (istWeiblich BOOLEAN);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DROP TABLE ==&lt;br /&gt;
Löscht eine vollständige Tabelle inklusive aller darin enthaltenen Daten unwiderruflich aus der Datenbank.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TABLE &amp;lt;Tabellen_Name&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CREATE VIEW ==&lt;br /&gt;
Abfragen (DQL) können als sogenannte &#039;&#039;&#039;Views&#039;&#039;&#039; (Sichten/virtuelle Tabellen) in der Datenbank gespeichert werden. Sie verhalten sich bei Abfragen wie echte Tabellen, speichern aber keine eigenen Daten, sondern führen die hinterlegte `SELECT`-Abfrage bei jedem Aufruf dynamisch aus.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW &amp;lt;View-Name&amp;gt; AS &amp;lt;Select-Ausdruck&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW weiblicheMitarbeiter AS &lt;br /&gt;
SELECT vorname, nachname &lt;br /&gt;
FROM mitarbeiter &lt;br /&gt;
WHERE istWeiblich = TRUE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Datenbanken]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik LK]]&lt;br /&gt;
[[Kategorie:FI_I_SDM]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=DDL&amp;diff=2805</id>
		<title>DDL</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=DDL&amp;diff=2805"/>
		<updated>2026-04-17T09:24:39Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: Die Seite wurde neu angelegt: „== Einstieg == Die &amp;#039;&amp;#039;&amp;#039;Data Definition Language&amp;#039;&amp;#039;&amp;#039; (kurz &amp;#039;&amp;#039;&amp;#039;DDL&amp;#039;&amp;#039;&amp;#039;) ist ein fundamentaler Bestandteil von SQL. Die DDL wird eingesetzt, um das Datenschema in relationalen Datenbanken zu beschreiben, zu ändern oder zu entfernen. Das Erstellen von Tabellen mit Hilfe der DDL ist in der Regel der letzte strukturelle Schritt im Datenbankenentwicklungsprozess, bevor Daten eingefügt werden können.  ---  == CREATE SCHEMA == Alle Datenbank-Tabellen befinden sich…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einstieg ==&lt;br /&gt;
Die &#039;&#039;&#039;Data Definition Language&#039;&#039;&#039; (kurz &#039;&#039;&#039;DDL&#039;&#039;&#039;) ist ein fundamentaler Bestandteil von SQL. Die DDL wird eingesetzt, um das Datenschema in relationalen Datenbanken zu beschreiben, zu ändern oder zu entfernen. Das Erstellen von Tabellen mit Hilfe der DDL ist in der Regel der letzte strukturelle Schritt im Datenbankenentwicklungsprozess, bevor Daten eingefügt werden können.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== CREATE SCHEMA ==&lt;br /&gt;
Alle Datenbank-Tabellen befinden sich in einem Schema. Dieses Schema repräsentiert die Datenbank an sich. Bevor Tabellen angelegt werden können, muss ein Schema vorhanden sein. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE SCHEMA &amp;lt;Schema-Name&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE SCHEMA friseur;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einem Datenbankmanagementsystem (DBMS) können mehrere Schemata verwaltet werden. Daher ist sicherzustellen, dass sich alle Anweisungen der SQL eindeutig auf ein Schema beziehen lassen, damit das DBMS die angesprochenen Tabellen identifizieren kann. Der Bezug zu einem Schema ergibt sich aus der Punktnotation:&lt;br /&gt;
&amp;lt;code&amp;gt;Schemaname.Tabellenname&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Schema `friseur` verwaltet alle Salons. Eine Tabelle `Salon` lässt sich also über `friseur.salon` ansprechen.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== CREATE TABLE ==&lt;br /&gt;
Tabellen stellen die Grundstruktur für die Speicherung von Daten in der Datenbank dar. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE &amp;lt;Tabellen_Name&amp;gt; (&lt;br /&gt;
    &amp;lt;Spalte 1&amp;gt; &amp;lt;Datentyp_für_Spalte_1&amp;gt;, &lt;br /&gt;
    &amp;lt;Spalte 2&amp;gt; &amp;lt;Datentyp_für_Spalte_2&amp;gt;,&lt;br /&gt;
    ... &lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039;&lt;br /&gt;
Zur Erstellung einer einfachen Mitarbeitertabelle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur Erstellung einer simplen Salontabelle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE salon (&lt;br /&gt;
    idsalon INT, &lt;br /&gt;
    name VARCHAR(45)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== CONSTRAINTs (Einschränkungen) ==&lt;br /&gt;
CONSTRAINT heißt übersetzt &amp;quot;Zwang&amp;quot; oder &amp;quot;Einschränkung&amp;quot;. Ein CONSTRAINT ist eine Bedingung, der ein Datensatz zwingend entsprechen muss. Wenn eine der aktuell gültigen Bedingungen verletzt wird, wird der betreffende Datensatz nicht gespeichert. Hierdurch wird die &#039;&#039;&#039;Datenkonsistenz&#039;&#039;&#039; gewährleistet. &lt;br /&gt;
&lt;br /&gt;
Die Befehle `PRIMARY KEY`, `FOREIGN KEY`, `NOT NULL` und `UNIQUE` fallen in die Kategorie CONSTRAINT. Nach der Attributliste wird das Schlüsselwort `CONSTRAINT` gesetzt (wobei das Wort `CONSTRAINT` selbst oft optional ist, wenn man die Standardnamen des DBMS akzeptiert).&lt;br /&gt;
&lt;br /&gt;
=== Primärschlüssel (PRIMARY KEY) ===&lt;br /&gt;
Es gibt drei Varianten, um die Entitätsintegrität (Eindeutigkeit) mit Hilfe eines Primärschlüssels zu definieren:&lt;br /&gt;
&lt;br /&gt;
1. &#039;&#039;&#039;Am Ende der Attributliste:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    PRIMARY KEY (idmitarbeiter)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. &#039;&#039;&#039;Direkt in der Spaltendefinition:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT PRIMARY KEY, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3. &#039;&#039;&#039;Mit explizitem CONSTRAINT-Namen:&#039;&#039;&#039; (Im Beispiel &amp;quot;pk&amp;quot; für primary key)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    fksalon INT, &lt;br /&gt;
    CONSTRAINT pk PRIMARY KEY (idmitarbeiter)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fremdschlüssel (FOREIGN KEY) ===&lt;br /&gt;
Mit einem Fremdschlüssel wird eine Spalte der einen Tabelle mit einer gleichartigen Spalte einer anderen Tabelle (meist dem Primärschlüssel) verknüpft, um referenzielle Integrität sicherzustellen. Beide Spalten müssen den exakt gleichen Datentyp besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Die referenzierte Tabelle (hier `salon`) muss bereits existieren, bevor der Fremdschlüssel in der referenzierenden Tabelle (`mitarbeiter`) angelegt werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    fksalon INT, &lt;br /&gt;
    CONSTRAINT pk PRIMARY KEY (idmitarbeiter), &lt;br /&gt;
    CONSTRAINT fk FOREIGN KEY (fksalon) REFERENCES salon(idsalon)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== FOREIGN KEY Optionen (Referenzielle Aktionen) ====&lt;br /&gt;
Diese Optionen bestimmen das Verhalten der Detailtabelle, wenn in der Primärtabelle Datensätze geändert (`ON UPDATE`) oder gelöscht (`ON DELETE`) werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;NO ACTION / RESTRICT&#039;&#039;&#039;: Die Änderung oder Löschung in der Primärtabelle wird verweigert und mit einem Fehler abgebrochen, falls abhängige Datensätze existieren.&lt;br /&gt;
* &#039;&#039;&#039;CASCADE&#039;&#039;&#039;: Die Änderung (z. B. eine neue ID) oder die Löschung wird &amp;quot;kaskadierend&amp;quot; an die Detailtabelle weitergegeben. Löscht man den Salon, werden alle Mitarbeiter dieses Salons ebenfalls gelöscht.&lt;br /&gt;
* &#039;&#039;&#039;SET NULL&#039;&#039;&#039;: Wird der Primärschlüssel gelöscht oder geändert, wird der Fremdschlüssel in der Detailtabelle auf `NULL` gesetzt (nur möglich, wenn die Spalte nicht als `NOT NULL` definiert ist).&lt;br /&gt;
&lt;br /&gt;
=== NOT NULL ===&lt;br /&gt;
Die Schlüsselwörter `NULL` bzw. `NOT NULL` legen fest, ob leere Werte in der Spalte zulässig sind. Der Standardwert ist &amp;quot;zulässig&amp;quot; (`NULL`). Primärschlüssel sollten grundsätzlich als `NOT NULL` deklariert werden.&lt;br /&gt;
&lt;br /&gt;
=== AUTO_INCREMENT / AUTOINCREMENT ===&lt;br /&gt;
Dieses Schlüsselwort legt fest, dass die Werte in der Spalte automatisch vom DBMS hochgezählt werden (z. B. für IDs).&lt;br /&gt;
* &#039;&#039;&#039;In MySQL:&#039;&#039;&#039; `idSalon INT AUTO_INCREMENT`&lt;br /&gt;
* &#039;&#039;&#039;In MS Access:&#039;&#039;&#039; `idSalon AUTOINCREMENT` (wird hier wie ein eigener Datentyp behandelt).&lt;br /&gt;
&lt;br /&gt;
=== DEFAULT ===&lt;br /&gt;
Legt einen Standardwert fest, der eingesetzt wird, wenn bei einem neuen Datensatz kein Wert für diese Spalte übergeben wird. (Achtung: Wird in MS Access in reinem DDL oft nicht nativ unterstützt).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
fksalon INT NOT NULL DEFAULT 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== UNIQUE ===&lt;br /&gt;
Sorgt dafür, dass innerhalb einer Spalte (oder Spaltenkombination) kein Wert doppelt auftreten darf.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Komplexes Beispiel (Zusammenführung aller Constraints):&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE mitarbeiter (&lt;br /&gt;
    idmitarbeiter INT NOT NULL AUTO_INCREMENT, &lt;br /&gt;
    nachname VARCHAR(45),&lt;br /&gt;
    vorname VARCHAR(45),&lt;br /&gt;
    fksalon INT NOT NULL DEFAULT 1, &lt;br /&gt;
    CONSTRAINT pk_mitarbeiter PRIMARY KEY (idmitarbeiter), &lt;br /&gt;
    CONSTRAINT fk_salon FOREIGN KEY (fksalon) REFERENCES salon(idsalon), &lt;br /&gt;
    CONSTRAINT uq_nachname UNIQUE (nachname)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== ALTER TABLE ==&lt;br /&gt;
`ALTER TABLE` ermöglicht das nachträgliche Ändern der Struktur einer vorhandenen Tabelle. &lt;br /&gt;
&lt;br /&gt;
Mögliche Änderungsoperatoren sind:&lt;br /&gt;
* &#039;&#039;&#039;ADD&#039;&#039;&#039;: Spalten/Constraints hinzufügen.&lt;br /&gt;
* &#039;&#039;&#039;DROP&#039;&#039;&#039;: Spalten/Constraints entfernen.&lt;br /&gt;
* &#039;&#039;&#039;MODIFY / ALTER COLUMN&#039;&#039;&#039;: Datentypen ändern.&lt;br /&gt;
* &#039;&#039;&#039;RENAME&#039;&#039;&#039;: Umbenennen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039;&lt;br /&gt;
Tabelle umbenennen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE Salon RENAME TO Filiale;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neue Spalte hinzufügen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE Mitarbeiter ADD (istWeiblich BOOLEAN);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== DROP TABLE ==&lt;br /&gt;
Löscht eine vollständige Tabelle inklusive aller darin enthaltenen Daten unwiderruflich aus der Datenbank.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
DROP TABLE &amp;lt;Tabellen_Name&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== CREATE VIEW ==&lt;br /&gt;
Abfragen (DQL) können als sogenannte &#039;&#039;&#039;Views&#039;&#039;&#039; (Sichten/virtuelle Tabellen) in der Datenbank gespeichert werden. Sie verhalten sich bei Abfragen wie echte Tabellen, speichern aber keine eigenen Daten, sondern führen die hinterlegte `SELECT`-Abfrage bei jedem Aufruf dynamisch aus.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Syntax:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW &amp;lt;View-Name&amp;gt; AS &amp;lt;Select-Ausdruck&amp;gt;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW weiblicheMitarbeiter AS &lt;br /&gt;
SELECT vorname, nachname &lt;br /&gt;
FROM mitarbeiter &lt;br /&gt;
WHERE istWeiblich = TRUE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Datenbanken]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik LK]]&lt;br /&gt;
[[Kategorie:FI_I_SDM]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Funktionale_Abh%C3%A4ngigkeit&amp;diff=2804</id>
		<title>Funktionale Abhängigkeit</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Funktionale_Abh%C3%A4ngigkeit&amp;diff=2804"/>
		<updated>2026-04-17T08:56:16Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
&#039;&#039;&#039;Funktionale Abhängigkeiten&#039;&#039;&#039; bilden das theoretische Fundament für die [[Normalisierung]] von Relationenschemata und die Bestimmung von [[Primärschlüssel]]n. [[Relation (Datenbanken)|Relationen]] bestehen aus einer Menge von [[Attribut|Attributen]]. Bestimmt der Wert eines bestimmten Attributs (oder einer Attributkombination) eindeutig den Wert eines anderen Attributs, liegt zwischen ihnen eine funktionale Abhängigkeit vor.&lt;br /&gt;
&lt;br /&gt;
== Funktionale Abhängigkeit ==&lt;br /&gt;
Anhand der folgenden Relation lässt sich erkennen, dass die Attribute &#039;&#039;Nachname&#039;&#039; und &#039;&#039;Vorname&#039;&#039; stets eindeutig mit der &#039;&#039;IdMitarbeiter&#039;&#039; verknüpft sind. In der Fachsprache sagt man: Die Attribute Nachname und Vorname sind funktional abhängig von IdMitarbeiter.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! IdMitarbeiter !! Nachname !! Vorname&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Krause || Sabine&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Schrotter || Claudia&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Hermann || Markus&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Krause || Christoph&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Bitter || Manuel&lt;br /&gt;
|-&lt;br /&gt;
| 6 || Schulze || Claudia&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn die `IdMitarbeiter` bekannt ist, lässt sich der Name der Person zweifelsfrei bestimmen. Die Umkehrung gilt jedoch nicht: Ist lediglich der Nachname bekannt (z. B. Krause), lässt sich die `IdMitarbeiter` nicht eindeutig ermitteln, da es mehrere Mitarbeiter mit diesem Nachnamen geben kann.&lt;br /&gt;
&lt;br /&gt;
Der Begriff leitet sich aus der Mathematik ab: Eine mathematische Funktion ordnet jedem Eingabewert exakt einen definierten Ausgabewert zu. Die Funktion &amp;lt;math&amp;gt;y = 3 \cdot x&amp;lt;/math&amp;gt; liefert für &amp;lt;math&amp;gt;x = 4&amp;lt;/math&amp;gt; immer das Ergebnis &amp;lt;math&amp;gt;12&amp;lt;/math&amp;gt;. Der Wert &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; ist somit abhängig vom Wert &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; (bzw. &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; bestimmt &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt;). &lt;br /&gt;
&lt;br /&gt;
In der Relationenalgebra spricht man hierbei von einer &#039;&#039;&#039;Determinante&#039;&#039;&#039; (Bestimmer) und notiert dies mit einem Pfeil: &lt;br /&gt;
&amp;lt;math&amp;gt;X \rightarrow Y&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für die beschriebene Datenbank-Abhängigkeit schreibt man formal: &lt;br /&gt;
&amp;lt;math&amp;gt;\text{IdMitarbeiter} \rightarrow \text{Nachname}, \text{Vorname}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Eine funktionale Abhängigkeit ist nur dann gültig, wenn sie für &#039;&#039;alle&#039;&#039; möglichen, fachlich zulässigen Tupel (Datensätze) der Relation gilt, nicht nur für den aktuell gespeicherten Zustand.&lt;br /&gt;
&lt;br /&gt;
== Volle funktionale Abhängigkeit ==&lt;br /&gt;
Von einer &#039;&#039;&#039;vollen funktionalen Abhängigkeit&#039;&#039;&#039; spricht man, wenn ein Nichtschlüssel-Attribut zwingend vom &#039;&#039;gesamten&#039;&#039; Primärschlüssel abhängig ist und nicht schon durch eine Teilmenge des Schlüssels bestimmt werden kann. Dies ist insbesondere bei zusammengesetzten Schlüsseln relevant.&lt;br /&gt;
&lt;br /&gt;
Angenommen, der Primärschlüssel der obigen Relation bestünde aus der Kombination von `IdMitarbeiter` und `Nachname`. Es gälte formal:&lt;br /&gt;
&amp;lt;math&amp;gt;(\text{IdMitarbeiter}, \text{Nachname}) \rightarrow \text{Vorname}&amp;lt;/math&amp;gt; &lt;br /&gt;
(Der Vorname ist funktional abhängig von der Kombination aus ID und Nachname).&lt;br /&gt;
&lt;br /&gt;
Prüft man dies genauer, stellt man fest, dass das Attribut `Nachname` für die Bestimmung des Vornamens überflüssig ist. Das Attribut `IdMitarbeiter` reicht bereits völlig aus, um den Vornamen eindeutig zu ermitteln. &lt;br /&gt;
&lt;br /&gt;
Da das Nichtschlüssel-Attribut `Vorname` bereits durch einen Teil des Primärschlüssels (`IdMitarbeiter`) bestimmbar ist, ist `Vorname` &#039;&#039;&#039;nicht voll funktional abhängig&#039;&#039;&#039; vom zusammengesetzten Schlüssel. Eine volle funktionale Abhängigkeit läge nur dann vor, wenn zur Bestimmung zwingend &#039;&#039;beide&#039;&#039; Attribute des Schlüssels benötigt würden.&lt;br /&gt;
&lt;br /&gt;
== Transitive Abhängigkeit ==&lt;br /&gt;
Eine &#039;&#039;&#039;transitive Abhängigkeit&#039;&#039;&#039; liegt vor, wenn eine Abhängigkeit &amp;quot;über eine Zwischenstation&amp;quot; besteht. &lt;br /&gt;
&lt;br /&gt;
Gegeben sei eine Relation &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; mit den Attributen &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;C&amp;lt;/math&amp;gt;. &lt;br /&gt;
* &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; ist der Primärschlüssel. &lt;br /&gt;
* &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; bestimmt &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;A \rightarrow B&amp;lt;/math&amp;gt;). &lt;br /&gt;
* &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; ist kein Bestandteil des Primärschlüssels. &lt;br /&gt;
* Dennoch bestimmt &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; eindeutig &amp;lt;math&amp;gt;C&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;B \rightarrow C&amp;lt;/math&amp;gt;). &lt;br /&gt;
&lt;br /&gt;
In diesem Fall ist &amp;lt;math&amp;gt;C&amp;lt;/math&amp;gt; zwar funktional von &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; abhängig, aber eben nur &#039;&#039;&#039;transitiv&#039;&#039;&#039; (indirekt) über das Attribut &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel aus der Praxis:&#039;&#039;&#039;&lt;br /&gt;
Mit Hilfe des Primärschlüssels `IdMitarbeiter` lässt sich eindeutig die ID des Salons (`IdSalon`) bestimmen, in dem der Mitarbeiter arbeitet. Der Klarname des Salons (`Salonname`) hängt jedoch funktional direkt von der `IdSalon` ab. Somit ist der `Salonname` nur transitiv von der `IdMitarbeiter` abhängig.&lt;br /&gt;
&lt;br /&gt;
Um solche Schemata zu optimieren und in die [[Normalisierung#Die_3._Normalform_(3NF)|3. Normalform (3NF)]] zu überführen, müssen Attribute mit transitiven Abhängigkeiten (wie die Saloninformationen) in eigene Relationen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Datenbanken]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik LK]]&lt;br /&gt;
[[Kategorie:FI_I_SDM]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Normalisierung&amp;diff=2803</id>
		<title>Normalisierung</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Normalisierung&amp;diff=2803"/>
		<updated>2026-04-17T08:55:51Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
Die &#039;&#039;&#039;Normalisierung&#039;&#039;&#039; eines relationalen Datenbankmodells bezeichnet den strukturierten Prozess, [[Attribut|Attribute]] anhand fester Regeln (Normalformen) auf mehrere [[Relation (Datenbanken)|Relationen]] (Tabellen) aufzuteilen. Das primäre Ziel dieses Vorgangs ist es, ein Datenbankschema zu erstellen, das frei von vermeidbaren &#039;&#039;&#039;Redundanzen&#039;&#039;&#039; (Datenverdopplungen) ist.&lt;br /&gt;
&lt;br /&gt;
Eine redundanzarme Datenspeicherung ist essenziell für die Datenintegrität. Enthält ein Schema Redundanzen, führt dies bei Aktualisierungen häufig zu [[Anomalie|Anomalien]] (Einfüge-, Änderungs- oder Löschanomalien). Mehrfach gespeicherte Daten könnten beispielsweise nur teilweise geändert werden, was zu widersprüchlichen und inkonsistenten Datenbeständen führt. Zudem belegt redundante Speicherung unnötigen Speicherplatz.&lt;br /&gt;
&lt;br /&gt;
Es gibt verschiedene formale Grade der Normalisierung (Erste, Zweite, Dritte Normalform etc.). Ein relationales Schema wird normalisiert, indem es anhand seiner [[Funktionale Abhängigkeit|funktionalen Abhängigkeiten]] schrittweise in kleinere Relationen zerlegt (dekomponiert) wird. Diese Zerlegung muss zwingend verlustfrei erfolgen – es dürfen keine Informationen verloren gehen.&lt;br /&gt;
&lt;br /&gt;
In der Praxis wird vor allem in der Entwurfsphase einer [[Relationale Datenbank|relationalen Datenbank]] normalisiert. Ein sauberer logischer Modellentwurf, beispielsweise durch ein [[Entity-Relationship-Modell]] (ERM), erfüllt oft bereits von vornherein die wichtigsten Normalformen. Dennoch dient die formale Normalisierung als wichtiges Prüfinstrument, um verbliebene Redundanzen aufzudecken.&lt;br /&gt;
&lt;br /&gt;
== Beispiel der Normalisierungsschritte ==&lt;br /&gt;
Folgendes Schema wird exemplarisch aus einem denormalisierten Ausgangszustand schrittweise in die 3. Normalform überführt.&lt;br /&gt;
&lt;br /&gt;
=== Ausgangssituation (Nicht normalisiert) ===&lt;br /&gt;
In diesem Zustand weist die Tabelle zwei massive Strukturfehler auf: Es existieren Attribute mit mehreren Werten (Wiederholungsgruppen beim &#039;&#039;Termindatum&#039;&#039;) und nicht atomare Werte (Vor- und Nachname stehen gemeinsam in einem Feld).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Kundenname !! Mitarbeitername !! Salon !! Termindatum&lt;br /&gt;
|-&lt;br /&gt;
| Klaus Meyer || Sabine Krause || Hammer Haare || 11.10.2012, 14.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| Paula Meyer || Claudia Schrotter || Friedrich List Frisuren || 09.11.2012&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Die 1. Normalform (1NF) ==&lt;br /&gt;
Ein Relationenschema befindet sich in der 1. Normalform (kurz &#039;&#039;&#039;1NF&#039;&#039;&#039;), wenn die Wertebereiche aller Attribute &#039;&#039;&#039;atomar&#039;&#039;&#039; sind. Das bedeutet, dass jeder Attributwert eine unteilbare Informationseinheit darstellen muss (z. B. muss eine Adresse in Straße, Hausnummer, PLZ und Ort aufgeteilt werden). Zudem sind Wiederholungsgruppen (Listen oder Arrays in einem Feld) unzulässig.&lt;br /&gt;
&lt;br /&gt;
Um ein Schema in die 1NF zu überführen, müssen zusammengesetzte Attribute aufgespalten und Wiederholungsgruppen durch das Anlegen neuer Datensätze (Zeilen) aufgelöst werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Umsetzung:&#039;&#039;&#039; Die Spalten &#039;&#039;Mitarbeitername&#039;&#039; und &#039;&#039;Kundenname&#039;&#039; werden jeweils in Vor- und Nachname (KVorname, KNachname, MVorname, MNachname) zerlegt. Für die beiden Termindaten von Klaus Meyer wird die Zeile dupliziert, sodass pro Zelle nur noch ein Datum steht.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! KVorname !! KNachname !! MVorname !! MNachname !! Salon !! Termindatum&lt;br /&gt;
|-&lt;br /&gt;
| Klaus || Meyer || Sabine || Krause || Hammer Haare || 11.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| Klaus || Meyer || Sabine || Krause || Hammer Haare || 14.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| Paula || Meyer || Claudia || Schrotter || Friedrich List Frisuren || 09.11.2012&lt;br /&gt;
|}&lt;br /&gt;
&#039;&#039;Die Relation erfüllt nun die Kriterien der 1. Normalform.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Die 2. Normalform (2NF) ==&lt;br /&gt;
Ein Relationenschema befindet sich in der 2. Normalform (kurz &#039;&#039;&#039;2NF&#039;&#039;&#039;), wenn es sich in der 1NF befindet und jedes Nicht-Schlüsselattribut von jedem Schlüsselkandidaten [[Funktionale Abhängigkeit#Volle funktionale Abhängigkeit|voll funktional abhängig]] ist.&lt;br /&gt;
&lt;br /&gt;
Um diese Bedingung zu prüfen, muss zunächst ein [[Primärschlüssel]] identifiziert werden. Im obigen Beispiel aus der 1NF gibt es keine natürliche Attributkombination, die einen Termin eindeutig identifiziert (ein Kunde könnte am selben Tag beim selben Mitarbeiter mehrere Termine haben).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Umsetzung:&#039;&#039;&#039; Die praktikabelste Lösung ist die Einführung eines künstlichen Primärschlüssels (Surrogatschlüssel). Die Tabelle wird um die Spalte `IdTermin` erweitert, die fortlaufend durchnummeriert wird.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! IdTermin !! KVorname !! KNachname !! MVorname !! MNachname !! Salon !! Termindatum&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Klaus || Meyer || Sabine || Krause || Hammer Haare || 11.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Klaus || Meyer || Sabine || Krause || Hammer Haare || 14.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Paula || Meyer || Claudia || Schrotter || Friedrich List Frisuren || 09.11.2012&lt;br /&gt;
|}&lt;br /&gt;
&#039;&#039;Die Relation befindet sich nun in der 2NF: Jedes Attribut in der Tabelle wird eindeutig und voll funktional durch den Primärschlüssel IdTermin bestimmt.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Die 3. Normalform (3NF) ==&lt;br /&gt;
Ein Relationenschema befindet sich in der 3. Normalform (kurz &#039;&#039;&#039;3NF&#039;&#039;&#039;), wenn es in der 2NF vorliegt und &#039;&#039;&#039;kein Nicht-Schlüsselattribut transitiv vom Primärschlüssel abhängt&#039;&#039;&#039;. Ein Nicht-Schlüsselattribut darf also nicht durch ein anderes Nicht-Schlüsselattribut bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir die Relation aus der 2NF: Der Primärschlüssel `IdTermin` bestimmt eindeutig den Mitarbeiter und den Salon. Allerdings hängt der &#039;&#039;Salonname&#039;&#039; fachlich von der Identität des Salons ab und somit nur indirekt (transitiv) vom Termin. Gleiches gilt für die Kunden (Kunden-ID bestimmt KVorname und KNachname) und Mitarbeiter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Umsetzung:&#039;&#039;&#039; Um transitive Abhängigkeiten aufzulösen, werden die zusammengehörigen Kunden-, Mitarbeiter- und Saloninformationen in eigene Relationen ausgelagert und über [[Fremdschlüssel]] verknüpft.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tabelle: Salon&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! SNR !! Salonname&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Hammer Haare&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Friedrich List Frisuren&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tabelle: Mitarbeiter&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! MNR !! MVorname !! MNachname !! FK_SNR&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Sabine || Krause || 1&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Claudia || Schrotter || 2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tabelle: Kunde&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! KNR !! KVorname !! KNachname&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Klaus || Meyer&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Paula || Meyer&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tabelle: Termin&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! IdTermin !! FK_KNR !! FK_MNR !! Termindatum&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 1 || 11.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || 1 || 14.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 2 || 2 || 09.11.2012&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Das Schema befindet sich nun vollständig in der 3NF. Es existieren ausschließlich direkte, voll funktionale Abhängigkeiten vom jeweiligen Primärschlüssel der Tabelle. Die Verknüpfung der Daten bleibt durch die Fremdschlüssel erhalten, während das Ziel der redundanzfreien Datenhaltung erreicht wurde.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Datenbanken]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik LK]]&lt;br /&gt;
[[Kategorie:FI_I_SDM]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Normalisierung&amp;diff=2802</id>
		<title>Normalisierung</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Normalisierung&amp;diff=2802"/>
		<updated>2026-04-16T12:30:03Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
Unter &#039;&#039;&#039;Normalisierung&#039;&#039;&#039; eines relationalen Datenbankmodells versteht man die Aufteilung von Attributen in mehrere [[Relation (Datenbanken)|Relationen]] (Tabellen) mithilfe der Normalisierungsregeln und deren Normalformen, sodass ein Schema entsteht, das keine vermeidbaren &#039;&#039;&#039;Redundanzen&#039;&#039;&#039; mehr enthält.&lt;br /&gt;
&lt;br /&gt;
Das Ziel ist die redundanzfreie Datenspeicherung. Ein Schema, das Redundanzen enthält, kann dazu führen, dass bei Änderungen die mehrfach enthaltenen Daten nicht konsistent, sondern nur teilweise und unvollständig geändert werden. Hierdurch können die Datenänderungen überflüssig oder widersprüchlich werden. Außerdem können [[Anomalie|Anomalien]] auftreten. Zudem belegt das mehrfache Speichern derselben Daten unnötig Speicherplatz.&lt;br /&gt;
&lt;br /&gt;
Es gibt verschiedene Grade der Normalisierung: Die so genannte erste, zweite, dritte usw. Normalform. Diese Normalformen sind durch formale Anforderungen definiert. &lt;br /&gt;
&lt;br /&gt;
Man bringt ein relationales Schema in eine Normalform, indem man für sie geltende [[funktionale Abhängigkeit]]en in einfachere Relationen zerlegt, bis keine weitere Zerlegung mehr möglich ist. Dabei dürfen jedoch keine Daten verloren gehen. &lt;br /&gt;
&lt;br /&gt;
Normalisiert wird vor allem in der Phase des Entwurfs einer [[Relationale Datenbank|relationalen Datenbank]]. Ein exakter Modellentwurf, wie das [[Entity-Relationship-Modell]] (ERM), macht eine Normalisierung eigentlich überflüssig, kann aber eingesetzt werden, um Anomalien zu beseitigen oder Redundanzen nachträglich zu minimieren.&lt;br /&gt;
&lt;br /&gt;
== Beispiel der Normalisierungsschritte ==&lt;br /&gt;
Folgendes Schema wird exemplarisch aus einem denormalisierten Zustand in die 3. Normalform überführt.&lt;br /&gt;
&lt;br /&gt;
=== Ausgangssituation (Nicht normalisiert) ===&lt;br /&gt;
In diesem Zustand gibt es Attribute mit mehreren Werten (Wiederholungsgruppen bei Termindatum) und nicht atomare Werte (Vor- und Nachname in einem Feld).&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Kundenname !! Mitarbeitername !! Salon !! Termindatum&lt;br /&gt;
|-&lt;br /&gt;
| Klaus Meyer || Sabine Krause || Hammer Haare || 11.10.2012, 14.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| Paula Meyer || Claudia Schrotter || Friedrich List Frisuren || 09.11.2012&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Die 1. Normalform (1NF) ==&lt;br /&gt;
Die 1. Normalform (kurz &#039;&#039;&#039;1NF&#039;&#039;&#039;) ist erfüllt, wenn jedes Attribut einer Relation einen &#039;&#039;&#039;atomaren Wertebereich&#039;&#039;&#039; aufweist. Eine Relation befindet sich in der ersten Normalform, wenn alle Attribute nur einfache Attributwerte aufweisen. (Beispiel: Die Adresse darf nicht als ein Attribut verwendet werden, sondern muss in PLZ, Ort, Straße und Hausnummer aufgeteilt werden). Zudem darf es keine Wiederholungsgruppen geben. &lt;br /&gt;
&lt;br /&gt;
Zur Bildung der ersten Normalform müssen die nicht atomaren Attribute umgewandelt werden. Dies kann durch Einfügen zusätzlicher Zeilen, Spalten oder neuer Relationen erfolgen. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Umsetzung:&#039;&#039;&#039; Um die 1NF zu erfüllen, müssen für Werte der Spalte &#039;&#039;Termindatum&#039;&#039; zusätzliche Zeilen eingefügt werden und die Inhalte der Spalten &#039;&#039;Mitarbeitername&#039;&#039; und &#039;&#039;Kundennamen&#039;&#039; müssen in jeweils zwei Spalten (MVorname und MNachname bzw. KVorname und KNachname) ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! KVorname !! KNachname !! MVorname !! MNachname !! Salon !! Termindatum&lt;br /&gt;
|-&lt;br /&gt;
| Klaus || Meyer || Sabine || Krause || Hammer Haare || 11.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| Klaus || Meyer || Sabine || Krause || Hammer Haare || 14.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| Paula || Meyer || Claudia || Schrotter || Friedrich List Frisuren || 09.11.2012&lt;br /&gt;
|}&lt;br /&gt;
&#039;&#039;Nun befindet sich die Tabelle in der 1NF!&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Die 2. Normalform (2NF) ==&lt;br /&gt;
Ein Relationenschema ist in der 2. Normalform (kurz &#039;&#039;&#039;2NF&#039;&#039;&#039;), wenn es in der 1. Normalform ist und jedes Nicht-Schlüsselattribut von einem [[Primärschlüssel]] [[Funktionale_Abhängigkeit#Volle_funktionale_Abhängigkeit|vollständig funktional abhängig]] ist.&lt;br /&gt;
&lt;br /&gt;
Zunächst muss also ein Primärschlüssel definiert werden. In diesem Beispiel lässt sich aber keine Attributkombination finden, für die sich immer jeder Datensatz identifizieren lässt. Denn es ist denkbar, dass ein Kunde beim gleichen Mitarbeiter am gleichen Datum zwei Friseurtermine wahrnimmt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Umsetzung:&#039;&#039;&#039; Wird ein künstlicher Primärschlüssel für die Relation definiert, ist die Anforderung automatisch erfüllt. Man erweitert die Tabelle um eine Spalte `IdTermin`. Der künstliche Primärschlüssel generiert einen Autowert, anhand dessen sich jede Spalte eindeutig identifizieren lässt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! IdTermin !! KVorname !! KNachname !! MVorname !! MNachname !! Salon !! Termindatum&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Klaus || Meyer || Sabine || Krause || Hammer Haare || 11.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Klaus || Meyer || Sabine || Krause || Hammer Haare || 14.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Paula || Meyer || Claudia || Schrotter || Friedrich List Frisuren || 09.11.2012&lt;br /&gt;
|}&lt;br /&gt;
&#039;&#039;Die Relation erfüllt nun die Anforderungen der 2NF. Sie befindet sich in der 1NF und es gibt kein Attribut in der Tabelle, das sich nicht eindeutig und voll funktional durch den Primärschlüssel IdTermin bestimmen lässt.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Die 3. Normalform (3NF) ==&lt;br /&gt;
Ein Relationenschema befindet sich in der 3. Normalform (kurz &#039;&#039;&#039;3NF&#039;&#039;&#039;), wenn es in der 2NF ist und kein Attribut, das nicht zum Primärschlüssel gehört, von diesem &#039;&#039;&#039;transitiv abhängt&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Betrachten wir die Relation aus der 2NF: Mit Hilfe des Primärschlüssels `IdTermin` lässt sich eindeutig der Vorname eines Mitarbeiters, eines Kunden oder eines Salons bestimmen. Allerdings hängt der Salonname von der Salonnummer (SNR) ab und nur &#039;&#039;transitiv&#039;&#039; von `IdTermin`. Gleiches gilt für Kunden (KNR -&amp;gt; KVorname, KNachname) und Mitarbeiter (MNR -&amp;gt; MVorname, MNachname).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Umsetzung:&#039;&#039;&#039; Um das Schema in die 3NF zu überführen, müssen Kunden-, Mitarbeiter- und Saloninformationen in eine eigene Relation ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tabelle: Salon&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! SNR !! Salonname&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Hammer Haare&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Friedrich List Frisuren&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tabelle: Mitarbeiter&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! MNR !! MVorname !! MNachname !! FK_SNR&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Sabine || Krause || 1&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Claudia || Schrotter || 2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tabelle: Kunde&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! KNR !! KVorname !! KNachname&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Klaus || Meyer&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Paula || Meyer&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tabelle: Termin&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! IdTermin !! FK_KNR !! FK_MNR !! Termindatum&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 1 || 11.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || 1 || 14.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 2 || 2 || 09.11.2012&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Das Schema befindet sich nun in der 3NF. Es herrschen nur direkte vollfunktionale Abhängigkeiten. Durch [[Fremdschlüssel|Fremdschlüsselbeziehungen]] bleibt der Bezug der Daten untereinander erhalten. Das Ziel der redundanzfreien Datenhaltung ist erreicht.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Datenbanken]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik LK]]&lt;br /&gt;
[[Kategorie:FI_I_SDM]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Normalisierung&amp;diff=2801</id>
		<title>Normalisierung</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Normalisierung&amp;diff=2801"/>
		<updated>2026-04-16T12:28:12Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
Unter &#039;&#039;&#039;Normalisierung&#039;&#039;&#039; eines relationalen Datenbankmodells versteht man die Aufteilung von Attributen in mehrere [[Relation (Datenbanken)|Relationen]] (Tabellen) mithilfe der Normalisierungsregeln und deren Normalformen, sodass ein Schema entsteht, das keine vermeidbaren &#039;&#039;&#039;Redundanzen&#039;&#039;&#039; mehr enthält.&lt;br /&gt;
&lt;br /&gt;
Das Ziel ist die redundanzfreie Datenspeicherung. Ein Schema, das Redundanzen enthält, kann dazu führen, dass bei Änderungen die mehrfach enthaltenen Daten nicht konsistent, sondern nur teilweise und unvollständig geändert werden. Hierdurch können die Datenänderungen überflüssig oder widersprüchlich werden. Außerdem können [[Anomalie|Anomalien]] auftreten. Zudem belegt das mehrfache Speichern derselben Daten unnötig Speicherplatz.&lt;br /&gt;
&lt;br /&gt;
Es gibt verschiedene Grade der Normalisierung: Die so genannte erste, zweite, dritte usw. Normalform. Diese Normalformen sind durch formale Anforderungen definiert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Man bringt ein relationales Schema in eine Normalform, indem man für sie geltende funktionale Abhängigkeiten in einfachere Relationen zerlegt, bis keine weitere Zerlegung mehr möglich ist. Dabei dürfen jedoch keine Daten verloren gehen. &lt;br /&gt;
&lt;br /&gt;
Normalisiert wird vor allem in der Phase des Entwurfs einer [[Relationale Datenbank|relationalen Datenbank]]. Ein exakter Modellentwurf, wie das [[Entity-Relationship-Modell]] (ERM), macht eine Normalisierung eigentlich überflüssig, kann aber eingesetzt werden, um Anomalien zu beseitigen oder Redundanzen nachträglich zu minimieren.&lt;br /&gt;
&lt;br /&gt;
== Beispiel der Normalisierungsschritte ==&lt;br /&gt;
Folgendes Schema wird exemplarisch aus einem denormalisierten Zustand in die 3. Normalform überführt.&lt;br /&gt;
&lt;br /&gt;
=== Ausgangssituation (Nicht normalisiert) ===&lt;br /&gt;
In diesem Zustand gibt es Attribute mit mehreren Werten (Wiederholungsgruppen bei Termindatum) und nicht atomare Werte (Vor- und Nachname in einem Feld).&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Kundenname !! Mitarbeitername !! Salon !! Termindatum&lt;br /&gt;
|-&lt;br /&gt;
| Klaus Meyer || Sabine Krause || Hammer Haare || 11.10.2012, 14.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| Paula Meyer || Claudia Schrotter || Friedrich List Frisuren || 09.11.2012&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Die 1. Normalform (1NF) ==&lt;br /&gt;
Die 1. Normalform (kurz &#039;&#039;&#039;1NF&#039;&#039;&#039;) ist erfüllt, wenn jedes Attribut einer Relation einen &#039;&#039;&#039;atomaren Wertebereich&#039;&#039;&#039; aufweist. Eine Relation befindet sich in der ersten Normalform, wenn alle Attribute nur einfache Attributwerte aufweisen. (Beispiel: Die Adresse darf nicht als ein Attribut verwendet werden, sondern muss in PLZ, Ort, Straße und Hausnummer aufgeteilt werden). Zudem darf es keine Wiederholungsgruppen geben. &lt;br /&gt;
&lt;br /&gt;
Zur Bildung der ersten Normalform müssen die nicht atomaren Attribute umgewandelt werden. Dies kann durch Einfügen zusätzlicher Zeilen, Spalten oder neuer Relationen erfolgen. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Umsetzung:&#039;&#039;&#039; Um die 1NF zu erfüllen, müssen für Werte der Spalte &#039;&#039;Termindatum&#039;&#039; zusätzliche Zeilen eingefügt werden und die Inhalte der Spalten &#039;&#039;Mitarbeitername&#039;&#039; und &#039;&#039;Kundennamen&#039;&#039; müssen in jeweils zwei Spalten (MVorname und MNachname bzw. KVorname und KNachname) ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! KVorname !! KNachname !! MVorname !! MNachname !! Salon !! Termindatum&lt;br /&gt;
|-&lt;br /&gt;
| Klaus || Meyer || Sabine || Krause || Hammer Haare || 11.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| Klaus || Meyer || Sabine || Krause || Hammer Haare || 14.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| Paula || Meyer || Claudia || Schrotter || Friedrich List Frisuren || 09.11.2012&lt;br /&gt;
|}&lt;br /&gt;
&#039;&#039;Nun befindet sich die Tabelle in der 1NF!&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Die 2. Normalform (2NF) ==&lt;br /&gt;
Ein Relationenschema ist in der 2. Normalform (kurz &#039;&#039;&#039;2NF&#039;&#039;&#039;), wenn es in der 1. Normalform ist und jedes Nicht-Schlüsselattribut von einem [[Primärschlüssel]] [[Funktionale_Abhängigkeit|vollständig funktional abhängig]] ist.&lt;br /&gt;
&lt;br /&gt;
Zunächst muss also ein Primärschlüssel definiert werden. In diesem Beispiel lässt sich aber keine Attributkombination finden, für die sich immer jeder Datensatz identifizieren lässt. Denn es ist denkbar, dass ein Kunde beim gleichen Mitarbeiter am gleichen Datum zwei Friseurtermine wahrnimmt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Umsetzung:&#039;&#039;&#039; Wird ein künstlicher Primärschlüssel für die Relation definiert, ist die Anforderung automatisch erfüllt. Man erweitert die Tabelle um eine Spalte `IdTermin`. Der künstliche Primärschlüssel generiert einen Autowert, anhand dessen sich jede Spalte eindeutig identifizieren lässt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! IdTermin !! KVorname !! KNachname !! MVorname !! MNachname !! Salon !! Termindatum&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Klaus || Meyer || Sabine || Krause || Hammer Haare || 11.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Klaus || Meyer || Sabine || Krause || Hammer Haare || 14.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Paula || Meyer || Claudia || Schrotter || Friedrich List Frisuren || 09.11.2012&lt;br /&gt;
|}&lt;br /&gt;
&#039;&#039;Die Relation erfüllt nun die Anforderungen der 2NF. Sie befindet sich in der 1NF und es gibt kein Attribut in der Tabelle, das sich nicht eindeutig und voll funktional durch den Primärschlüssel IdTermin bestimmen lässt.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
== Die 3. Normalform (3NF) ==&lt;br /&gt;
Ein Relationenschema befindet sich in der 3. Normalform (kurz &#039;&#039;&#039;3NF&#039;&#039;&#039;), wenn es in der 2NF ist und kein Attribut, das nicht zum Primärschlüssel gehört, von diesem &#039;&#039;&#039;transitiv abhängt&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Betrachten wir die Relation aus der 2NF: Mit Hilfe des Primärschlüssels `IdTermin` lässt sich eindeutig der Vorname eines Mitarbeiters, eines Kunden oder eines Salons bestimmen. Allerdings hängt der Salonname von der Salonnummer (SNR) ab und nur &#039;&#039;transitiv&#039;&#039; von `IdTermin`. Gleiches gilt für Kunden (KNR -&amp;gt; KVorname, KNachname) und Mitarbeiter (MNR -&amp;gt; MVorname, MNachname).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Umsetzung:&#039;&#039;&#039; Um das Schema in die 3NF zu überführen, müssen Kunden-, Mitarbeiter- und Saloninformationen in eine eigene Relation ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tabelle: Salon&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! SNR !! Salonname&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Hammer Haare&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Friedrich List Frisuren&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tabelle: Mitarbeiter&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! MNR !! MVorname !! MNachname !! FK_SNR&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Sabine || Krause || 1&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Claudia || Schrotter || 2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tabelle: Kunde&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! KNR !! KVorname !! KNachname&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Klaus || Meyer&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Paula || Meyer&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tabelle: Termin&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! IdTermin !! FK_KNR !! FK_MNR !! Termindatum&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 1 || 11.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || 1 || 14.10.2012&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 2 || 2 || 09.11.2012&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Das Schema befindet sich nun in der 3NF. Es herrschen nur direkte vollfunktionale Abhängigkeiten. Durch [[Fremdschlüssel|Fremdschlüsselbeziehungen]] bleibt der Bezug der Daten untereinander erhalten. Das Ziel der redundanzfreien Datenhaltung ist erreicht.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Datenbanken]]&lt;br /&gt;
[[Kategorie:AHR_I_Informatik LK]]&lt;br /&gt;
[[Kategorie:FI_I_SDM]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
	<entry>
		<id>https://wiki.flbk-hamm.de/index.php?title=Funktionale_Abh%C3%A4ngigkeit&amp;diff=2800</id>
		<title>Funktionale Abhängigkeit</title>
		<link rel="alternate" type="text/html" href="https://wiki.flbk-hamm.de/index.php?title=Funktionale_Abh%C3%A4ngigkeit&amp;diff=2800"/>
		<updated>2026-04-16T12:27:35Z</updated>

		<summary type="html">&lt;p&gt;Flbkwikiadmin: Die Seite wurde neu angelegt: „== Einführung == &amp;#039;&amp;#039;&amp;#039;Funktionale Abhängigkeiten&amp;#039;&amp;#039;&amp;#039; bilden die Grundlage für die Normalisierung von Relationenschemata und die Bestimmung von Primärschlüsseln. Relationen werden durch Attribute definiert. Bestimmen einige dieser Attribute eindeutig die Werte anderer Attribute, so spricht man von funktionaler Abhängigkeit.  == Funktionale Abhängigkeit == Betrachtet man folgende Relation, wird man festste…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
&#039;&#039;&#039;Funktionale Abhängigkeiten&#039;&#039;&#039; bilden die Grundlage für die [[Normalisierung]] von Relationenschemata und die Bestimmung von [[Primärschlüssel]]n. [[Relation (Datenbanken)|Relationen]] werden durch [[Attribut|Attribute]] definiert. Bestimmen einige dieser Attribute eindeutig die Werte anderer Attribute, so spricht man von funktionaler Abhängigkeit.&lt;br /&gt;
&lt;br /&gt;
== Funktionale Abhängigkeit ==&lt;br /&gt;
Betrachtet man folgende Relation, wird man feststellen, dass die Attribute &#039;&#039;Nachname&#039;&#039; und &#039;&#039;Vorname&#039;&#039; abhängig sind von der &#039;&#039;IdMitarbeiter&#039;&#039;. Man sagt in diesem Zusammenhang, die Attribute Nachname und Vorname sind funktional abhängig von der IdMitarbeiter.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! IdMitarbeiter !! Nachname !! Vorname&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Krause || Sabine&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Schrotter || Claudia&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Hermann || Markus&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Krause || Christoph&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Bitter || Manuel&lt;br /&gt;
|-&lt;br /&gt;
| 6 || Schulze || Claudia&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn man also die `IdMitarbeiter` kennt, kann man auch den Namen des Mitarbeiters benennen. Andersherum ist der Schluss nicht möglich: Ist der Nachname bekannt (z. B. Krause), lässt sich die `IdMitarbeiter` nicht eindeutig bestimmen.&lt;br /&gt;
&lt;br /&gt;
In Anlehnung an die Mathematik wird hier von der funktionalen Abhängigkeit gesprochen, da eine mathematische Funktion immer den gleichen Ausgabewert bei gleichen Eingabewerten liefert. Die Funktion \({ y = 3 \cdot x }\) liefert für \({ x = 4 }\) immer die Ausgabe \(12\) für die Variable \(y\). Für jede beliebige Zahl, die man für die Variable \(x\) einsetzt, ergibt sich also ein ganz bestimmter Wert für \(y\). Man kann sagen, dass der Wert \(y\) abhängig ist vom Wert \(x\) bzw. \(x\) bestimmt \(y\). &lt;br /&gt;
&lt;br /&gt;
Mathematisch spricht man auch von einer &#039;&#039;&#039;Determinante&#039;&#039;&#039; und schreibt: &lt;br /&gt;
\({ x \rightarrow y }\)&lt;br /&gt;
&lt;br /&gt;
Analog schreibt man also für die besprochene funktionale Abhängigkeit der Attribute: &lt;br /&gt;
\({ \text{IdMitarbeiter} \rightarrow \text{Nachname}, \text{Vorname} }\)&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Die funktionale Abhängigkeit muss für alle möglichen bzw. denkbaren Tupel (Datensätze) einer Relation gelten.&lt;br /&gt;
&lt;br /&gt;
== Volle funktionale Abhängigkeit ==&lt;br /&gt;
Man spricht von &#039;&#039;&#039;voller funktionaler Abhängigkeit&#039;&#039;&#039;, wenn jedes Nichtschlüssel-Attribut nur durch den &#039;&#039;gesamten&#039;&#039; Primärschlüssel eindeutig bestimmt werden kann.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir an, der zusammengesetzte Primärschlüssel der obigen Relation würde sich aus den Attributen `IdMitarbeiter` und `Nachname` zusammensetzen. Es gälte also:&lt;br /&gt;
\({ (\text{IdMitarbeiter}, \text{Nachname}) \rightarrow \text{Vorname} }\) &lt;br /&gt;
(Vorname ist funktional von IdMitarbeiter und Nachname abhängig.)&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der Vorname nicht vom Nachnamen funktional abhängig. Der Vorname „Claudia“ gilt sowohl für Schulze als auch für Schrotter. Das Attribut `IdMitarbeiter` bestimmt aber nach wie vor alleine und eindeutig das Attribut `Vorname`. Auf das Attribut `Nachname` kann also im Schlüssel verzichtet werden. &lt;br /&gt;
&lt;br /&gt;
Da das Nichtschlüssel-Attribut `Vorname` auch durch einen Teil des Primärschlüssels – nämlich `IdMitarbeiter` – bestimmbar ist, ist `Vorname` &#039;&#039;&#039;nicht voll funktional abhängig&#039;&#039;&#039; von der Kombination aus IdMitarbeiter und Nachname. Die volle funktionale Abhängigkeit würde hier nur für das isolierte Attribut `IdMitarbeiter` gelten.&lt;br /&gt;
&lt;br /&gt;
== Transitive Abhängigkeit ==&lt;br /&gt;
Angenommen, wir haben eine Relation \(R\) mit den Attributen \(A\), \(B\) und \(C\). &lt;br /&gt;
* \(A\) ist der Primärschlüssel. &lt;br /&gt;
* \(A\) bestimmt \(B\) (\({ A \rightarrow B }\)). &lt;br /&gt;
* \(B\) ist nicht Teil des Primärschlüssels. &lt;br /&gt;
* Dennoch gilt \({ B \rightarrow C }\). &lt;br /&gt;
&lt;br /&gt;
Somit ist \(C\) &#039;&#039;&#039;transitiv&#039;&#039;&#039; von \(A\) abhängig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel aus der Praxis:&#039;&#039;&#039;&lt;br /&gt;
Mit Hilfe des Primärschlüssels `IdMitarbeiter` lässt sich eindeutig der Salon bestimmen, in dem der Mitarbeiter arbeitet. Allerdings hängt der Salonname funktional von der `IdSalon` ab und somit nur &#039;&#039;transitiv&#039;&#039; von der `IdMitarbeiter`.&lt;br /&gt;
&lt;br /&gt;
Um ein solches Schema in die [[Normalisierung#Die_3._Normalform_(3NF)|3. Normalform (3NF)]] zu überführen, müssen die Saloninformationen in eine eigene Relation ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Datenbanken]]&lt;br /&gt;
[[Kategorie:FI_I_SDM]]&lt;/div&gt;</summary>
		<author><name>Flbkwikiadmin</name></author>
	</entry>
</feed>