Kurs:Algorithmen und Datenstrukturen/Vorlesung/Sortieren2 Nicht vergleichsbasiertes Sortieren




Bucket SortBearbeiten

Auf dieser Seite wird der Sortieralgorithmus BucketSort behandelt.

IdeeBearbeiten

Die Elemente kommen aus einem vorher festgelegtem Bereich, z.B. [1...K] für eine natürliche Zahl K und sie sind in [1...K] ungefähr gleich verteilt. Als Eingabe haben wir n Elemente und eine Funktion f die auf den Wertebereich [0,1) abbildet. f(e) ist dann der Sortierschlüssel der Elemente e. Vereinfacht haben wir als Eingabe n Elemente aus dem Wertebereich [0,1), das heißt wir nehmen die Anwendung von Funktion f bereits an. Es wird eine n verkettete Liste erzeugt (buckets), die das Intervall [0,1) in n Teilintervalle der Größe 1/n einteilen. Das bedeutet die Wertebereiche werden in n gleich große Teilintervalle zerlegt. Als nächstes wird jedes Element in die passende Liste eingefügt und danach werden die Listen verkettet. Wir nehmen an, dass die Funktion f eine Eingabe aus n Zahlen in n reellen Zahlen im Bereich [0,1)abbildet. Die Vereinfachte Variante ist, dass die Eingabe der n reellen Zahlen aus dem Bereich [0,1) stammt. Die Aufteilung in Buckets erfolgt dadurch, dass Element e (  ) in Bucket   kommt.

Beispiel 1Bearbeiten

Als Eingabe haben wir 10 Zahlen (n=10)  

Das Element e kommt in Bucket  . Das Bucket i ( ) enthält Werte in  . Für den Fall mit n=10 kommt das Element e in Bucket  . Beispielsweise 0,17 in 1. Das Bucket i enthält Werte in  , z.B. i=1:[1/10,2/20)

Beispiel 2Bearbeiten

Als weiteres Beispiel ist K=100. Weiterhin haben wir als Eingabe folgende Folge:  . Sortiert man die Zahlen nun in die Buckets ein, sind die Zahlen danach folgend eingeordnet.

0-9 2
10-19 12
20-29 23
30-39 34 39
40-49
50-59 54 52
60-69 65
70-79
80-89 89
90-99 94

Von links nach rechts sind es verkettete Listen. Auf die einzelne Buckets, die mehr als ein Element besitzen, muss im Anschluss eventuell noch ein vergleichbasiertes Sortierverfahren angewandt werden. Wie es in diesem Beispiel beim Bucket von 50-59 der Fall ist.

Anschließend erhalten wir als Ausgabe:  .

AlgorithmusBearbeiten

void bucketSort(int[] f, int bound){
   int n = f.length;
   List<List<Integer>> buckets = createBuckets(n);
   for(int i = 0; i < n; i++)
      buckets.get(n*f[i]/bound).add(f[i]);
   int idx = 0;
   for(int i = 0; i < n; i++){
      quickSort(buckets.get(i));
      for(int j = 0; j < buckets.get(i).size(); j++)
         f[idx++] = buckets.get(i).get(j);
   }
}
List<List<Integer>> createBuckets(int n){
   List<List<Integer>> buckets 
      = new ArrayList<List<Integer>>();
   for(int i = 0; i < n; i++)
      buckets.add(new ArrayList<Integer>());
   return buckets;
}

AnalyseBearbeiten

Theorem der TerminierungBearbeiten

Der Algorithmus BucketSort terminiert für jede Eingabe int[] f nach endlicher Zeit.

BeweisBearbeiten

Alle Schleifenvariablen haben wohl-definierte Start- und Endpunkte.

Theorem der KorrektheitBearbeiten

Das Theorem der Korrektheit besagt, dass nach Aufruf BucketSort(f,bound) f ein sortiertes Array mit gleichen Elementen wie vor dem Aufruf ist.

BeweisBearbeiten

Sei i<j beliebig und betrachte f[i] und f[j] nach Anwendung BucketSort(f, bound). Falls n*f[i]/bound = n*f[j]/bound so  sind f[i] und f[j] im selben Bucket gelandet. Nach Annahme ist QuickSort korrekt und hat den Bucket korrekt sortiert.  Anschließend wurde zunächst f[i] und dann f[j] in das Array f geschrieben, also folgt f[i] ≤ f[j]. Fall n*f[i]/bound  ̸=    n*f[j]/bound so muss n*f[i]/bound < n*f[j]/bound gelten  (da i<j), daraus folgt direkt f[i] < f[j]. Also ist f sortiert. 

Theorem der LaufzeitBearbeiten

Das Theorem der Laufzeit besagt, ist f mit n=f‐length ein Array mit Elementen, die gleich verteilt  sind in [1..bound] so hat BucketSort(f, bound) eine Laufzeit von  .

BeweisBearbeiten

Im Schnitt landet in jedes Bucket genau ein Element (da  Gleichverteilung) und die Anwendung des  Sortieralgorithmus auf jedes Bucket (und die innerste  for‐Schleife) kann vernachlässigt werden. Alle anderen  Schleifen haben genau n Durchläufe, also  .  Falls die Annahme der Gleichverteilung nicht gilt,  wird keine lineare Komplexität erreicht. 

AufwandsanalyseBearbeiten

Die zugrunde liegende Annahme ist, dass die Eingabe-Elemente gleich verteilt auf [0,1) sind und wir somit eine uniforme Eingabeverteilung haben. Bzw. im Allgemeinfall ist f(e) gleich verteilt auf [0,1) Im Algorithmus werden n Elemente eingefügt und InsertionSort mit   wird aufgerufen, wobei   die Anzahl der Elemente in Bucket i ist.  

Bei gleich verteilter Eingabe liegt O(n) vor. Die Abschätzung ist intuitiv, da wir n Elemente auf n Buckets verteilen! Wir erwarteten 1 Elemente je Bucket(genauer: O(1) – d.h. konstant). Das Sortieren (z.B. InsertionSort) je Bucket ist dann O(1). Da wir n buckets haben, muss n-mal sortiert werden. Falls die Annahme der Gleichverteilung nicht gilt, wird keine lineare Komplexität erreicht.