* Shell Sort *


Importance:
Sorting algorithms are a very important part of Computer Science and every day life. What would we do without them? How would we find things quickly? Isn’t it hard enough trying to find things in the dictionary when they are in order, wouldn’t it be just pure hell if they were out of order? Well, of course it would, and that’s one of the main reasons Sorting Algorithms are such an important thing in our life. Having our wonderful computer do all of the work for us is a miracle in itself. I am going to explain in the next few lines information about Shell sort, and how its used, how efficient it is, and a piece of sample code so you can see how it works, and perhaps even implement it in your own code!

History:
The Shell Sort algorithm was invited by Donald L. Shell in 1959. It is viewed as the most efficient of the O(n^2) class of sorting algorithms and with that, it is also the most complicated and viewed bye some as being quite unstable. This algorithm was invited to improve on the insertion sort algorithm, and it does that quite well for almost every case. It works by sorting smaller sets of data, and works its way with larger and larger lists till all of the data has been sorted successfully. This algorithm is typically called a diminishing increment sort because of its sequence of diminishing increments.

Increments:
This algorithm makes many passes through the list of data, and each time it does this, it sorts the smaller sets using insertion sort. Work done by a man named Knuth experimented with this algorithm and came up with the following formulas for the spacing of your shell sort.


H(X) = 3 x H(X-1) + 1
H(1)                    = 1
H(2) = (3 x 1) + 1      = 4
H(3) = (3 x 4) + 1      = 13
H(4) = (3 x 13) + 1     = 40
H(5) = (3 x 40) + 1     = 121
H(6) = (3 x 121) + 1    = 364

This basically means that a list of size less than 13 should be used with spaces of 1. For a list of say 100 items, we find a value for H(X) where it’s greater than it.H(4) being equal to 40 is too small, and in this case, H(5) = 121 and that number is the first greater than 100. Our increment is going to be H(X-2) or H(3) and we find, in this case it is going to be13 than 4 than 1 [Which is basically insertion sort].

Note that this is not the ONLY way that you can decide how you are going to space your Shell Sort algorithm, it is just one of the many that are out there, and I found it as being one of the easiest to comprehend and seems to work quite well for most cases.

How it works:
This algorithm works in a very peculiar fashion. What it does is it makes many passes to our list of data. On the first pass, it is going to check every i-th item in the list. Lets say for example we have a list of 100 items. In the first pass, we are going to check x[0], x[13], x[26], x[39], x[52], x[65], x[78] and x[91] (thinking of these as elements in an array where 0 is the first element). It is going to look at all of these values and sort them with Insertion Sort. On the next run, it is going to check the values of x[1], x[14], x[27], x[40], x[53], x[66], x[79] and x[92]. I am not going to list out every combination, but it is going to do x[0] - x[12] and every multiple of 13 there-after. Once those little lists are sorted, we are going to choose yet another increment. Using Knuth’s formula, we would most likely use 4 for our next. Choosing x[0], x[4], x[8], x[12], x[16], and so on, the list is already growing very quickly and will now be 4 lists of 25 items. Insertion sort will be used once again to sort these tiny lists of data. Finally in the last stage of the sort, most of our values will be in about the correct area for their final destination, and when we run the insertion sort with an increment of 1, it will go rather quickly because the values will not have to move too far.

Sorting times:
The average sort time for Shell sort is approximately O(n^1.25) and its worst-case is about O(n^1.5). Compared to the other O(n^2) algorithms, Shell Sort is about 5 times as quick as Bubble Sort and about twice as quick as Insertion Sort by itself. Shell Sort is not the quickest sorting method however; there are examples like quick sort, merge sort and heap sort which are able to sort lists many times faster; they are in a category of O(n log n), they can complete the same sorting in MUCH less time. This algorithm is very helpful when you are sorting lists which are typically less then 5000 items and it’s good for re-sorting smaller lists because most of the data is already in order, and the algorithm will not have to do nearly as much work. Anything much larger then that just increases exponentially and the user of the computer will be ready to kick in their box waiting for it to complete.

This algorithm is the most efficient when the highest values are in the front of the list and smaller values are located towards the end of it, or if the list is already in order. In these scenarios, they are switched right off the bat, and in most other O(n^2), like Bubble and Insertion sort, these values are going to spend a long time moving to the right and to the left depending on which you are looking at, so this algorithm really saves a lot of time in these cases.

Teaching:
This algorithm is a fairly easy algorithm to learn, it’s not the easiest, but it’s defiantly not the hardest. It is easy to implement and is quite efficient for its simplicity. It is taught in most universities and used in many fields in Computer Science.



//
// Code from http://www.cise.ufl.edu/~sahni/dsaaj/enrich/c2/shell.htm
//

public class ShellSort
{
   /** sort the elements a[0 : a.length - 1] using
     * the shaker sort method */
   public static void shellSort(Comparable [] a)
   {
      int incr = a.length / 2;  // initial increment
      while (incr >= 1)
      {// insertion sort all sequences spaced by incr
         for (int i = incr; i < a.length; i++)
         {// insert a[i] into a[i - incr], a[i - 2*incr], ...
            Comparable insertElement = a[i];
            int j;
            for (j = i - incr;
                 j >= 0 && insertElement.compareTo(a[j]) < 0;
                 j -= incr)
               a[j + incr] = a[j];
            a[j + incr] = insertElement;
         }

         // reduce increment by a factor of 2.2
         if (incr == 2)
            incr = 1;  // last increment must be 1
         else
            incr = (int) (incr / 2.2);
      }
   }
}



Bibilography:
http://ciips.ee.uwa.edu.au/~morris/Year2/PLDS210/niemann/s_shl.htm
http://linux.wku.edu/~lamonml/algor/sort/shell.html
http://epaperpress.com/sortsearch/index.html
http://www.cise.ufl.edu/~sahni/dsaaj/enrich/c2/shell.htm


Report by Alexander C. Schrepfer (University of Hawaii at Manoa)