Komplexität von Algorithmen. Was ist Komplexität? Die Groß-O-Notation Komplexität von Schleifen Effizienz-Beispiel Schlussfolgerungen.
Post on 06-Apr-2015
110 Views
Preview:
Transcript
Komplexität von Algorithmen
Komplexität von Algorithmen
Was ist Komplexität?
Die Groß-O-Notation
Komplexität von Schleifen
Effizienz-Beispiel
Schlussfolgerungen
Komplexität von Algorithmen
Was ist Komplexität?
Die Groß-O-Notation
Komplexität von Schleifen
Effizienz-Beispiel
Schlussfolgerungen
Komplexität
Nicht die Komplexität eines Algorithmus im Sinne von Kompliziertheit ist gemeint, sondern das Laufzeitverhalten, die Ausführungsgeschwindigkeit
Es gibt auch Speicherplatzkomplexität, diese wird hier nicht thematisiert
Beispiel
Rechner L Rechner M
A3 Sekunden 6 Sekunden
B6 Sekunden 3 Sekunden
Angenommen wir haben 2 Programme A und B und deren Ausführungszeit auf den Rechnern L und M:
BeispielRechner L Rechner M
A 3 s 6 s
B 6 s 3 s
Mögliche Aussagen: Beide Programme sind ähnlich schnell Ausführungsgeschwindigkeit hängt vom Rechner ab
Unsinnige Aussage: Programm A ist 25% schneller als Programm B
BeispielRechner L Rechner M
A 3 s 6 s
B 6 s 3 s
Unsinnige Aussage: Programm A ist 25% schneller als Programm B Trotzdem wird diese Aussage oft gemacht. Trick:
Umrechnen der Rechenzeiten auf relative Werte
Rechner L Rechner M Durchschnitt
A 3s ≙ 1 6s ≙ 1 1
B 6s ≙ 2 3s ≙ 0,5 1,25
Rechner L Rechner M Durchschnitt
A 0,5 2 1,25
B 1 1 1
Benchmarks und andere Tricks
Quelle: AMD Homepage Quelle: ZDNet
Komplexität Komplexitätsabschätzung soll angeben, wie sich die
Laufzeit eines Algorithmus nur in Abhängigkeit von der Größe des Problems entwickelt
Wir werden deshalb eine Methode zur Ab-schätzung der Geschwindigkeit eines Algorithmus unabhängig von verwendeter Hardware benötigen
Als Problemgröße betrachten wir den nötigen Umfang der Berechnung: z.B. Anzahl der Eingabewerte / Daten
Bezeichnung: n
Komplexität von Algorithmen
Was ist Komplexität? Die Groß-O-Notation
Komplexität von Schleifen
Effizienz-Beispiel
Schlussfolgerungen
Laufzeit eines Algorithmus
Beispiel: Algorithmus, der alle Elemente eines Arrays auf 0 setzt.
Größe des Problems: Anzahl n der Array-Elemente Laufzeit f(n) auf Maschine L: (29n + 13) μs Laufzeit f(n) auf Maschine M: (17n + 52) μs
Vernachlässigen wir den konstanten Summanden und den konstanten Faktor, können wir sagen, dass die Komplexität des Algorithmus von der Ordnung n ist (Lineare Ordnung). Schreibweise: f(n) ∈ O(n).
Groß-O-Notation
∃a,n0: (∀n: n ≥ n0: f(n) ≤ a·g(n)) mit a ≥ 0
Man kann sagen „f(n) ist aus O(g(n)) [f(n) ∈ O(g(n))]“ oder „Die Komplexität des Algorithmus ist von der Größenordnung O(g(n))“, wenn gilt:
Beispiele: 3n + 2 ∈ O(n) 12n2 + 3n + 5 ∈ O(n2) 23n + n939 ∈ O(23n)
Klasse der linearen Komplexitätsfunktionen
Klasse der quadratischen Komplexitätsfunktionen
Groß-O-Notationint[] a = new int[n];int i=0;while(i!=n){
a[i]=0;i=i+1;
}
Der Aufwand dieses Algorithmus ist f(n) ∈ O(n), also linear
Groß-O-Notation abstrahiert nicht nur von der Maschine, sondern auch von der Anzahl der Befehle
erfasst wird die Anzahl der Schritte
z.B.: f(n) = 2n + 1
(nur Zuweisungen)oder
f(n) = 3n + 1 (auch Vergleiche)
Verschiedene Komplexitätsklassen
Kleine Problemgrößen: Große Problemgrößen:
O, Ω und Θ Die Angabe von O(n) beschreibt nur eine obere Schranke der
Komplexität Insgesamt gibt es:
(Groß-O, Omega und Theta)
f(n) ∈ O(g(n)): ∃a,n0: (∀n: n ≥ n0: f(n) ≤ a·g(n)) mit a ≥ 0
„f wächst höchstens so schnell wie g“
„g ist obere Schranke von f“f(n) ∈ Ω(g(n)): ∃a,n0: (∀n: n ≥ n0: f(n) ≥ a·g(n)) mit a ≥ 0
„f wächst mindestens so schnell wie g“
„g ist untere Schranke von f“
f(n) ∈ Θ(g(n)): ∃a1,a2,n0: (∀n: n ≥ n0: a1·g(n) ≥ f(n) ≥ a2·g(n)) mit ax ≥ 0
„f wächst ebenso schnell wie g“
„g ist untere und obere Schranke von f“
Beispiel: Suchalgorithmen
Lineare Suche: int i=0;while(a[i]!=x){
i=i+1;}
Gesuchtes Element kann an jeder Stelle im Array stehen
Komplexität: O(1) oder O(n)?
Antwort: O(n), worst case: O(n) obere Schranke
Beispiel: Suchalgorithmen
Jetzt: Beweis, dass Suchalgorithmen keine bessere Komplexität als O(log n) erreichen können, wobei n die Anzahl der zu durchsuchenden Elemente ist
Annahme: Suchalgorithmus verwendet 2-Wege-Vergleiche: Wir können 2 Elemente des Arrays untereinander
vergleichen, oder ein Element des Arrays mit einem speziellen Wert
Ergebnis des Vergleichs ist immer true oder false
Beispiel: Suchalgorithmen
Ablauf eines beliebigen Suchalgorithmus, der diese Annahme erfüllt: Evtl. initialisieren / Startelement ermitteln Ersten Vergleich durchführen Abhängig vom Ergebnis (true / false) etwas
berechnen (Verzweigung) Zweiten Vergleich durchführen Wieder dual verzweigen Und so weiter...
Beispiel: Suchalgorithmen
Wir könnten den Ablauf des Programms als Baum aufzeichnen:
Vergleich 1
Vergleich 3Vergleich 2
true false
…1 2 3 n+1nn-1
⋮
Beispiel: Suchalgorithmen Wir wissen:
Baum ist ein binärer Baum Insgesamt muss er min. n+1 Blätter haben, da der gesuchte
Wert an jeder Stelle des Arrays stehen, oder nicht vorhanden sein könnte
Der Satz:
Ein binärer Baum der Höhe d besitzt höchstens 2d Blätter.
führt unmittelbar zu dem Schluss, dass ein Pfad von der Wurzel zu einem Blatt des Baumes mindestens log2(n+1) Knoten besitzt.
Also hat die Komplexität jedes Suchalgorithmus die untere Schranke f(n) ∈ Ω(log(n)).
Komplexität von Algorithmen
Was ist Komplexität?
Die Groß-O-Notation Komplexität von Schleifen
Effizienz-Beispiel
Schlussfolgerungen
Schleifenint[] a=new int[n];int[] b=new int[n];int i=0;while(i!=n){
a[i]=0;i=i+1;
}i=0;while(i!=n){
b[i]=0;i=i+1;
}
int[] a=new int[n];int[] b=new int[n];int i=0;while(i!=n){
a[i]=0;b[i]=0;i=i+1;
}
f(n) = (2n+1)+(2n+1)= 4n+2⇒ f(n) ∈ O(n)
Die Hintereinanderausführung von Schleifen ändert die Komplexität eines Algorithmus nicht.
g(n) = 3n+1⇒ g(n) ∈
O(n)
Verschachtelte Schleifen
int i = 0, j = 0;
while (i != n - 1){ while (j != n - 1) { if (b[j] > b[j + 1]) {
swap(b[j],b[j+1]); } j++; } j = 0; i++;}
Bubble-Sort: Die innere und die äußere
Schleife werden jeweils (n-1)-mal durchlaufen
Die Anzahl der Schritte und damit die Komplexität ist also:
(n-1)·(n-1)=(n-1)2 ∈ O(n2)
Die Verschachtelung von Schleifen führt zur Multi-plikation der Komplexitäten der einzelnen Schleifen.
Verschachtelte Schleifenint k = 0,l = 0,m = 0,n = 0;
while (n != N){ if (a[n] == 0) {k++;}
while (k > 7) { if (a[m] == 0)
{k--;}
m++; } l = max(l, n + 1 - m); n++;}
Programm ermittelt die Länge des längsten 7er-Segments
7er-Segment: zusammenhängender Teil des Arrays, der maximal 7 Nullen enthält
nmmax. 7 Nullen
a[]
Verschachtelte Schleifen Äußere Schleife wird N-
mal durchlaufen Innere Schleife wird
jedes Mal maximal N-mal durchlaufen
⇒ f(N) ∈ O(N2)? Betrachten innere
Schleife: manche Durchläufe können ∈ O(N) sein, andere benötigen wesentlich weniger Schritte
int k = 0,l = 0,m = 0,n = 0;
while (n != N){ if (a[n] == 0) {k++;}
while (k > 7) { if (a[m] == 0)
{k--;}
m++; } l = max(l, n + 1 - m); n++;}
Verschachtelte Schleifen
Schleifeninvariante u.A.: 0 ≤ m ≤ n ≤ N
⇒ Die innere Schleife wird insgesamt maximal N-mal ausgeführt
⇒ Komplexität im schlimmsten Fall O(N) in einem Durchlauf, aber auch O(N) insgesamt
int k = 0,l = 0,m = 0,n = 0;
while (n != N){ if (a[n] == 0) {k++;}
while (k > 7) { if (a[m] == 0)
{k--;}
m++; } l = max(l, n + 1 - m); n++;}
nmmax. 7 Nullen
a[]
Verschachtelte Schleifen
Komplexität im schlimmsten Fall O(N) in einem Durchlauf, aber auch O(N) über alle N Durchläufe der äußeren Schleife
⇒ Die durchschnittliche Komplexität pro Durchlauf der äußeren Schleife ist O(1)
„Getilgte Komplexität“ (Amortized Complexity) nennt man die Bildung des Durchschnitts der Komplexität über alle möglichen Situationen.
⇒ Die Komplexität des Algorithmus ist ∈ O(N·1)=O(N)
Komplexität von Algorithmen
Was ist Komplexität?
Die Groß-O-Notation
Komplexität von Schleifen Effizienz-Beispiel
Schlussfolgerungen
Binäre Sucheint i = M, j = N, m;
while (j != i+1){ m = (i+j) div 2; if (a[m] ≤ x) {
i = m; } else if (a[m] > x) {
j = m; }}
NM
x
a[]
Aussage: Programm ist ineffizient, da es eigentlich sofort abbrechen könnte, wenn x gefunden wurde
Statt dessen läuft es bis gilt: j=i+1
Binäre Suche – „Verbesserte Version“int i = M, j = N, m;while (j != i+1){ m = (i+j) div 2; if (a[m] < x) {
i = m; } else if (a[m] = x) {
i = m; j = m + 1;
} else if (a[m] > x) { j = m; }}
Annahme: M=0 und N=2p; x befinde sich an allen Stellen des Arrays mit gleicher Wahrschein-lichkeit
1. Fall x ist nicht im Array vorhanden: beide Algorithmen brauchen gleich viele Schritte
Vergleich der 2 Algorithmen:
Binäre Suche - Vergleich 2. Fall x befindet sich an einer ungeraden Position im Array:
wegen N=2p finden beide Algorithmen x erst im letzten Schritt ⇒ in der Hälfte der Fälle spart der zweite Algorithmus nichts
3. Fall x befindet sich an einer geraden Stelle, deren Index durch 2 dividiert ungerade ist: der zweite Algorithmus bricht einen Schritt eher als der erst ab ⇒ in einem Viertel der Fälle wird ein Schritt gespart
4. Fall in einem Achtel der Fälle werden 2 Schritte gespart und so weiter…
)(log
12
)(log
121
2
1
2
1
n
i
in
iii i
1 121
21
12
12
12 2
1
211121
i i
i
i
i
i
i
i
iiiiii
ss
1 s
Binäre Suche - Vergleich Zweites if-Statement könnte
bei Ausführung das 1,5-fache der Zeit des ersten benötigen
if (a[m] ≤ x){i = m;}
else{j = m;}
if (a[m] < x){i = m;}
else if (a[m] = x){i=m;j=m+1;}
else{j = m;}
8
3)(log
2)(log3)(log3
1)(log)1)((log
2
22
223
2
n
n
nn
nn
Wann lohnt sich der zweite Algorithmus?
Komplexität von Algorithmen
Was ist Komplexität?
Die Groß-O-Notation
Komplexität von Schleifen
Effizienz-Beispiel Schlussfolgerungen
Schlussfolgerungen Man sollte besser die Komplexität eines Algorithmus
verbessern als ihn im Detail „feinzutunen“ Wichtigkeit eines effizienten Algorithmus nimmt mit
schnelleren Computern nicht ab, sondern zu:
Programm A Programm B
3·109·n μs 2n μs
Programm A Programm B
n=370 n=40
Beide Programme können in rund einem Tag ein Problem der Größenordnung n=37 lösen
Wird der Rechner 10-mal schneller, schaffen sie an einem Tag:
Komplexität von Algorithmen
Was ist Komplexität?
Die Groß-O-Notation
Komplexität von Schleifen
Effizienz-Beispiel
Schlussfolgerungen
top related