How To Grailsort: The Definitive Guide

How To Grailsort: The Definitive Guide is a place to understand the Block Merge Sort Grailsort!

= Chapter 1: Improving Merge Sort = This is Merge Sort. >

It is a pretty useful sort, being $$O(n \log n)$$ worst case time and stable. Probably the only thing that's stopping it from taking over the world with its usefulness is it's space complexity. $$O(n)$$ space complexity!

Remember this figure, our main focus will be reducing it to $$O(1)$$, while retaining Merge Sort's other properties. now, how do we go about doing this?

Breaking it down
Why does Merge Sort use $$O(n)$$ memory? Answering this question will give us an insight to how we can get rid of the memory.

The anwser is, it uses the space to insert items, like Insertion sort. Insertion sort is not $$O(n)$$ though, because for it to insert its items, it must shift a ton of elements over using swaps or overwrites. The merge operation is more efficient because it takes advantage of the structure of the array: e.g there being 2 sorted halves, and because it dosen't need to shift elements over to insert its items, it has an external array for that.

So, how do we make the merge in-place?

With a buffer!

= Chapter 2: Merging with a scrolling buffer= The buffer is a place in the array dedicated to being a "scratchpad", or an auxiliary space. we cannot afford to overwrite the items in the buffer, so we swap them instead!

An Example
Here's an example:

Take the array [x x x x x x x x] [2 3 6 7] [1 4 4 5]. The x's are buffer items that we cannot overwrite, and we want to merge the second 2 blocks.

first, look at the first items of the blocks (not including x's): [2] and [1].

[1] is smaller, so we swap it with an x to the front.

[1 x x x x x x x] [2 3 6 7] [x 4 4 5]

look at the first items of the blocks (not including x's): [2] and [4].

[2] is smaller, so we swap it with an x to the next position.

[1 2 x x x x x x] [x 3 6 7] [x 4 4 5]

keep going...

[1 2 3 x x x x x] [x x 6 7] [x 4 4 5]

[1 2 3 4 x x x x] [x x 6 7] [x x 4 5]

[1 2 3 4 4 x x x] [x x 6 7] [x x x 5]

[1 2 3 4 4 5 x x] [x x 6 7] [x x x x]

At this point, one subarray is entirely filled with x's, so we swap the rest of the elements:

[1 2 3 4 4 5 6 7] [x x x x] [x x x x]

Notice how the x's all moved to the end, and we can now merge another pair of blocks:

[1 2 3 4 4 5 6 7] [x x x x x x x x] [? ? ? ?] [? ? ? ?]

The scrolling buffer
We can keep going on like this until we get to the end.

The process can be done backwards (just use the last x instead of the first), merging until we get to the start. The aptly named scrolling buffer is scrolling through the array, merging as it goes.

The aptly named scrolling buffer is scrolling through the array, merging as it goes.

The buffer is scrambled by this process, though. so the buffer must consist of distinct values, because as long as they are distinct we can recover their order later.

An animated example where the buffer is blue and the blocks to be merged are green: --->

Grailsort actually uses a half-size buffer, only the size of 1 subarray instead of 2. I won't get into why it works, but suffice it to say you can get away with using a buffer half as big to do the same work.

An Example:

[x x x x] [1 4 4 5] [2 3 6 7]

[1 x x x] [x 4 4 5] [2 3 6 7]

[1 2 x x] [x 4 4 5] [x 3 6 7]

[1 2 3 x] [x 4 4 5] [x x 6 7]

[1 2 3 4] [x x 4 5] [x x 6 7]

[1 2 3 4] [4 x x 5] [x x 6 7]

[1 2 3 4] [4 5 x x] [x x 6 7]

[1 2 3 4 4 5 6 7] [x x x x]

=Chapter 3: Collecting The Keys= The first step of Grailsort is to collect $${2^{\lceil \frac {\log_2 n} {2} \rceil}} + {\lfloor 2^{-{\lceil \frac {\log_2 n} {2} \rceil}}n \rfloor} $$ unique keys, to form the buffer. the buffer consists of 2 regions: the Scrolling Buffer and the Key Buffer. The scrolling buffer is placed after the Key Buffer and is $${2^{\lceil \frac {\log_2 n} {2} \rceil}} $$ elements in length. The key buffer is placed in front and consists of the rest of the unique keys.

If there are less unique keys than say, 4, we must resort to a different sorting algorithm, as it will become impossible to efficiently tag and sort blocks with the Grailsort method.

If there are more than 4, we can use stategy 2, smaller / no scrolling buffer and larger blocks.

The process
We can collect keys by maintaining a sorted region of unique keys, so we can see if a new key is unique by binary-searching for it. If the binary search fails, we can insert it into its correct spot in the key-buffer. if the search succeds, we rotate the key-buffer so that the next key to be examined is always right in front of the sorted region.

After that is done, we rotate the sorted region to where it belongs.

Example
Take the list [13 2 12 3 4 8 15 1 11 10 7 5 14 9 6 16], and we want to collect 8 keys.

The first element is always unique, so we can put it in the key buffer.

[13] [2 12 3 4 8 15 1 11 10 7 5 14 9 6 16]

Examine the second element (the first is always unique), [2]. Search for it in the buffer [13]. The search fails, so insertion sort it into its correct place.

[2 13] [12 3 4 8 15 1 11 10 7 5 14 9 6 16]

Examine the third element, [12]. Search for it in the buffer [2 13]. The search fails, so insertion sort it into its correct place.

[2 12 13] [3 4 8 15 1 11 10 7 5 14 9 6 16]

keep going...

[2 3 12 13] [4 8 15 1 11 10 7 5 14 9 6 16]

[2 3 4 12 13] [8 15 1 11 10 7 5 14 9 6 16]

[2 3 4 8 12 13] [15 1 11 10 7 5 14 9 6 16]

[2 3 4 8 12 13 15] [1 11 10 7 5 14 9 6 16]

[1 2 3 4 8 12 13 15] [11 10 7 5 14 9 6 16]

we have now collected 8 keys, so we are done.

we can split the buffer into the scrolling and key buffers. both are length 4 in this case.

[1 2 3 4] [8 12 13 15] [11 10 7 5 14 9 6 16]

How to collect when there are duplicate keys
Let's try it with few uniques.

The list [14 8 16 2 8 16 2 6 10 12 2 2 6 12 2 2] has few unique keys. let's see what grailsort does here.

The first element is always unique, so we can put it in the key buffer.

[14] [8 16 2 8 16 2 6 10 12 2 2 6 12 2 2]

keep going...

[8 14] [16 2 8 16 2 6 10 12 2 2 6 12 2 2]

[8 14 16] [2 8 16 2 6 10 12 2 2 6 12 2 2]

[2 8 14 16] [8 16 2 6 10 12 2 2 6 12 2 2]

Here. the search for [8] succeds, so we must rotate the buffer.

[8] [2 8 14 16] [16 2 6 10 12 2 2 6 12 2 2]

The search for [16] also succeds, so we must rotate the buffer again.

[8 16] [2 8 14 16] [2 6 10 12 2 2 6 12 2 2]

[8 16 2] [2 8 14 16] [6 10 12 2 2 6 12 2 2]

[8 16 2] [2 6 8 14 16] [10 12 2 2 6 12 2 2]

[8 16 2] [2 6 8 10 14 16] [12 2 2 6 12 2 2]

Two uniques are found.

[8 16 2] [2 6 8 10 12 14 16] [2 2 6 12 2 2]

[8 16 2 2] [2 6 8 10 12 14 16] [2 6 12 2 2]

[8 16 2 2 2] [2 6 8 10 12 14 16] [6 12 2 2]

[8 16 2 2 2 6] [2 6 8 10 12 14 16] [12 2 2]

[8 16 2 2 2 6 12] [2 6 8 10 12 14 16] [2 2]

Another unique.

[8 16 2 2 2 6 12 2] [2 6 8 10 12 14 16] [2]

[8 16 2 2 2 6 12 2 2] [2 6 8 10 12 14 16]

We now rotate the buffer to the front.

[2 6 8 10 12 14 16] [8 16 2 2 2 6 12 2 2]

We could not find 8 keys, but we can still use strategy 2.

Animation
An animation of the collect keys procedure. collected keys in blue -->

= Chapter 4: Build Blocks = Now that we have a scrolling buffer of size $${2^{\lceil \frac {\log_2 n} {2} \rceil}} $$, we can build blocks, or sorted subarrays, of size $${2 \times {2^{\lceil \frac {\log_2 n} {2} \rceil}} $$. The process is as follows: