views:

146

answers:

2

Hi SO,

Is there a better way to shell sort using C#?

// array of integers to hold values
private int[] a = new int[100];

// number of elements in array
private int count;

// Shell Sort Algorithm
public void sortArray()
{
  int i, j, increment, temp;

  increment = 3;

  while( increment > 0 )
  {
    for( i=0; i < count; i++ )
    {
      j = i;
      temp = a[i];

      while( (j >= increment) && (a[j-increment] > temp) )
      {
        a[j] = a[j - increment];
        j = j - increment;
      }

      a[j] = temp;
    }

    if( increment/2 != 0 )
    {
      increment = increment/2;
    }
    else if( increment == 1 )
    {
      increment = 0;
    }
    else
    {
      increment = 1;
    }
  }
}

By the way, I'm wondering because I have some different examples of 'elegant' sorts in different languages (like bubble sorting in C# and F#), and I'm comparing them. In real life, I'd probably use the following most of the time in C#:

Array.Sort( object[] )

I don't care if these are 'academic' and non-pragmatic patterns. You can down-mod me into oblivion if you want :)

KA

+1  A: 

In my test this is 75% to %90 faster than array sort with the same System.Func comparer. I use it to sort a custom struct. you can easily modify it to sort classes.

public class DualQuickSort<T> where T : struct
    {
        private readonly System.Func<T, T, int> comparer;
        public DualQuickSort(System.Func<T, T, int> comparer)
        {
            this.comparer = comparer;
        }

    public DualQuickSort(IComparer<T> comparer)
        : this(comparer.Compare)
    {

    }

    public  void Sort(T[] a)
    {
        Sort(a, 0, a.Length);
    }
    public  void Sort(T[] a, int fromIndex, int toIndex)
    {
        RangeCheck(a.Length, fromIndex, toIndex);

        DualPivotQuicksort(a, fromIndex, toIndex - 1, 3);
    }
    private static void RangeCheck(int length, int fromIndex, int toIndex)
    {
        if (fromIndex > toIndex)
        {
            throw new ArgumentException("fromIndex > toIndex");
        }
        if (fromIndex < 0)
        {
            throw new IndexOutOfRangeException(fromIndex + " is less than 0");
        }
        if (toIndex > length)
        {
            throw new IndexOutOfRangeException(toIndex + " is greater than " + fromIndex);
        }
    }

    private static void Swap(T[] a, int i, int j)
    {
        var temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
    private  void DualPivotQuicksort(T[] a, int left, int right, int div)
    {
        var len = right - left;

        if (len < 27)
        { // insertion sort for tiny array
            for (var i = left + 1; i <= right; i++)
            {
                for (var j = i; j > left && comparer(a[j] , a[j - 1])==-1; j--)

                {
                    Swap(a, j, j - 1);
                }
            }
            return;
        }
        var third = len / div;
        // "medians"
        var m1 = left + third;
        var m2 = right - third;
        if (m1 <= left)
        {
            m1 = left + 1;
        }
        if (m2 >= right)
        {
            m2 = right - 1;
        }
        if (comparer(a[m1] , a[m2])==-1)
        {
            Swap(a, m1, left);
            Swap(a, m2, right);
        }
        else
        {
            Swap(a, m1, right);
            Swap(a, m2, left);
        }
        // pivots
        var pivot1 = a[left];
        var pivot2 = a[right];
        // pointers
        var less = left + 1;
        var great = right - 1;
        // sorting
        for (var k = less; k <= great; k++)
        {
            if (comparer(a[k] , pivot1)==-1)
            {
                Swap(a, k, less++);
            }
            else if (comparer(a[k], pivot2) == 1)
            {
                while (k < great && comparer(a[great] , pivot2)==1)
                {
                    great--;
                }
                Swap(a, k, great--);
                if (comparer(a[k], pivot1) == -1)
                {
                    Swap(a, k, less++);
                }
            }
        }
        // Swaps
        var dist = great - less;
        if (dist < 13)
        {
            div++;
        }
        Swap(a, less - 1, left);
        Swap(a, great + 1, right);
        // subarrays
        DualPivotQuicksort(a, left, less - 2, div);
        DualPivotQuicksort(a, great + 2, right, div);

        // equal elements
        if (dist > len - 13 && comparer(pivot1,pivot2)!=0)
        {
            for (int k = less; k <= great; k++)
            {
                if (comparer(a[k] , pivot1)==0)
                {
                    Swap(a, k, less++);
                }
                else if (comparer(a[k], pivot2) == 0)
                {
                    Swap(a, k, great--);
                    if (comparer(a[k], pivot1) == 0)
                    {
                        Swap(a, k, less++);
                    }
                }
            }
        }
        // subarray
        if (comparer(pivot1 , pivot2)==-1)
        {
            DualPivotQuicksort(a, less, great, div);
        }
    }
}
Gary
+2  A: 

Improvements you could very easily make:

  • Don't keep state in the object. This should be easy to do just with local variables.
  • Use conventional C# names like ShellSort rather than shellSort
  • Use meaningful names like count instead of x
  • Use the conditional operator, e.g.

    // This replaces your last 12 lines
    int halfIncrement = increment / 2;
    increment = halfIncrement != 0 ? halfIncrement : 1 - increment;
    
  • Make the code generic - why restrict yourself to integers?

  • Make your method take the data in question, and make it an IList<T>
  • Make the sort order arbitrary via an IComparer<T>, providing an overload which uses the default.
  • Declare variables as late as possible to reduce their scope and improve readability

Very little of this is actually sort-related - I haven't verified whether your code is actually a legitimate shell sort or not...

Jon Skeet
very cool Jon - I really appreciate this!
Kaiser Advisor
Do you like, have a life outside Stack Overflow or are you some kind of AI program invented by Google?
Gary
Along the same lines, another possible language-oriented improvement may be to expose ShellSort() as an extension method of IList<T> to allow fluent usage in LINQ queries and the like.
LBushkin
Gary, in order for me to tell you the truth, you'd have to sign one of Google's terrifying NDA's, so perhaps it's best if you never know.
Steven Sudit