Lably
Skip to content

Samplesort

Sorting algorithm

  • 11 min read
  • 6 chapters

Related on Lably

Through Quicksort, Sampling (statistics) and Big O notation

Algorithm

Samplesort is a generalization of quicksort. Where quicksort partitions its input into two parts at each step, based on a single value called the pivot, samplesort instead takes a larger sample from its input and divides its data into buckets accordingly. Like quicksort, it then recursively sorts the buckets.

To devise a samplesort implementation, one needs to decide on the number of buckets p. When this is done, the actual algorithm operates in three phases:

  1. Sample p−1 elements from the input (the splitters). Sort these; each pair of adjacent splitters then defines a bucket.
  2. Loop over the data, placing each element in the appropriate bucket. (This may mean: send it to a processor, in a multiprocessor system.)
  3. Sort each of the buckets.

The full sorted output is the concatenation of the buckets.

A common strategy is to set p equal to the number of processors available. The data is then distributed among the processors, which perform the sorting of buckets using some other, sequential, sorting algorithm.

Pseudocode

The following listing shows the above-mentioned three step algorithm as pseudocode and shows how the algorithm works in principle. In the following, A is the unsorted data, k is the oversampling factor, discussed later, and p is the number of splitters.

<b id="mwNw">function</b> sampleSort(A[1..n], <span class="texhtml mvar" style="font-style:italic;" about="#mwt27" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"mvar","href":"./Template:Mvar"},"params":{"1":{"wt":"k"}},"i":0}}]}' id="mwOA">k</span>, <span class="texhtml mvar" style="font-style:italic;" about="#mwt28" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"mvar","href":"./Template:Mvar"},"params":{"1":{"wt":"p"}},"i":0}}]}' id="mwOQ">p</span>) // if average bucket size is below a threshold switch to e.g. quicksort <b id="mwOg">if</b> <i id="mwOw">n</i> / <i id="mwPA">k</i> < threshold <b id="mwPQ">then</b> smallSort(A) /* Step 1 */ select <i id="mwPg">S</i> = [<i id="mwPw">S</i><sub id="mwQA">1</sub>, ..., <i id="mwQQ">S</i><sub id="mwQg">(<i id="mwQw">p</i>−1)<i id="mwRA">k</i></sub>] randomly from // select samples sort <span class="texhtml mvar" style="font-style:italic;" about="#mwt29" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"mvar","href":"./Template:Mvar"},"params":{"1":{"wt":"S"}},"i":0}}]}' id="mwRQ">S</span> // sort sample [<i id="mwRg">s</i><sub id="mwRw">0</sub>, <i id="mwSA">s</i><sub id="mwSQ">1</sub>, ..., s<sub id="mwSg"><i id="mwSw">p</i>−1</sub>, <i id="mwTA">s</i><sub id="mwTQ"><i id="mwTg">p</i></sub>] <- [-∞, <i id="mwTw">S</i><sub id="mwUA"><i id="mwUQ">k</i></sub>, <i id="mwUg">S</i><sub id="mwUw">2<i id="mwVA">k</i></sub>, ..., <i id="mwVQ">S</i><sub id="mwVg">(<i id="mwVw">p</i>−1)<i id="mwWA">k</i></sub>, ∞] // select splitters /* Step 2 */ <b id="mwWQ">for each</b> <i id="mwWg">a</i> in <i id="mwWw">A</i> find <span class="texhtml mvar" style="font-style:italic;" about="#mwt30" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"mvar","href":"./Template:Mvar"},"params":{"1":{"wt":"j"}},"i":0}}]}' id="mwXA">j</span> such that <i id="mwXQ">s</i><sub id="mwXg"><i id="mwXw">j</i>−1</sub> < <i id="mwYA">a</i> <= <i id="mwYQ">s</i><sub id="mwYg"><i id="mwYw">j</i></sub> place <span class="texhtml mvar" style="font-style:italic;" about="#mwt31" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"mvar","href":"./Template:Mvar"},"params":{"1":{"wt":"a"}},"i":0}}]}' id="mwZA">a</span> in bucket <i id="mwZQ">b</i><sub id="mwZg"><i id="mwZw">j</i></sub> /* Step 3 and concatenation */ <b id="mwaA">return</b> concatenate(sampleSort(<i id="mwaQ">b</i><sub id="mwag">1</sub>), ..., sampleSort(<i id="mwaw">b</i><sub id="mwbA"><i id="mwbQ">k</i></sub>))

The pseudo code is different from the original Frazer and McKellar algorithm. In the pseudo code, samplesort is called recursively. Frazer and McKellar called samplesort just once and used quicksort in all following iterations.

Complexity

The complexity, given in Big O notation, for a parallelized implementation with p processors:

Find the splitters.

O(np+log⁡(p))

Send to buckets.

O(p) for reading all nodes
O(log⁡(p)) for broadcasting
O(nplog⁡(p)) for binary search for all keys
O(np) to send keys to bucket

Sort buckets.

O(c(np)) where c(n) is the complexity of the underlying sequential sorting method. Often c(n)=nlog⁡(n).

The number of comparisons, performed by this algorithm, approaches the information theoretical optimum log2⁡(n!) for big input sequences. In experiments, conducted by Frazer and McKellar, the algorithm needed 15% fewer comparisons than quicksort.

Sign in to keep reading

5 more chapters are free with a Lably account.

More in Computing and communication