Letzte Nacht habe ich mit einem anderen Programmierer besprochen, dass eine Operation, die O (n) ist, möglicherweise die Leistung von O (1) übertrifft, wenn der O (1) -Algorithmus eine große Konstante enthält. Er war anderer Meinung, also habe ich es hierher gebracht.
Gibt es Beispiele für Algorithmen, die die in der darunter liegenden Klasse weit übertreffen? Beispielsweise ist O (n) schneller als O (1) oder O (n 2 ) schneller als O (n).
Mathematisch kann dies für eine Funktion mit einer asymptotischen Obergrenze demonstriert werden, wenn Sie konstante Faktoren außer Acht lassen. Existieren solche Algorithmen jedoch in der Natur? Und wo würde ich Beispiele dafür finden? Für welche Arten von Situationen werden sie verwendet?
algorithms
big-o
KyleWpppd
quelle
quelle
n
groß genug machen, um die Konstante zu kompensieren (was der Punkt der Big-O-Notation ist).Antworten:
Suchvorgänge in sehr kleinen, festen Datentabellen. Eine optimierte Hash-Tabelle kann 0 (1) sein und dennoch aufgrund der Kosten der Hash-Berechnung langsamer als eine binäre Suche oder sogar eine lineare Suche.
quelle
Matrix-Multiplikation. Der naive O (n ^ 3) -Algorithmus wird in der Praxis häufig schneller als Strassens O (n ^ 2.8) für kleinräumige Matrizen verwendet. und Strassen wird anstelle des O (n ^ 2.3) -Coppersmith-Winograd-Algorithmus für größere Matrizen verwendet.
quelle
Ein einfaches Beispiel ist der Unterschied zwischen verschiedenen Sortieralgorithmen. Mergesort, Heapsort und einige andere sind O (n log n) . Quicksort ist der schlechteste Fall von O (n ^ 2) . Aber oft ist Quicksort schneller und arbeitet im Durchschnitt wie O (n log n) . Mehr Infos .
Ein anderes Beispiel ist die Erzeugung einer einzelnen Fibonacci-Zahl. Der iterative Algorithmus ist O (n) , während der matrixbasierte Algorithmus O (log n) ist . Dennoch ist der iterative Algorithmus für die ersten paar tausend Fibonacci-Zahlen wahrscheinlich schneller. Das kommt natürlich auch auf die Umsetzung an!
Algorithmen mit einer besseren asymptotischen Leistung können kostspielige Operationen enthalten, die bei einem Algorithmus mit schlechterer Leistung, aber einfacheren Operationen nicht erforderlich sind. Am Ende sagt die O- Anmerkung nur dann etwas über die Leistung aus, wenn das Argument, mit dem sie arbeitet, dramatisch zunimmt (gegen unendlich geht).
quelle
Hinweis: Bitte lesen Sie die Kommentare von @ back2dos und anderen Gurus, da sie in der Tat hilfreicher sind als das, was ich geschrieben habe. Vielen Dank für alle Mitwirkenden.
Ich denke aus der folgenden Tabelle (entnommen aus: Big O-Notation , suche nach "The Pessimistic Nature of Algorithms:"), dass O (log n) nicht immer besser ist, als zu sagen, O (n). Also, ich denke, dein Argument ist gültig.
quelle
y = 1
,y = log x
und so weiter und die Kreuzung vony = 1
undy = x
ist eigentlich der Punkt(1,1)
. Wenn dies wirklich richtig wäre, würde man sagen, dass Algorithmen mit höherer Komplexität für 0 bis 2 Einträge schneller sein können, was die Leute kaum interessieren würden. Was in der Grafik völlig außer Acht gelassen wird (und woraus der wahrnehmbare Leistungsunterschied resultiert), sind konstante Faktoren.Für praktische Werte von
n
ja. Dies kommt in der CS-Theorie häufig vor. Oft gibt es einen komplizierten Algorithmus mit technisch besserer Big-Oh-Leistung, aber die konstanten Faktoren sind so groß, dass sie unpraktisch werden.Mein Professor für Computergeometrie hatte einmal einen Algorithmus zur Triangulation eines Polygons in linearer Zeit beschrieben, aber er schloss mit "sehr kompliziert. Ich glaube, niemand hat ihn tatsächlich implementiert" (!!).
Auch Fibonacci-Haufen haben bessere Eigenschaften als normale Haufen, sind aber nicht sehr beliebt, da sie in der Praxis nicht so gut funktionieren wie normale Haufen. Dies kann auf andere Algorithmen übergehen, die Heaps verwenden - beispielsweise ist Dijkstras kürzester Pfad mit einem Fibonacci-Heap mathematisch schneller, in der Praxis jedoch normalerweise nicht.
quelle
Vergleichen Sie das Einfügen in eine verknüpfte Liste und das Einfügen in ein Array mit geänderter Größe.
Die Datenmenge muss ziemlich groß sein, damit sich das Einfügen der verknüpften Liste O (1) lohnt.
Eine verknüpfte Liste hat zusätzlichen Overhead für die nächsten Zeiger und Dereferenzen. Ein Array mit veränderbarer Größe muss Daten kopieren. Das Kopieren ist O (n), aber in der Praxis sehr schnell.
quelle
Die Big-Oh-Notation wird verwendet, um die Wachstumsrate einer Funktion zu beschreiben. Daher ist es möglich, dass ein O (1) -Algorithmus schneller ist, jedoch nur bis zu einem bestimmten Punkt (dem konstanten Faktor).
Gemeinsame Notationen:
O (1) - Die Anzahl der Iterationen (manchmal können Sie dies als von der Funktion aufgewendete Benutzerzeit bezeichnen) hängt nicht von der Größe der Eingabe ab und ist tatsächlich konstant.
O (n) - Die Anzahl der Iterationen wächst linear mit der Größe der Eingabe. Bedeutung - Wenn der Algorithmus eine Eingabe N, 2 × N-mal durchläuft, wird er weiterhin als O (n) betrachtet.
O (n ^ 2) (quadratisch) - Die Anzahl der Iterationen ist das Quadrat der Eingabegröße.
quelle
Regex-Bibliotheken werden normalerweise für das Backtracking implementiert, bei dem die Exponentialzeit im ungünstigsten Fall größer ist als bei der DFA-Generierung mit einer Komplexität von
O(nm)
.Naives Backtracking kann eine bessere Leistung erbringen, wenn die Eingabe auf der Überholspur bleibt oder fehlschlägt, ohne dass ein übermäßiges Backtracking erforderlich ist.
(Obwohl diese Entscheidung nicht nur auf der Leistung basiert, werden auch Rückverweise zugelassen.)
quelle
Ein
O(1)
Algorithmus:Ein
O(n)
Algorithmus:Für jeden Wert von
n
wheren < one_million
ist derO(n)
im Beispiel angegebene Algorithmus eindeutig schneller als derO(1)
Algorithmus.Dieses Beispiel ist zwar ein bisschen scherzhaft, entspricht aber im Grunde dem folgenden Beispiel:
Sie müssen die Konstanten und Koeffizienten in Ihrem wissen
O
Ausdruck, und Sie müssen den erwarteten Bereich von wissenn
, um zu bestimmen , a priori , der Algorithmus schneller sein wird am Ende.Andernfalls Sie müssen Benchmark die beiden Algorithmen mit Werten
n
im erwarteten Bereich , um zu bestimmen , a posteriori , die schneller ist beendet Algorithmus auf.quelle
Sortierung:
Die Einfügungssortierung ist O (n ^ 2), übertrifft jedoch andere O (n * log (n)) - Sortieralgorithmen für eine geringe Anzahl von Elementen.
Dies ist der Grund, warum die meisten Sortierimplementierungen eine Kombination aus zwei Algorithmen verwenden. Verwenden Sie beispielsweise die Zusammenführungssortierung, um große Arrays auf eine bestimmte Größe aufzuteilen, und verwenden Sie dann die Einfügesortierung, um die kleineren Einheiten zu sortieren und sie erneut mit der Zusammenführungssortierung zusammenzuführen.
Weitere Informationen finden Sie unter Timsort der aktuellen Standardimplementierung von Python- und Java 7-Sortierungen, die diese Technik verwenden.
quelle
Der in der Praxis verwendete Vereinigungsalgorithmus ist für einige pathologische Eingaben im schlimmsten Fall exponentiell.
Es gibt einen polynomiellen Vereinigungsalgorithmus , der in der Praxis jedoch zu langsam ist.
quelle
Bubblesort im Speicher kann die Leistung von QuickSort übertreffen, wenn das Programm auf die Festplatte ausgelagert wird oder wenn beim Vergleich alle Elemente von der Festplatte gelesen werden müssen.
Dies sollte ein Beispiel sein, auf das er sich beziehen kann.
quelle
Häufig setzen die fortgeschritteneren Algorithmen einen gewissen (teuren) Installationsaufwand voraus. Wenn Sie es nur einmal ausführen müssen, ist die Brute-Force-Methode möglicherweise besser geeignet.
Beispiel: Die binäre Suche und die Suche nach Hashtabellen sind pro Suche viel schneller als die lineare Suche, Sie müssen jedoch die Liste sortieren bzw. die Hashtabelle erstellen.
Die Sortierung kostet Sie N log (N) und die Hash-Tabelle kostet mindestens N. Wenn Sie jetzt Hunderte oder Tausende von Suchvorgängen durchführen, ist dies immer noch eine amortisierte Einsparung. Wenn Sie jedoch nur ein oder zwei Suchvorgänge ausführen müssen, ist es möglicherweise sinnvoll, nur die lineare Suche durchzuführen und die Startkosten zu sparen.
quelle
Die Entschlüsselung ist oft 0 (1). Zum Beispiel ist der Schlüsselraum für DES 2 ^ 56, so dass das Entschlüsseln einer Nachricht eine zeitlich konstante Operation ist. Es ist nur so, dass Sie einen Faktor von 2 ^ 56 haben, also ist es eine wirklich große Konstante.
quelle
Verschiedene Implementierungen von Sets kommen mir in den Sinn. Eine der naivsten ist die Implementierung über einen Vektor, was bedeutet
remove
, dass auchcontains
und deshalb auchadd
alle O (N) nehmen.Eine Alternative ist die Implementierung über einen allgemeinen Hash, der Eingabehashes auf Eingabewerte abbildet. Eine solche Mengenimplementierung führt mit O (1) für
add
,contains
undremove
.Wenn wir annehmen, dass N ungefähr 10 ist, ist die erste Implementierung wahrscheinlich schneller. Um ein Element zu finden, müssen lediglich 10 Werte mit einem verglichen werden.
Die andere Implementierung muss alle Arten von cleveren Transformationen starten, die viel teurer sein können, als 10 Vergleiche anzustellen. Bei all dem Overhead kann es sogar zu Cache-Fehlern kommen, und dann spielt es keine Rolle, wie schnell Ihre Lösung theoretisch ist.
Dies bedeutet nicht, dass die schlechteste Implementierung, die Sie sich vorstellen können, eine anständige übertrifft, wenn N klein genug ist. Für ausreichend kleine N bedeutet dies einfach, dass eine naive Implementierung mit geringem Platzbedarf und Overhead tatsächlich weniger Anweisungen erfordern und weniger Cache-Fehler verursachen kann als eine Implementierung, bei der die Skalierbarkeit an erster Stelle steht und die daher schneller ist.
Man kann nicht wirklich wissen, wie schnell etwas in einem realen Szenario ist, bis man es in eines einsetzt und es einfach misst. Oft sind die Ergebnisse überraschend (zumindest für mich).
quelle
Ja, für entsprechend kleines N. Es wird immer ein N geben, über dem Sie immer die Reihenfolge O (1) <O (lg N) <O (N) <O (N log N) <O (N c haben ) <O (c ^ N) (wobei O (1) <O (lg N) bedeutet, dass bei einem O (1) -Algorithmus weniger Operationen ausgeführt werden, wenn N geeignet groß ist und c eine feste Konstante ist, die größer als 1 ist ).
Angenommen, ein bestimmter O (1) -Algorithmus benötigt genau f (N) = 10 ^ 100 (a googol) -Operationen und ein O (N) -Algorithmus benötigt genau g (N) = 2 N + 5 Operationen. Der O (N) -Algorithmus liefert eine höhere Leistung, bis N ungefähr ein Googol ist (tatsächlich, wenn N> (10 ^ 100 - 5) / 2). Wenn Sie also nur damit gerechnet haben, dass N im Bereich von 1000 bis einer Milliarde liegt, haben Sie würde unter Verwendung des O (1) -Algorithmus eine größere Strafe erleiden.
Zum realistischen Vergleich können Sie auch n-stellige Zahlen miteinander multiplizieren. Der Karatsuba-Algorithmus besteht aus höchstens 3 n ^ (lg 3) Operationen (dh ungefähr 0 (n ^ 1,585)), während der Schönhage-Strassen-Algorithmus 0 (N log N log N) ist, was eine schnellere Ordnung darstellt , aber zu zitieren ist Wikipedia:
Wenn Sie also 500-stellige Zahlen miteinander multiplizieren, ist es nicht sinnvoll, den Algorithmus zu verwenden, der durch große O-Argumente "schneller" ist.
EDIT: Sie können f (N) im Vergleich zu g (N) bestimmen, indem Sie die Grenze N-> unendlich von f (N) / g (N) nehmen. Wenn die Grenze 0 ist, dann ist f (N) <g (N), wenn die Grenze unendlich ist, dann ist f (N)> g (N), und wenn die Grenze eine andere Konstante ist, dann sind f (N) ~ g (N) in Bezug auf die große O-Notation.
quelle
Die Simplex-Methode für die lineare Programmierung kann im schlimmsten Fall exponentiell sein, während relativ neue Algorithmen für innere Punkte polynomisch sein können.
In der Praxis tritt der exponentiell schlechteste Fall für die Simplex-Methode jedoch nicht auf - die Simplex-Methode ist schnell und zuverlässig, während frühe Algorithmen für innere Punkte viel zu langsam waren, um wettbewerbsfähig zu sein. (Es gibt jetzt modernere innere Punkt - Algorithmen , die sind wettbewerbsfähig - aber die Simplex - Methode ist auch ...)
quelle
Ukkonens Algorithmus zum Erstellen von Suffixen lautet O (n log n). Es hat den Vorteil, dass es "online" ist, dh Sie können schrittweise mehr Text anhängen.
In letzter Zeit haben andere komplexere Algorithmen behauptet, in der Praxis schneller zu sein, hauptsächlich weil ihr Speicherzugriff eine höhere Lokalität aufweist, wodurch die Prozessor-Cache-Auslastung verbessert und CPU-Pipeline-Stillstände vermieden werden. Siehe z. B. diese Umfrage , in der behauptet wird, dass 70-80% der Verarbeitungszeit für das Warten auf Speicher aufgewendet wird, und dieses Papier , das den "wotd" -Algorithmus beschreibt.
Suffix-Versuche sind wichtig in der Genetik (für die Zuordnung von Gensequenzen) und, etwas weniger wichtig, bei der Implementierung von Scrabble-Wörterbüchern.
quelle
Es gibt immer den schnellsten und kürzesten Algorithmus für jedes genau definierte Problem . Es ist jedoch nur rein theoretisch der (asymptotisch) schnellste Algorithmus.
Bei gegebener Beschreibung eines Problems P und einer Instanz für dieses Problem I werden alle möglichen Algorithmen A und Beweise Pr aufgelistet , wobei für jedes dieser Paare geprüft wird, ob Pr ein gültiger Beweis dafür ist, dass A der asymptotisch schnellste Algorithmus für P ist . Wenn es einen solchen Beweis findet, führt es A auf I aus .
Die Suche nach diesem problemfreien Paar hat die Komplexität O (1) (für ein festes Problem P ), sodass Sie immer den asymptotisch schnellsten Algorithmus für das Problem verwenden. Da diese Konstante in fast allen Fällen so unbeschreiblich groß ist, ist diese Methode in der Praxis völlig unbrauchbar.
quelle
Viele Sprachen / Frameworks verwenden einen naiven Mustervergleich, um Zeichenfolgen anstelle von KMP abzugleichen . Wir suchen eher nach Streichern wie Tom, New York als nach Ababaababababababababababab.
quelle