Was sind die asymptotischen Funktionen? Was ist überhaupt eine Asymptote?
Unter der Annahme einer Funktion f (n) , die die Menge an Ressourcen (CPU-Zeit, RAM, Speicherplatz usw.) beschreibt, die ein Algorithmus verbraucht, wenn er auf eine Eingabe mit der Größe n angewendet wird , definieren wir bis zu drei asymptotische Notationen zur Beschreibung seiner Leistung für große n .
Eine Asymptote (oder asymptotische Funktion) ist einfach eine andere Funktion (oder Beziehung) g (n) , der f (n) immer näher kommt, je größer n wird, aber nie ganz reicht. Der Vorteil von asymptotischen Funktionen besteht darin, dass sie im Allgemeinen viel einfacher zu beschreiben sind, selbst wenn der Ausdruck für f (n) äußerst kompliziert ist. Asymptotische Funktionen werden als Teil der Begrenzungsnotationen verwendet , die f (n) oben oder unten einschränken .
(Hinweis: In dem hier verwendeten Sinne sind die asymptotischen Funktionen erst nach der Korrektur eines konstanten Faktors ungleich Null nahe an der ursprünglichen Funktion, da alle drei Big-O / Θ / Ω-Notationen diese konstanten Faktoren von ihrer Berücksichtigung ausschließen.)
Was sind die drei asymptotischen Grenznotationen und wie unterscheiden sie sich?
Alle drei Notationen werden wie folgt verwendet:
f (n) = O (g (n))
wobei f (n) hier die interessierende Funktion ist und g (n) eine andere asymptotische Funktion ist, mit der Sie versuchen, f (n) zu approximieren . Dies sollte nicht als Gleichheit im engeren Sinne verstanden werden, sondern als formale Aussage darüber, wie schnell f (n) in Bezug auf n im Vergleich zu g (n) wächst , wenn n groß wird. Puristen verwenden häufig die alternative Schreibweise f (n) ∈ O (g (n)) , um zu betonen, dass das Symbol O (g (n)) tatsächlich eine ganze Familie von Funktionen ist, die eine gemeinsame Wachstumsrate aufweisen.
Die Big-ϴ (Theta) -Notation gibt eine Gleichheit über das Wachstum von f (n) bis zu einem konstanten Faktor an (dazu später mehr). Es verhält sich ähnlich wie ein =
Operator für Wachstumsraten.
Die Big-O-Notation beschreibt eine obere Grenze für das Wachstum von f (n) . Es verhält sich ähnlich wie ein ≤
Operator für Wachstumsraten.
Die Big-Ω (Omega) -Notation beschreibt eine niedrigere Grenze für ein Wachstum von f (n) . Es verhält sich ähnlich wie ein ≥
Operator für Wachstumsraten.
Es gibt viele andere asymptotische Bezeichnungen , die jedoch in der Informatikliteratur bei weitem nicht so häufig vorkommen.
Big-O-Notationen und deren Verwendung dienen häufig dazu, die zeitliche Komplexität zu vergleichen .
Was ist Zeitkomplexität?
Zeitkomplexität ist ein ausgefallener Begriff für die Zeitdauer T (n) , die ein Algorithmus benötigt, um in Abhängigkeit von seiner Eingabegröße n auszuführen . Dies kann in Echtzeit (z. B. Sekunden), Anzahl der CPU-Anweisungen usw. gemessen werden. In der Regel wird davon ausgegangen, dass der Algorithmus auf Ihrem alltäglichen von Neumann-Architekturcomputer ausgeführt wird. Aber natürlich können Sie die Komplexität der Zeit nutzen, um über exotischere Computersysteme zu sprechen, bei denen die Dinge möglicherweise anders sind!
Es ist auch üblich, über Raumkomplexität in der Big-O-Notation zu sprechen . Die Speicherkomplexität ist die Speicherkapazität, die zur Vervollständigung des Algorithmus erforderlich ist, z. B. RAM, Festplatte usw.
Es kann vorkommen, dass ein Algorithmus langsamer ist, aber weniger Speicher verwendet, während ein anderer Algorithmus schneller ist, aber mehr Speicher verwendet. Jedes kann unter verschiedenen Umständen angemessener sein, wenn die Ressourcen unterschiedlich eingeschränkt sind. Beispielsweise kann ein eingebetteter Prozessor über begrenzten Arbeitsspeicher verfügen und den langsameren Algorithmus bevorzugen, während ein Server in einem Rechenzentrum über einen großen Arbeitsspeicher verfügt und den schnelleren Algorithmus bevorzugt.
Berechnung von Big-ϴ
Die Berechnung des Big-ϴ eines Algorithmus ist ein Thema, das ein kleines Lehrbuch oder etwa ein halbes Semester der Grundschule füllen kann: In diesem Abschnitt werden die Grundlagen behandelt.
Eine Funktion f (n) im Pseudocode gegeben:
int f(n) {
int x = 0;
for (int i = 1 to n) {
for (int j = 1 to n) {
++x;
}
}
return x;
}
Was ist die zeitliche Komplexität?
Die äußere Schleife läuft n mal. Bei jeder Ausführung der äußeren Schleife wird die innere Schleife n Mal ausgeführt. Damit liegt die Laufzeit bei T (n) = n 2 .
Betrachten Sie eine zweite Funktion:
int g(n) {
int x = 0;
for (int k = 1 to 2) {
for (int i = 1 to n) {
for (int j = 1 to n) {
++x;
}
}
}
return x;
}
Die äußere Schleife läuft zweimal. Die mittlere Schleife läuft n mal. Bei jeder Ausführung der mittleren Schleife wird die innere Schleife n Mal ausgeführt. Damit liegt die Laufzeit bei T (n) = 2n 2 .
Nun stellt sich die Frage, wie hoch die asymptotische Laufzeit beider Funktionen ist.
Um dies zu berechnen, führen wir zwei Schritte durch:
- Konstanten entfernen. Da Algorithmen aufgrund von Eingaben mit der Zeit zunehmen, dominieren die anderen Terme die Laufzeit und machen sie unwichtig.
- Entfernen Sie alle bis auf den größten Begriff. Als n gegen unendlich geht, n 2 outpaces schnell n .
Sie konzentrieren sich auf die vorherrschenden Begriffe und vereinfachen diese .
T (n) = n 2 ≤ (n 2 )
T (n) = 2n 2 ≤ (n 2 )
Wenn wir einen anderen Algorithmus mit mehreren Begriffen haben, würden wir ihn mit denselben Regeln vereinfachen:
T (n) = 2n 2 + 4n + 7 ≤ ≤ (n 2 )
Der Schlüssel bei all diesen Algorithmen ist, dass wir uns auf die größten Terme konzentrieren und Konstanten entfernen . Wir betrachten nicht die tatsächliche Laufzeit, sondern die relative Komplexität .
Berechnung von Big-Ω und Big-O
Seien Sie zunächst gewarnt, dass in der informellen Literatur „Big-O“ häufig als Synonym für Big-Θ behandelt wird, möglicherweise weil die Eingabe von griechischen Buchstaben schwierig ist. Wenn Sie jemand aus heiterem Himmel nach dem Big-O eines Algorithmus fragt, möchte er wahrscheinlich das Big-Θ.
Wenn Sie nun wirklich Big-Ω und Big-O in den zuvor definierten formalen Sinnen berechnen möchten , haben Sie ein großes Problem: Es gibt unendlich viele Big-Ω- und Big-O-Beschreibungen für jede gegebene Funktion! Es ist, als würde man fragen, welche Zahlen kleiner oder gleich 42 sind. Es gibt viele Möglichkeiten.
Für einen Algorithmus mit T (n) ∈ ϴ (n 2 ) ist eine der folgenden Aussagen formal gültig:
- T (n) ≤ O (n 2 )
- T (n) ≤ O (n 3 )
- T (n) ≤ O (n 5 )
- T (n) ≤ O (n 12345 × e n )
- T (n) ≤ Ω (n 2 )
- T (n) ≤ Ω (n)
- T (n) ≤ Ω (log (n))
- T (n) ∈ Ω (log (log (n)))
- T (n) ≤ Ω (1)
Es ist jedoch falsch, T (n) ≤ O (n) oder T (n) ≤ Ω (n 3 ) anzugeben .
Was ist relative Komplexität? Welche Klassen von Algorithmen gibt es?
Wenn wir zwei verschiedene Algorithmen vergleichen, nimmt normalerweise ihre Komplexität zu, wenn die Eingabe ins Unendliche geht. Wenn wir verschiedene Arten von Algorithmen betrachten, können sie relativ gleich bleiben (sich beispielsweise um einen konstanten Faktor unterscheiden) oder stark voneinander abweichen. Dies ist der Grund für die Durchführung einer Big-O-Analyse: um festzustellen, ob ein Algorithmus mit großen Eingaben eine angemessene Leistung erbringt.
Die Klassen von Algorithmen setzen sich wie folgt zusammen:
Θ (1) - konstant. Das Auswählen der ersten Nummer in einer Liste dauert immer gleich lange.
Θ (n) - linear. Zum Beispiel wird das Iterieren einer Liste immer Zeit in Anspruch nehmen, die proportional zur Listengröße ist, n .
Θ (log (n)) - logarithmisch (Basis spielt normalerweise keine Rolle). Beispiele sind Algorithmen, die den Eingaberaum bei jedem Schritt aufteilen, z. B. die binäre Suche.
Θ (n × log (n)) - linear mal logarithmisch ("linearithmisch"). Diese Algorithmen teilen und erobern normalerweise ( log (n) ), während sie ( n ) die gesamte Eingabe wiederholen. Viele gängige Sortieralgorithmen (Merge Sort, Timsort) fallen in diese Kategorie.
Θ (n m ) - Polynom ( n erhöht auf eine beliebige Konstante m ). Dies ist eine sehr häufige Komplexitätsklasse, die häufig in verschachtelten Schleifen vorkommt.
Θ (m n ) - Exponential (jede Konstante m, die auf n angehoben wird ). Viele rekursive und graphische Algorithmen fallen in diese Kategorie.
Θ (n!) - Fakultät. Bestimmte graphische und kombinatorische Algorithmen sind faktorielle Komplexität.
Hat dies etwas mit Best / Average / Worst Case zu tun?
Nr Big-O und seine Familie von Notationen sprechen von einer bestimmten mathematischen Funktion . Sie sind mathematische Werkzeuge, die zur Charakterisierung der Effizienz von Algorithmen eingesetzt werden. Der Begriff „Best / Average / Worst-Case“ hat jedoch nichts mit der hier beschriebenen Theorie der Wachstumsraten zu tun.
Um über das Big-O eines Algorithmus zu sprechen, muss man sich auf ein bestimmtes mathematisches Modell eines Algorithmus mit genau einem Parameter festlegen n
, der die „Größe“ der Eingabe beschreiben soll, in welchem Sinne auch immer dies sinnvoll ist. In der realen Welt haben Eingaben jedoch viel mehr Struktur als nur ihre Länge. Wenn dies ein Sortieralgorithmus ist, konnte ich in den Saiten füttern "abcdef"
, "fedcba"
oder "dbafce"
. Alle haben die Länge 6, aber einer ist bereits sortiert, einer ist umgekehrt und der letzte ist nur ein zufälliges Durcheinander. Einige Sortieralgorithmen (wie Timsort) funktionieren besser, wenn die Eingabe bereits sortiert ist. Wie aber nimmt man diese Inhomogenität in ein mathematisches Modell auf?
Der typische Ansatz besteht darin, einfach anzunehmen, dass die Eingabe aus einer zufälligen, probabilistischen Verteilung stammt. Anschließend wird die Komplexität des Algorithmus über alle Eingaben mit Länge gemittelt n
. Dies gibt Ihnen ein Modell der durchschnittlichen Komplexität des Algorithmus. Ab hier können Sie wie gewohnt die Big-O / Θ / Ω-Notationen verwenden, um das durchschnittliche Fallverhalten zu beschreiben.
Wenn Sie sich jedoch Sorgen über Denial-of-Service-Angriffe machen, müssen Sie möglicherweise pessimistischer sein. In diesem Fall ist es sicherer anzunehmen, dass die einzigen Eingaben diejenigen sind, die den größten Kummer in Ihrem Algorithmus verursachen. Dies gibt Ihnen ein Worst-Case- Komplexitätsmodell des Algorithmus. Anschließend können Sie über Big-O / Θ / Ω usw. des Worst-Case- Modells sprechen .
Ebenso können Sie Ihr Interesse ausschließlich auf die Eingaben konzentrieren, mit denen Ihr Algorithmus am wenigsten Probleme hat, um ein Best-Case- Modell zu erhalten, und dann Big-O / Θ / Ω usw. betrachten.