Der effizienteste Sortieralgorithmus für viele identische Schlüssel?

8

Was der effizienteste Algorithmus zum Gruppieren identischer Elemente in einem Array ist, ist Folgendes zu beachten:

  1. Fast alle Elemente werden mehrmals dupliziert.
  2. Die Elemente sind nicht notwendigerweise ganze Zahlen oder etwas anderes, das ähnlich einfach ist. Die Reichweite der Tasten ist nicht einmal gut definiert, geschweige denn klein. Tatsächlich können die Schlüssel beliebige Strukturen sein. Dies schließt die einfachsten Formen des Zählens aus.
  3. Uns sind sowohl asymptotische als auch nicht asymptotische Eigenschaften wichtig, und n kann manchmal klein sein. Wenn n jedoch klein ist, ist die Leistung immer noch wichtig, da diese Funktion mehrere Millionen Mal in einer Schleife mit Millionen kleiner Datensätze aufgerufen werden kann. Dies schließt eine teure Hash-Funktion aus oder verwendet eine komplexe Datenstruktur, die viele Speicherzuweisungen durchführen muss.
  4. Die Daten können in beliebiger Reihenfolge sortiert werden, solange alle identischen Objekte gruppiert sind.

Wenn das verwirrend ist, ist hier ein Beispiel, vorausgesetzt, eine solche Funktion heißt groupIdentical:

%Vor%

Als Erinnerung können wir jedoch nicht annehmen, dass die Daten als Ganzzahlen zusammengesetzt sind.

Bearbeiten: Danke für die Antworten. Mein Hauptproblem beim Hashing war, dass Hash-Tabellen häufig Speicherzuweisungen durchführen. Am Ende habe ich meine eigene Hash-Tabelle geschrieben, die einen Regionallocator verwendet, den ich hatte, um dieses Problem zu umgehen. Funktioniert gut.

    
dsimcha 09.12.2008, 21:00
quelle

9 Antworten

10

Ich denke, du könntest die Objekte einfach hashen, da die richtige Reihenfolge keine Rolle spielt, nur die Gruppierung. Identische Objekte werden im selben Bucket gruppiert. Dies setzt voraus, dass jeder Typ, an dem Sie interessiert sind, eine eigene Hash-Funktion hat, oder Sie können Ihre eigenen definieren und überladen (wobei jeder Typ als Parameter für eine andere hashCode-Funktionsdefinition verwendet wird).

Um Kollisionen zwischen Datentypen zu vermeiden (damit Strings nicht in einem einzigen Bucket enden, wie in einem Beispiel), müssten Sie den Datentyp in den Hash codieren. Wenn Sie zum Beispiel einen 32-Bit-Hash haben, könnten die ersten 5 Bits den Datentyp möglicherweise verschlüsseln, so dass Sie 32 verschiedene Typen in derselben Hash-Map haben können.

BEARBEITEN: Lassen Sie mich nur hinzufügen, dass der Grund, dass ich eine benutzerdefinierte Hash-Map vorschlage, ist, weil ich keine von denen kenne, die genug ihrer internen Implementierung für Sie freigibt, um die Werte aus jedem Bucket zu erhalten. Es könnte eine solche Implementierung geben, von der ich nichts weiß. Es gibt viele Dinge, die ich nicht kenne. :)

    
Bill the Lizard 09.12.2008, 21:04
quelle
4

Das Zauberwort, nach dem Sie suchen, ist multiset (oder Tasche ). Es ist nicht wirklich eine Art überhaupt, da Sie sich nicht um die Reihenfolge kümmern, solange Sie alle Elemente mit gleichen Schlüsseln gruppiert haben. Abhängig von der Sprache, die Sie verwenden, gibt es mehrere vordefinierte Implementierungen, aber im Allgemeinen ist die obige Hashversion asymptotisch optimal. Ich glaube: insert() ist eine konstante Zeit, da Sie einen Hash in O (1 ) und fügen Sie kollidierende Einfügungen an eine Liste in O (1) Zeit an; Sie können ein Element aus den Bins in O (1) Zeit abrufen, Sie greifen einfach den ersten in den Bin; und Sie können daher alle in O (n) Zeit sammeln, da Sie n Elemente mit O (1) für jedes Element abrufen.

    
Charlie Martin 09.12.2008 22:17
quelle
3

Ein galoppierender Mergesort, wie zB die integrierte Python-Sortierung (vgl. timsort ) , hat eine gute erwartete Leistung, wenn große Läufe von bereits sortierten Daten (wie in Ihrem Beispiel, identische Objekte) - Sie überspringen O (log (N)) Arbeit pro Zusammenführung. Sie können auch einen Mergesort über mehrere CPUs und Festplatten verteilen, wenn Ihre Datenmenge extrem groß ist (dies wird als "externe" Sortierung bezeichnet). Es ist jedoch der schlechteste Fall O (Nlog (N)).

Die einzigen Arten, die schneller sind als Nlog (N), zählen Sortierungen, die eine gemeinsame Eigenschaft der Schlüssel ausnutzen. Um eine lineare Zeitsortierung (Hash-Tabelle oder Radix / Bucket-Sortierung) zu verwenden, müssen Sie die Struktur mit einer Hash-Funktion versehen, um eine Art numerischen Schlüssel zu generieren.

Radix sort führt mehrere Durchläufe durch die Schlüssel durch, so dass die erwartete Zeit länger ist als eine Hashtabellen-Methode; und da Sie sich nicht um die lexikographische Ordnung kümmern, klingt die Hash-Tabellen-Lösung für Sie besser, wenn Sie es sich leisten können, die Schlüssel zu hashen.

    
user26294 09.12.2008 21:10
quelle
1

3-Wege-QuickSort funktioniert sehr gut, wenn eine große Anzahl von Duplikaten vorhanden ist.

    
CMS 09.12.2008 21:14
quelle
1

Ich denke, das Hashing in Buckets wäre die beste Lösung, wenn man annimmt, dass es einen Hash gibt, der operator = mapping (0.0 könnte nicht auf dasselbe -0.0 hacken, aber sie könnten "gleich" sein). Unter der Annahme, dass Sie nur einen Gleich- und Kleiner-als-Operator haben, könnten Sie einen rudimentären Schnellsortieralgorithmus implementieren, bei dem das erste Element als Drehpunkt ausgewählt wird und weniger als in einer Gruppe und größer als in einer anderen Gruppe gesetzt und dann wiederholt wird der Prozess für jede Gruppe.

    
FryGuy 09.12.2008 21:16
quelle
0

Wenn Sie den Bereich der möglichen Werte kennen und dieser klein ist, könnten Sie Folgendes tun: (Pseudo-isch-Code)

%Vor%     
recursive 09.12.2008 21:16
quelle
0

Ich denke, da Sie beliebige Objekte haben, die Sie nicht zu sehr kopieren möchten, könnten Sie einfach Verweise oder Zeiger für die Sortierung verwenden und, wenn nötig, die Objekte in der Reihenfolge danach kopieren.

    
Svante 09.12.2008 21:19
quelle
0

Vielleicht ein R + B- oder AVL-Baum? Dann wieder - es wäre immer noch O (NlogN). Könnte auch Heapsort verwenden - wird nicht schlechter und keine zusätzliche Speicherauslastung ...

    
Vilx- 09.12.2008 21:36
quelle
0

Ein einfacher Algorithmus mit der Ausführungsreihenfolge von O (n (n-1) / 2) ist wie folgt:

  1. Nehmen wir an, dass das Eingabearray als Eingabe mit der Größe n bezeichnet wird.
  2. Ordnen Sie einen Speicher für das Rückgabe-Array mit derselben Größe namens Result zu
  3. Ordnen Sie einem Speicher für Boolean-Array dieselbe Größe mit dem Namen Visited zu und setzen Sie alle Visted auf false
  4. Angenommen, es gibt eine Equal-Funktion mit dem Namen Equals return true, wenn beide Elemente gleich oder false sind.
  5. Angenommen Array-Index beginnt von 1 bis n
  6. Siehe Pseudo-C-Code unten:
%Vor%     
lakshmanaraj 10.12.2008 07:16
quelle