A Shell Sort Implementation in Ruby

This algorithm expands on concepts derived from Insertion Sort, and effectively uses those strategies by optimizing and consolidating swap operations per scan. Wikipedia offers some great background information for Shell Sort.

The example below is coded to the "poor" implementation of Shell Sort which operates in O(N2) time1, and room for improvements exist by optimizing the gap sequence; N/2 is the gap strategy used here. An effective gap strategy can lend a O(N3/2) down to O(N4/3) runtime.

  1. def shell_sort (list)
  2. gap = list.length/2
  3. while(gap > 0)
  4. list[gap..list.length-1].each_with_index do |element, index|
  5. j = (index += gap)
  6. index.step(gap-1, -gap) do |j|
  7. if ( j > 0 && j >= gap && element < list[j-gap] ) then
  8. list[j] = list[j-gap]
  9. else
  10. break
  11. end
  12. end
  13. list[j] = element
  14. end
  15. gap /= 2
  16. end
  17. list
  18. end



Elements that are a "gap" apart, are compared and sorted; as the gap diminishes over each repitition, elements which are closest together are eventually compared.

Shell Sort Example 1
Gap Array Elements
input 81 94 11 96 12 35 17 95 28 58 41 75 15
5 35 17 11 28 12 41 75 15 96 58 81 94 95
3 28 12 11 35 15 41 58 17 94 75 81 96 95
1 11 12 15 17 28 35 41 58 75 81 94 95 96



Code Review
Start with a gap of N/2, and iterate until adjacent elements have been processed

  1. def shell_sort (list)
  2. gap = list.length/2
  3. while(gap > 0)

Currently Ruby does not support an iterator which starts at a given index, so we will use the standard array API to obtain a sub-list which starts from "gap" and ends at the last element. We then iterate over that sub-list, and obtain each element and index.

  1. list[gap..list.length-1].each_with_index do |element, index|

The sublist is returned as a seperate object, and therefore it's index starts at "0"; we must compensate for this by adjusting "index" to "index + gap". Additionally, we introduce "j" here so that we can use it outside of the upcoming loop; note, it's value at this point in the sequence is arbitrary and has no meaning.

  1. j = (index += gap)

We then take advantage of Ruby's "step" construct for integers, to loop using a custom step sequence of "-gap", starting at "gap-1". As we scan down the list, we essentially compare and swap elements, in a similar fashion to Insertion Sort.

  1. index.step(gap-1, -gap) do |j|
  2. if ( j > 0 && j >= gap && element < list[j-gap] ) then
  3. list[j] = list[j-gap]
  4. else
  5. break
  6. end
  7. end
  8. list[j] = element

I offer the same caveat as with the Insertion Sort tutorial; I attempted rubify the code, and therefore this implementation may not be as efficient as possible (e.g. use of "step" versus other loop tactics, the use of Ruby's sub-array functionality, having to re-adjust the index, etc..). However, I hope this provides some valuable insight into both Shell Sort and Ruby.

References
1. Weiss, Mark Allen (1999). "Data Structures and Algorithm Analysis in Java". Addison Wesley. ISBN 0201357542.
2. "Shell Sort". Wikipedia. http://en.wikipedia.org/wiki/Shell_sort

0 Comments »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a comment



(c) 2009 sheel kapur / powered by WordPress