Amortisierte Komplexität in Laienbegriffen?

74

Kann jemand die amortisierte Komplexität in Laienbegriffen erklären? Es fiel mir schwer, online eine genaue Definition zu finden, und ich weiß nicht, wie sie sich vollständig auf die Analyse von Algorithmen bezieht. Alles Nützliche, auch wenn es extern referenziert wird, wäre sehr dankbar.

Bob John
quelle

Antworten:

98

Die amortisierte Komplexität ist der Gesamtaufwand pro Operation, der über eine Folge von Operationen bewertet wird.

Die Idee ist, die Gesamtkosten der gesamten Sequenz zu garantieren und gleichzeitig zu ermöglichen, dass einzelne Vorgänge viel teurer sind als die fortgeführten Anschaffungskosten.

Beispiel:
Das Verhalten von C ++ std::vector<>. Wenn push_back()die Vektorgröße über ihren vorab zugewiesenen Wert erhöht wird, wird die zugewiesene Länge verdoppelt.

Die Ausführung eines einzelnen push_back()kann daher einige O(N)Zeit in Anspruch nehmen (da der Inhalt des Arrays in die neue Speicherzuordnung kopiert wird).

Da jedoch die Größe der Zuordnung verdoppelt wurde, dauert die Ausführung der nächsten N-1Aufrufe push_back()jeweils einige O(1)Zeit. Die Gesamtzahl der NOperationen wird also noch O(N)Zeit in Anspruch nehmen . Dadurch ergeben sich push_back()fortgeführte Anschaffungskosten O(1)pro Operation.


Sofern nicht anders angegeben, ist die amortisierte Komplexität eine asymptotische Worst-Case-Garantie für jede Abfolge von Operationen. Das heisst:

  • Genau wie bei der nicht amortisierten Komplexität ignoriert die für die amortisierte Komplexität verwendete Big-O-Notation sowohl den festen anfänglichen Overhead als auch konstante Leistungsfaktoren. Für die Bewertung der amortisierten Big-O-Leistung können Sie daher im Allgemeinen davon ausgehen, dass jede Folge von amortisierten Vorgängen "lang genug" ist, um einen festen Startaufwand zu amortisieren. Insbesondere für das std::vector<>Beispiel müssen Sie sich deshalb keine Gedanken darüber machen, ob Sie tatsächlich auf Nzusätzliche Operationen stoßen : Die asymptotische Natur der Analyse setzt dies bereits voraus.

  • Abgesehen von der willkürlichen Länge werden bei der amortisierten Analyse keine Annahmen über die Abfolge von Vorgängen getroffen, deren Kosten Sie messen. Sie ist eine Worst-Case-Garantie für eine mögliche Abfolge von Vorgängen. Unabhängig davon, wie schlecht die Vorgänge ausgewählt werden (z. B. von einem böswilligen Gegner!), Muss eine amortisierte Analyse sicherstellen, dass eine ausreichend lange Abfolge von Vorgängen nicht durchgehend mehr kostet als die Summe ihrer amortisierten Kosten. Aus diesem Grund sind "Wahrscheinlichkeit" und "Durchschnittsfall" (sofern nicht ausdrücklich als Qualifikationsmerkmal angegeben) für die amortisierte Analyse nicht relevant - ebenso wenig wie für eine gewöhnliche Big-O-Analyse im schlimmsten Fall!

kommender Sturm
quelle
31

Bei einer amortisierten Analyse wird die zur Durchführung einer Folge von Datenstrukturoperationen erforderliche Zeit über alle durchgeführten Operationen gemittelt ... Die amortisierte Analyse unterscheidet sich von der Durchschnittsfallanalyse darin, dass die Wahrscheinlichkeit nicht beteiligt ist. Eine amortisierte Analyse garantiert im schlimmsten Fall die durchschnittliche Leistung jeder Operation.

(von Cormen et al., "Introduction to Algorithms")

Das könnte etwas verwirrend sein, da es sowohl besagt, dass die Zeit gemittelt wird, als auch, dass es sich nicht um eine Durchschnittsfallanalyse handelt. Lassen Sie mich versuchen, dies mit einer finanziellen Analogie zu erklären (in der Tat ist "amortisiert" ein Wort, das am häufigsten mit Bank- und Rechnungswesen in Verbindung gebracht wird.)

Angenommen, Sie betreiben eine Lotterie. (Sie kaufen keinen Lottoschein, auf den wir gleich noch eingehen, sondern betreiben die Lotterie selbst.) Sie drucken 100.000 Tickets, die Sie für jeweils 1 Währungseinheit verkaufen. Eines dieser Tickets berechtigt den Käufer zu 40.000 Währungseinheiten.

Angenommen, Sie können alle Tickets verkaufen, verdienen Sie 60.000 Währungseinheiten: 100.000 Währungseinheiten im Verkauf abzüglich des Preises von 40.000 Währungseinheiten. Für Sie beträgt der Wert jedes Tickets 0,60 Währungseinheiten, die über alle Tickets abgeschrieben werden. Dies ist ein zuverlässiger Wert. Darauf können Sie sich verlassen. Wenn Sie es leid sind, die Tickets selbst zu verkaufen, und jemand kommt und anbietet, sie für jeweils 0,30 Währungseinheiten zu verkaufen, wissen Sie genau, wo Sie stehen.

Für den Lotteriekäufer ist die Situation anders. Der Käufer hat beim Kauf eines Lottoscheins einen erwarteten Verlust von 0,60 Währungseinheiten. Aber das ist wahrscheinlich: Der Käufer kann 30 Jahre lang jeden Tag zehn Lottoscheine kaufen (etwas mehr als 100.000), ohne jemals zu gewinnen. Oder sie kaufen eines Tages spontan ein einzelnes Ticket und gewinnen 39.999 Währungseinheiten.

Bei der Datenstrukturanalyse handelt es sich um den ersten Fall, bei dem wir die Kosten für eine Datenstrukturoperation (z. B. Einfügen) über alle Operationen dieser Art amortisieren. Die Durchschnittsfallanalyse befasst sich mit dem erwarteten Wert einer stochastischen Operation (z. B. Suche), bei der wir nicht die Gesamtkosten aller Operationen berechnen können, sondern eine probabilistische Analyse der erwarteten Kosten einer einzelnen Operation liefern können.

Es wird oft behauptet, dass eine amortisierte Analyse für die Situation gilt, in der eine kostenintensive Operation selten ist, und das ist häufig der Fall. Aber nicht immer. Betrachten Sie zum Beispiel die sogenannte "Banker-Warteschlange", eine FIFO-Warteschlange (First-In-First-Out), die aus zwei Stapeln besteht. (Es ist eine klassische funktionale Datenstruktur; Sie können billige LIFO-Stapel aus unveränderlichen, einfach verknüpften Knoten erstellen, aber billige FIFOs sind nicht so offensichtlich). Die Operationen werden wie folgt implementiert:

put(x):  Push x on the right-hand stack.
y=get(): If the left-hand stack is empty:
           Pop each element off the right-hand stack and
             push it onto the left-hand stack. This effectively
             reverses the right-hand stack onto the left-hand stack.
         Pop and return the top element of the left-hand stack.

Nun behaupte ich, dass die amortisierten Kosten von putund getsind O(1), vorausgesetzt, ich beginne und ende mit einer leeren Warteschlange. Die Analyse ist einfach: Ich putgehe immer auf den rechten Stapel und getvom linken Stapel. Abgesehen von der IfKlausel ist jeder putein pushund jeder getein a pop, die beide sind O(1). Ich weiß nicht, wie oft ich die IfKlausel ausführen werde - es hängt vom Muster von puts und gets ab -, aber ich weiß, dass sich jedes Element genau einmal vom rechten zum linken Stapel bewegt. Die Gesamtkosten über die gesamte Folge von ns putund ns getbetragen also: pushns, ns popund ns move, wobei a a moveistpopgefolgt von a push: Mit anderen Worten, die 2n-Operationen (ns putund ns get) führen zu push2ns und 2ns pop. Die fortgeführten Anschaffungskosten eines einzelnen putoder getsind eins pushund eins pop.

Beachten Sie, dass Banker-Warteschlangen genau aufgrund der amortisierten Komplexitätsanalyse (und der Zuordnung des Wortes "amortisiert" zu Finanzen) so genannt werden. Die Warteschlangen von Banker sind die Antwort auf eine häufig gestellte Interviewfrage, obwohl ich denke, dass sie jetzt als zu bekannt angesehen wird: Überlegen Sie sich eine Warteschlange, die die folgenden drei Operationen in der amortisierten O (1) -Zeit implementiert:

1) Holen Sie sich das älteste Element der Warteschlange und entfernen Sie es.

2) Stellen Sie ein neues Element in die Warteschlange.

3) Ermitteln Sie den Wert des aktuellen Maximalelements.

Rici
quelle
21

Das Prinzip der "amortisierten Komplexität" ist, dass, obwohl etwas dabei ziemlich komplex sein kann, da es nicht sehr oft gemacht wird, es als "nicht komplex" betrachtet wird. Wenn Sie beispielsweise einen Binärbaum erstellen, der von Zeit zu Zeit ausgeglichen werden muss - beispielsweise einmal bei jeder 2^nEinfügung -, da das Ausgleichen des Baums zwar recht komplex ist, jedoch nur einmal in n Einfügungen erfolgt (z. B. einmal bei Einfügungsnummer 256, dann erneut bei 512., 1024. usw.). Bei allen anderen Einfügungen ist die Komplexität O (1) - ja, es wird O (n) einmal alle n Einfügungen benötigt, aber es ist nur eine 1/nWahrscheinlichkeit - also multiplizieren wir O (n) mit 1 / n und erhalten O (1). Das heißt also "Amortisierte Komplexität von O (1)" - denn wenn Sie weitere Elemente hinzufügen, ist der Zeitaufwand für das Neuausgleichen des Baums minimal.

Mats Petersson
quelle
1
Was hat "groß genug" damit zu tun? Hier gibt es irrelevante Details, und das Schlüsselkonzept der Multiplikation mit einer Wahrscheinlichkeit wird weggelassen.
Potatoswatter
Nicht qualifizierte amortisierte Leistungsgarantien haben nichts mit Wahrscheinlichkeit zu tun - sie sind absolute Garantien für jede Abfolge von Operationen. Wenn von probabilistischer Leistung die Rede ist, sollte ein Kunstbegriff verwendet werden, der "erwartete" oder "durchschnittliche" Leistung angibt.
Comingstorm
Ich habe es ein wenig umgestaltet, um überflüssiges "groß genug" zu entfernen (womit ich meinte, dass ein kleiner Baum zwar ziemlich häufig neu ausbalanciert werden kann, ein größerer jedoch nicht sehr oft neu ausbalanciert wird - aber ich stimme zu, dass dies nicht der Fall war ein sehr guter Weg, um es auszudrücken, weil der Aufwand auch steigt, wenn es wächst)
Mats Petersson
Die amortisierte Komplexität ist nicht gleichbedeutend mit der durchschnittlichen Fallkomplexität. Wie @comingstorm sagt, kommen also keine Wahrscheinlichkeiten dazu. Um eine amortisierte Analyse auf das Neuausgleichen von Binärbäumen anzuwenden, müssen Sie nachweisen, dass das Neuausgleichen (im schlimmsten Fall) zu jeder Einfügung eine konstante Zeit beiträgt.
Rici
@comingstorm Danke, ich habe meine Antwort korrigiert.
Potatoswatter
5

Amortisierte Mittel, aufgeteilt auf wiederholte Läufe. Das Worst-Case-Verhalten tritt garantiert nicht häufig auf. Wenn zum Beispiel der langsamste Fall O (N) ist, aber die Wahrscheinlichkeit, dass dies geschieht, nur O (1 / N) ist und andernfalls der Prozess O (1) ist, hätte der Algorithmus immer noch die konstante O (1) -Zeit amortisiert . Betrachten Sie einfach die Arbeit jedes O (N) -Laufs als auf N andere Läufe aufgeteilt.

Das Konzept hängt davon ab, dass genügend Läufe vorhanden sind, um die Gesamtzeit aufzuteilen. Wenn der Algorithmus nur einmal ausgeführt wird oder bei jeder Ausführung eine Frist einhalten muss, ist die Komplexität im schlimmsten Fall relevanter.

Kartoffelklatsche
quelle
4

Angenommen, Sie versuchen, das k-te kleinste Element eines unsortierten Arrays zu finden. Das Sortieren des Arrays wäre O (n logn). Wenn Sie also die k-te kleinste Zahl finden, suchen Sie einfach den Index, also O (1).

Da das Array bereits sortiert ist, müssen wir nie wieder sortieren. Wir werden das Worst-Case-Szenario nie mehr als einmal treffen.

Wenn wir n Abfragen durchführen, um das k-te kleinste zu finden, ist es immer noch O (n logn), da es über O (1) dominiert. Wenn wir die Zeit jeder Operation mitteln, ist dies:

(n logn) / n oder O (logn). Also, Zeitkomplexität / Anzahl der Operationen.

Dies ist amortisierte Komplexität.

Ich denke, so geht es, ich lerne es auch nur ..

user2968401
quelle
2

Es ist etwas ähnlich wie das Multiplizieren der Worst-Case-Komplexität verschiedener Zweige in einem Algorithmus mit der Wahrscheinlichkeit, diesen Zweig auszuführen und die Ergebnisse zu addieren. Wenn es also sehr unwahrscheinlich ist, dass ein Zweig genommen wird, trägt dies weniger zur Komplexität bei.

perreal
quelle