Nehmen wir an, Sie haben ein Flugzeug und es ist wenig Treibstoff. Wenn das Flugzeug nicht 3000 Pfund Passagiergewicht verliert, kann es den nächsten Flughafen nicht erreichen. Um die maximale Anzahl von Menschenleben zu retten, möchten wir zuerst die schwersten Menschen aus dem Flugzeug werfen.
Und oh ja, es gibt Millionen von Menschen im Flugzeug, und wir möchten einen optimalen Algorithmus, um die schwersten Passagiere zu finden, ohne unbedingt die gesamte Liste zu sortieren.
Dies ist ein Proxy-Problem für etwas, das ich in C ++ codieren möchte. Ich würde gerne eine "partielle Sortierung" des Passagiermanifests nach Gewicht durchführen, aber ich weiß nicht, wie viele Elemente ich benötigen werde. Ich könnte meinen eigenen "Partial_Sort" -Algorithmus ("Partial_Sort_accumulate_until") implementieren, aber ich frage mich, ob es einen einfacheren Weg gibt, dies mit Standard-STL zu tun.
Antworten:
Eine Möglichkeit wäre, einen minimalen Haufen zu verwenden (
std::priority_queue
in C ++). So würden Sie es machen, vorausgesetzt, Sie hatten eineMinHeap
Klasse. (Ja, mein Beispiel ist in C #. Ich denke, Sie haben die Idee.)Nach den Standardreferenzen, Laufzeit sollte proportional sein
n log k
, wobein
die Anzahl der Passagiere undk
ist die maximale Anzahl der Elemente auf dem Heap. Wenn wir davon ausgehen, dass das Gewicht der Passagiere in der Regel 100 Pfund oder mehr beträgt, ist es unwahrscheinlich, dass der Haufen zu irgendeinem Zeitpunkt mehr als 30 Gegenstände enthält.Der schlimmste Fall wäre, wenn die Passagiere in der Reihenfolge vom niedrigsten zum höchsten Gewicht präsentiert würden. Dies würde erfordern, dass jeder Passagier zum Haufen hinzugefügt und jeder Passagier vom Haufen entfernt wird. Mit einer Million Passagieren und der Annahme, dass das leichteste 100 Pfund wiegt, ist die
n log k
Zahl jedoch relativ gering.Wenn Sie die Gewichte der Passagiere zufällig ermitteln, ist die Leistung viel besser. Ich verwende so etwas für eine Empfehlungs-Engine (ich wähle die 200 besten Artikel aus einer Liste von mehreren Millionen aus). Normalerweise werden dem Haufen nur 50.000 oder 70.000 Artikel hinzugefügt.
Ich vermute, dass Sie etwas ganz Ähnliches sehen werden: Die Mehrheit Ihrer Kandidaten wird abgelehnt, weil sie leichter sind als die leichteste Person, die sich bereits auf dem Haufen befindet. Und
Peek
ist einO(1)
Operation.Weitere Informationen zur Leistung von Heap-Auswahl und Schnellauswahl finden Sie unter Wenn Theorie auf Praxis trifft . Kurzversion: Wenn Sie weniger als 1% der Gesamtzahl der Elemente auswählen, ist die Heap-Auswahl ein klarer Gewinner gegenüber der Schnellauswahl. Mehr als 1%, dann verwenden Sie Quick Select oder eine Variante wie Introselect .
quelle
std::priority_queue
Dies hilft jedoch nicht bei Ihrem Proxy-Problem:
Damit 1.000.000 Passagiere 3000 Pfund an Gewicht verlieren, muss jeder Passagier (3000/1000000) = 0,003 Pfund pro Person verlieren. Dies könnte erreicht werden, indem jedes Hemd oder alle Schuhe oder wahrscheinlich sogar Fingernagelabschnitte weggeworfen werden, um alle zu retten. Dies setzt eine effiziente Sammlung und Entsorgung voraus, bevor der erforderliche Gewichtsverlust zunimmt, wenn das Flugzeug mehr Treibstoff verbraucht.
Eigentlich erlauben sie keine Fingernagelknipser mehr an Bord, also ist das raus.
quelle
Nachfolgend finden Sie eine recht einfache Implementierung der einfachen Lösung. Ich glaube nicht, dass es einen schnelleren Weg gibt, der zu 100% korrekt ist.
Dies funktioniert, indem die Gruppe der "Toten" aufgefüllt wird, bis die Schwelle erreicht ist. Sobald die Schwelle erreicht ist, gehen wir die Liste der Passagiere durch, die versuchen, diejenigen zu finden, die schwerer sind als die leichteste tote Person. Wenn wir eine gefunden haben, fügen wir sie der Liste hinzu und beginnen dann, die leichtesten Personen von der Liste zu "speichern", bis wir nicht mehr speichern können.
Im schlimmsten Fall entspricht dies in etwa der gesamten Liste. Aber im besten Fall (die "tote Liste" ist richtig mit den ersten X Personen gefüllt) wird es ausgeführt
O(n)
.quelle
total
neben "continue;
Anderes" aktualisieren. Dies ist die Antwort, die ich veröffentlichen wollte. Super schnelle LösungAngenommen, alle Passagiere kooperieren: Verwenden Sie ein paralleles Sortiernetz . (siehe auch das )
Hier ist eine Live-DemonstrationUpdate: Alternatives Video (Sprung auf 1:00)
Bitten Sie zwei Personen, Vergleiche auszutauschen - schneller geht es nicht.
quelle
n
Prozessoren erhalten, trifft nicht zu.@Blastfurnace war auf dem richtigen Weg. Sie verwenden die Schnellauswahl, wenn die Drehpunkte Gewichtsschwellen sind. Jede Partition teilt eine Gruppe von Personen in Gruppen auf und gibt das Gesamtgewicht für jede Gruppe von Personen zurück. Sie brechen den entsprechenden Eimer weiter, bis Ihre Eimer, die den Personen mit dem höchsten Gewicht entsprechen, mehr als 3000 Pfund wiegen und Ihr niedrigster Eimer in diesem Satz 1 Person hat (das heißt, er kann nicht weiter aufgeteilt werden).
Dieser Algorithmus ist linear amortisiert, aber quadratischer Worst-Case. Ich denke, es ist der einzige lineare Zeitalgorithmus .
Hier ist eine Python-Lösung, die diesen Algorithmus veranschaulicht:
Ausgabe:
quelle
Angenommen, Sie haben wie die Gewichte von Personen eine gute Vorstellung davon, welche Maximal- und Minimalwerte wahrscheinlich mit einer Radix-Sortierung in O (n) sortiert werden. Dann arbeiten Sie einfach vom schwersten Ende der Liste zum leichtesten. Gesamtlaufzeit: O (n). Leider gibt es keine Implementierung einer Radix-Sortierung in der STL, aber das Schreiben ist ziemlich einfach.
quelle
Warum verwenden Sie nicht einen Teil-Quicksort mit einer anderen Abbruchregel als "sortiert"? Sie können es ausführen und dann nur die höhere Hälfte verwenden und fortfahren, bis das Gewicht in dieser höheren Hälfte nicht mehr das Gewicht enthält, das mindestens mehr weggeworfen werden muss, als Sie einen Schritt in der Rekursion zurückgehen und die Liste sortieren. Danach können Sie Leute aus dem oberen Bereich dieser sortierten Liste werfen.
quelle
Massively Parallel Tournament Sort: -
Angenommen, standardmäßig drei Sitze auf jeder Seite der Ailse: -
Bitten Sie die Passagiere auf dem Fensterplatz, sich auf den mittleren Sitz zu bewegen, wenn sie schwerer sind als die Person auf dem Fensterplatz.
Bitten Sie die Passagiere auf dem mittleren Sitz, mit dem Passagier auf dem Gangplatz zu tauschen, wenn sie schwerer sind.
Bitten Sie den Passagier auf dem linken Gang, mit dem Passagier auf dem rechten Gang zu tauschen, wenn er schwerer ist.
Blasensortierung der Passagiere auf dem rechten Gangplatz. (Führt n Schritte für n Zeilen aus). - Bitten Sie die Passagiere auf dem rechten Gangplatz, n -1 Mal mit der Person vor ihnen zu tauschen.
5 Treten Sie sie aus der Tür, bis Sie 3000 Pfund erreichen.
3 Schritte + n Schritte plus 30 Schritte, wenn Sie eine wirklich dünne Passagierlast haben.
Für eine Ebene mit zwei Gängen sind die Anweisungen komplexer, aber die Leistung ist ungefähr gleich.
quelle
Ich würde wahrscheinlich verwenden
std::nth_element
, um die 20 schwersten Leute in linearer Zeit abzutrennen. Verwenden Sie dann eine komplexere Methode, um den schwersten der Schweren zu finden und abzustoßen.quelle
Sie können die Liste einmal durchlaufen, um den Mittelwert und die Standardabweichung zu erhalten, und diese dann verwenden, um die Anzahl der Personen zu schätzen, die gehen müssen. Verwenden Sie Partial_Sort, um die Liste basierend auf dieser Nummer zu generieren. Wenn die Vermutung niedrig war, verwenden Sie teilweise_sort für den Rest erneut mit einer neuen Vermutung.
quelle
@James hat die Antwort in den Kommentaren: a
std::priority_queue
wenn Sie einen beliebigen Container verwenden können, oder eine Kombination ausstd::make_heap
undstd::pop_heap
(undstd::push_heap
), wenn Sie so etwas wie a verwenden möchtenstd::vector
.quelle
Hier ist eine Heap-basierte Lösung, die das in Python integrierte Heapq-Modul verwendet. Es ist in Python, beantwortet also nicht die ursprüngliche Frage, ist aber sauberer (IMHO) als die andere veröffentlichte Python-Lösung.
Wenn k = die Anzahl der zu werfenden Passagiere und N = die Anzahl der Passagiere ist, ist der beste Fall für diesen Algorithmus O (N) und der schlechteste Fall für diesen Algorithmus ist Nlog (N). Der schlimmste Fall tritt auf, wenn k für lange Zeit in der Nähe von N ist. Hier ist ein Beispiel für die schlechteste Besetzung:
In diesem Fall (Leute aus dem Flugzeug werfen (mit einem Fallschirm, nehme ich an)) muss k jedoch kleiner als 3000 sein, was << "Millionen von Menschen" ist. Die durchschnittliche Laufzeit sollte daher etwa Nlog (k) betragen, was linear zur Anzahl der Personen ist.
quelle