Ich glaube, es gibt eine Möglichkeit, das k-te größte Element in einem unsortierten Array der Länge n in O (n) zu finden. Oder vielleicht ist es "erwartet" O (n) oder so. Wie können wir das machen?
performance
algorithm
big-o
MrDatabase
quelle
quelle
Antworten:
Dies wird als Finden der Statistik der k-ten Ordnung bezeichnet . Es gibt einen sehr einfachen randomisierten Algorithmus ( Quickselect genannt ), der die
O(n)
durchschnittliche Zeit imO(n^2)
ungünstigsten Fall benötigt, und einen ziemlich komplizierten nicht randomisierten Algorithmus ( Introselect genannt ), der dieO(n)
Worst-Case-Zeit benötigt. Es gibt einige Informationen auf Wikipedia , aber es ist nicht sehr gut.Alles, was Sie brauchen, finden Sie in diesen PowerPoint-Folien. Nur um denO(n)
Basisalgorithmus des Worst-Case-Algorithmus zu extrahieren (Introselect):Es ist auch sehr schön im Buch Einführung in Algorithmen von Cormen et al.
quelle
Wenn Sie
O(n)
im Gegensatz zuO(kn)
oder so etwas einen echten Algorithmus wollen , sollten Sie quickselect verwenden (es ist im Grunde eine Quicksortierung, bei der Sie die Partition wegwerfen, an der Sie nicht interessiert sind). Mein Professor hat eine großartige Zusammenfassung mit der Laufzeitanalyse: ( Referenz )Der QuickSelect-Algorithmus findet schnell das k-te kleinste Element eines unsortierten Arrays von
n
Elementen. Da es sich um einen RandomizedAlgorithmus handelt , berechnen wir die im schlimmsten Fall erwartete Laufzeit.Hier ist der Algorithmus.
Was ist die Laufzeit dieses Algorithmus? Wenn der Gegner Münzen für uns wirft, stellen wir möglicherweise fest, dass der Drehpunkt immer das größte Element ist und
k
immer 1 ist, was eine Laufzeit von ergibtWenn die Auswahl jedoch tatsächlich zufällig ist, ist die erwartete Laufzeit gegeben durch
wo wir die nicht ganz vernünftige Annahme machen, dass die Rekursion immer im größeren von
A1
oder landetA2
.Lassen Sie uns das
T(n) <= an
für einige erratena
. Dann bekommen wirund jetzt müssen wir irgendwie die schreckliche Summe rechts vom Pluszeichen bekommen, um
cn
die links zu absorbieren . Wenn wir es nur so binden , bekommen wir ungefähr . Aber das ist zu groß - es gibt keinen Platz, um ein Extra einzudrücken . Erweitern wir also die Summe mit der Formel der arithmetischen Reihen:2(1/n) ∑i=n/2 to n an
2(1/n)(n/2)an = an
cn
wo wir ausnutzen, dass n "ausreichend groß" ist, um die hässlichen
floor(n/2)
Faktoren durch die viel saubereren (und kleineren) zu ersetzenn/4
. Jetzt können wir weitermachenzur Verfügung gestellt
a > 16c
.Das gibt
T(n) = O(n)
. Es ist klarOmega(n)
, also bekommen wirT(n) = Theta(n)
.quelle
k > length(A) - length(A2)
?A
inA1
undA2
um den Pivot aufgeteilt haben, wissen wir daslength(A) == length(A1)+length(A2)+1
. Also,k > length(A)-length(A2)
ist äquivalent zuk > length(A1)+1
, was wahr ist, wennk
irgendwo drin istA2
.Ein schnelles Google auf diesem ('k-ten größten Elementarray') gab Folgendes zurück: http://discuss.joelonsoftware.com/default.asp?interview.11.509587.17
(Es war speziell für 3D größte)
und diese Antwort:
quelle
Du magst Quicksort. Wähle zufällig ein Element aus und schiebe alles entweder höher oder niedriger. An diesem Punkt wissen Sie, welches Element Sie tatsächlich ausgewählt haben, und wenn es das k-te Element ist, das Sie fertig haben, wiederholen Sie andernfalls mit dem Bin (höher oder niedriger), in das das k-te Element fallen würde. Statistisch gesehen die Zeit Es dauert, bis das k-te Element mit n, O (n) wächst.
quelle
Der Begleiter eines Programmierers zur Algorithmusanalyse gibt eine Version an, die O (n) ist. Obwohl der Autor angibt, dass der konstante Faktor so hoch ist, würden Sie wahrscheinlich die naive Methode bevorzugen, die Liste zu sortieren und dann auszuwählen.
Ich habe den Brief deiner Frage beantwortet :)
quelle
Die C ++ Standardbibliothek hat fast genau diese Funktion Anruf
nth_element
, obwohl sie Ihre Daten nicht verändert. Es hat eine lineare Laufzeit O (N) erwartet und führt auch eine Teilsortierung durch.quelle
Obwohl nicht sehr sicher über die Komplexität von O (n), wird es sicher zwischen O (n) und nLog (n) liegen. Achten Sie auch darauf, näher an O (n) als an nLog (n) zu sein. Die Funktion ist in Java geschrieben
quelle
Ich habe das Finden des k-ten Minimums in n unsortierten Elementen mithilfe der dynamischen Programmierung, insbesondere der Turniermethode, implementiert. Die Ausführungszeit beträgt O (n + klog (n)). Der verwendete Mechanismus ist als eine der Methoden auf der Wikipedia-Seite zum Auswahlalgorithmus aufgeführt (wie in einem der obigen Beiträge angegeben). Sie können über den Algorithmus lesen und auch Code (Java) auf meiner Blog-Seite Finding Kth Minimum finden . Zusätzlich kann die Logik die Liste teilweise anordnen - geben Sie die ersten K min (oder max) in O (klog (n)) Zeit zurück.
Obwohl der Code das k-te Minimum liefert, kann eine ähnliche Logik verwendet werden, um das k-te Maximum in O (klog (n)) zu finden, wobei die Vorarbeiten zum Erstellen des Turnierbaums ignoriert werden.
quelle
Sie können dies in O (n + kn) = O (n) (für die Konstante k) für die Zeit und O (k) für den Raum tun, indem Sie die k größten Elemente verfolgen, die Sie gesehen haben.
Für jedes Element im Array können Sie die Liste der k größten Elemente scannen und das kleinste Element durch das neue ersetzen, wenn es größer ist.
Warrens vorrangige Heap-Lösung ist jedoch ordentlicher.
quelle
O(n log k)
... entartet bei großen k immer noch zu O (nlogn). Ich würde denken, es würde gut funktionieren für kleine Werte von k jedoch ... möglicherweise schneller als einige der anderen hier erwähnten Algorithmen [???]Sexy Schnellauswahl in Python
quelle
a1 = [i for i in arr if i > arr[r]]
unda2 = [i for i in arr if i < arr[r]]
wird das k-te größte Element zurückgegeben.numpy.sort
fürnumpy array
odersorted
für Listen) als mit dieser manuellen Implementierung.Suchen Sie den Median des Arrays in linearer Zeit und teilen Sie das Array mithilfe der Partitionsprozedur genau wie in Quicksort in zwei Teile, wobei die Werte links vom Median kleiner (<) als der Median und rechts größer als (>) sind Auch dies kann in kürzester Zeit geschehen. Gehen Sie nun zu dem Teil des Arrays, in dem das k-te Element liegt. Jetzt wird die Wiederholung zu: T (n) = T (n / 2) + cn, was mir O (n) ergibt.
quelle
Unten finden Sie den Link zur vollständigen Implementierung mit einer ausführlichen Erklärung, wie der Algorithmus zum Auffinden des K-ten Elements in einem unsortierten Algorithmus funktioniert. Die Grundidee besteht darin, das Array wie in QuickSort zu partitionieren. Um jedoch Extremfälle zu vermeiden (z. B. wenn das kleinste Element in jedem Schritt als Drehpunkt ausgewählt wird, so dass der Algorithmus in die Laufzeit O (n ^ 2) ausartet), wird eine spezielle Drehpunktauswahl angewendet, die als Median-of-Medians-Algorithmus bezeichnet wird. Die gesamte Lösung läuft im schlimmsten und im durchschnittlichen Fall in O (n) -Zeit.
Hier ist ein Link zum vollständigen Artikel (es geht darum, das kleinste K-te Element zu finden, aber das Prinzip ist das gleiche, um das größte K- te Element zu finden ):
Finden des k-ten kleinsten Elements in einem unsortierten Array
quelle
Gemäß diesem Artikel wird der folgende Algorithmus
O(n)
im schlimmsten Fall einige Zeit in Anspruch nehmen, um das k-te größte Element in einer Liste von n Elementen zu finden .Analyse: Wie im Originalpapier vorgeschlagen:
Warum wird die Partitionsgröße 5 und nicht 3 angenommen?
Wie bereits erwähnt in Originalpapier :
Jetzt habe ich versucht, den obigen Algorithmus wie folgt zu implementieren:
Nur zur Vervollständigung verwendet ein anderer Algorithmus die Prioritätswarteschlange und benötigt Zeit
O(nlogn)
.Beide Algorithmen können wie folgt getestet werden:
Wie erwartet ist die Ausgabe:
18 18
quelle
Wie wäre es mit diesem Ansatz?
Behalte a
buffer of length k
und a beitmp_max
, tmp_max zu bekommen ist O (k) und wird n-mal gemacht, also so etwas wieO(kn)
Ist es richtig oder fehlt mir etwas?
Es übertrifft zwar nicht den durchschnittlichen Fall der Schnellauswahl und den schlimmsten Fall der Medianstatistikmethode, ist aber ziemlich einfach zu verstehen und zu implementieren.
quelle
Durchlaufen Sie die Liste. Wenn der aktuelle Wert größer als der gespeicherte größte Wert ist, speichern Sie ihn als den größten Wert und drücken Sie die 1-4 nach unten, und 5 wird von der Liste gestrichen. Wenn nicht, vergleichen Sie es mit Nummer 2 und machen Sie dasselbe. Wiederholen Sie diesen Vorgang und vergleichen Sie ihn mit allen 5 gespeicherten Werten. dies sollte es in O (n) tun
quelle
Ich möchte eine Antwort vorschlagen
wenn wir die ersten k Elemente nehmen und sie in eine verknüpfte Liste von k Werten sortieren
Jetzt ist für jeden anderen Wert, selbst für den schlimmsten Fall, wenn wir eine Einfügungssortierung für Rest-nk-Werte durchführen, selbst im schlimmsten Fall die Anzahl der Vergleiche k * (nk) und für vorher zu sortierende k-Werte sei es k * (k-) 1) so kommt es heraus, dass (nk-k) ist, was o (n) ist
Prost
quelle
Eine Erklärung des Median-of-Medians-Algorithmus zum Ermitteln der k-ten größten Ganzzahl aus n finden Sie hier: http://cs.indstate.edu/~spitla/presentation.pdf
Die Implementierung in c ++ ist unten:
quelle
Es gibt auch den Auswahlalgorithmus von Wirth , der einfacher zu implementieren ist als QuickSelect. Der Auswahlalgorithmus von Wirth ist langsamer als der von QuickSelect, wird jedoch mit einigen Verbesserungen schneller.
Genauer. Unter Verwendung der MODIFIND-Optimierung von Vladimir Zabrodsky und der Auswahl des Median-of-3-Pivots und unter Berücksichtigung der letzten Schritte des Partitionierungsteils des Algorithmus habe ich den folgenden Algorithmus entwickelt (möglicherweise "LefSelect" genannt):
In Benchmarks, die ich hier durchgeführt habe , ist LefSelect 20-30% schneller als QuickSelect.
quelle
Haskell-Lösung:
Dadurch wird der Median der Medianlösungen mithilfe der withShape-Methode implementiert, um die Größe einer Partition zu ermitteln, ohne sie tatsächlich zu berechnen.
quelle
Hier ist eine C ++ - Implementierung von Randomized QuickSelect. Die Idee ist, zufällig ein Pivot-Element auszuwählen. Um eine zufällige Partition zu implementieren, verwenden wir eine Zufallsfunktion, rand (), um einen Index zwischen l und r zu generieren, tauschen das Element bei einem zufällig generierten Index mit dem letzten Element aus und rufen schließlich den Standardpartitionsprozess auf, der das letzte Element als Pivot verwendet.
Die Zeitkomplexität der obigen Lösung im ungünstigsten Fall ist immer noch O (n2). Im schlimmsten Fall kann die zufällige Funktion immer ein Eckelement auswählen. Die erwartete zeitliche Komplexität von oben randomisiertem QuickSelect beträgt Θ (n)
quelle
Rufen Sie poll () k mal auf.
quelle
Dies ist eine Implementierung in Javascript.
Wenn Sie die Einschränkung aufheben, dass Sie das Array nicht ändern können, können Sie die Verwendung von zusätzlichem Speicher verhindern, indem Sie zwei Indizes verwenden, um die "aktuelle Partition" zu identifizieren (im klassischen Quicksort-Stil - http://www.nczonline.net/blog/2012/). 11/27 / Informatik-in-Javascript-Quicksort / ).
Wenn Sie die Leistung testen möchten, können Sie diese Variante verwenden:
Der Rest des Codes dient nur dazu, einen Spielplatz zu erstellen:
Führen Sie nun einige Male Tests durch. Aufgrund von Math.random () werden jedes Mal unterschiedliche Ergebnisse erzielt:
Wenn Sie es einige Male testen, können Sie sogar empirisch sehen, dass die Anzahl der Iterationen im Durchschnitt O (n) ~ = Konstante * n ist und der Wert von k den Algorithmus nicht beeinflusst.
quelle
Ich habe mir diesen Algorithmus ausgedacht und scheint O (n) zu sein:
Nehmen wir an, k = 3 und wir möchten das drittgrößte Element im Array finden. Ich würde drei Variablen erstellen und jedes Element des Arrays mit dem Minimum dieser drei Variablen vergleichen. Wenn das Array-Element größer als unser Minimum ist, ersetzen wir die Variable min durch den Elementwert. Wir machen dasselbe bis zum Ende des Arrays. Das Minimum unserer drei Variablen ist das drittgrößte Element im Array.
Und um den K-ten größten Gegenstand zu finden, benötigen wir K-Variablen.
Beispiel: (k = 3)
Kann jemand dies bitte überprüfen und mich wissen lassen, was mir fehlt?
quelle
Hier ist die Implementierung des vorgeschlagenen Algorithmus eladv (ich habe hier auch die Implementierung mit zufälligem Pivot angegeben):
quelle
Es ähnelt der quickSort-Strategie, bei der wir einen beliebigen Drehpunkt auswählen und die kleineren Elemente nach links und die größeren nach rechts bringen
quelle
Gehen Sie zum Ende dieses Links: ...........
http://www.geeksforgeeks.org/kth-smallestlargest-element-unsorted-array-set-3-worst-case-linear-time/
quelle
Sie finden das k-te kleinste Element in O (n) Zeit und konstantem Raum. Wenn wir berücksichtigen, ist das Array nur für ganze Zahlen.
Der Ansatz besteht darin, eine binäre Suche im Bereich der Array-Werte durchzuführen. Wenn wir einen min_value und einen max_value beide im ganzzahligen Bereich haben, können wir eine binäre Suche in diesem Bereich durchführen. Wir können eine Komparatorfunktion schreiben, die uns sagt, ob ein Wert der k-kleinste oder kleiner als der k-kleinste oder größer als der k-kleinste ist. Führen Sie die binäre Suche durch, bis Sie die k-kleinste Zahl erreichen
Hier ist der Code dafür
Klasse Lösung:
quelle
Es gibt auch einen Algorithmus, der den Schnellauswahlalgorithmus übertrifft. Es heißt Floyd-Rivets (FR) -Algorithmus .
Originalartikel: https://doi.org/10.1145/360680.360694
Herunterladbare Version: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.309.7108&rep=rep1&type=pdf
Wikipedia-Artikel https://en.wikipedia.org/wiki/Floyd%E2%80%93Rivest_algorithm
Ich habe versucht, Quickselect und FR-Algorithmus in C ++ zu implementieren. Außerdem habe ich sie mit den Standardimplementierungen der C ++ - Bibliothek std :: nth_element verglichen (was im Grunde eine Introselect-Mischung aus Quickselect und Heapselect ist). Das Ergebnis war die Schnellauswahl und nth_element lief im Durchschnitt vergleichbar, aber der FR-Algorithmus lief ca. doppelt so schnell im Vergleich zu ihnen.
Beispielcode, den ich für den FR-Algorithmus verwendet habe:
quelle
Was ich tun würde, ist Folgendes:
Sie können einfach Zeiger auf das erste und letzte Element in der verknüpften Liste speichern. Sie ändern sich nur, wenn Aktualisierungen an der Liste vorgenommen werden.
Aktualisieren:
quelle
Zuerst können wir eine BST aus einem unsortierten Array erstellen, das O (n) Zeit benötigt, und aus der BST können wir das k-te kleinste Element in O (log (n)) finden, das insgesamt bis zu einer Ordnung von O (n) zählt.
quelle