Wednesday, 2 April 2014

Sorting and Efficiency

Week 11: Sorting and Efficiency

The last topic of CSC 148 is sorting and efficiency. My first exploration of this topic began in CSC 108, where the bubble, insertion, and selection sorting methods were introduced, and their run times were analyzed. Now we have turned to this topic again with more information. More sorting methods (quick and merge mainly) were introduced, their run times were analyzed, and we began using big O notation to describe efficiency.

To begin with sorting, here is a little summary. For quick sort, a pivot is chosen, then all values less than the pivot go to the left, all values greater than the pivot go to the right, and the pivot is in its proper position. After, quick sort is recursively called on each side of the pivot; the process continues until the list is sorted. For merge sort, the list is first divided in half. Merge sort is then recursively called to sort each half, and then the sorted halves are “merged” together.

Something I have noticed about these sorts is that they are generally much more efficient than bubble, selection and insertion sort. For example, all of the 108 sorts have a worst case run time of O(n2), while merge and quick sort's run times are generally O(nlgn). I wondered why this was, and then it occurred to me. The 108 sorts are iterative over multiple rounds. The number of comparisons in one round increases linearly with the size of the list, giving O(n). In addition, the number of rounds is simply the length of the list or sometimes the length of the list minus one. The number of rounds also increases linearly with the size of the list, giving O(n). This leads to the total number of comparisons/exchanges to be quadratic by the famous formula for the summation of every number from 1 to n. Merge sort, however, takes more recursive and binary approach. By dividing the list in half each round (a binary strategy), the number of recursive calls required only increases by one every time the list size doubles, greatly reducing the run time as opposed to a four fold increase for the 108 sorts. This is where the lgn comes from in merge's run time. So where does the additional n come from? In each recursive round of merge sort, all of the elements in the list are being compared. So the number of comparisons increases linearly with the size of the list, giving O(n). Therefore, the total number of comparisons is the number of comparisons in each round times the number of rounds. The number of comparisons in each round is n; the total number of comparisons is lgn, so the run time of merge sort is O(nlgn). The run time for quick sort offers a similar explanation to merge sort, but only if the pivot chosen lies in the middle of the list. When the pivot is in a middle position, the list is roughly divided in half, with a binary strategy like merge sort. However, when the pivot is too much to the left or too much to the right (in the worst case), quick sort starts to resemble a combination of insertion and bubble sort. The run time complexity here is O(n2), which is the same as both insertion and bubble sort in the worst case. So back to the first sentence of this paragraph, these new sorts are generally more efficient than the 108 sorts because by taking a binary approach, the number of rounds of comparisons is greatly reduced.

Of the things we learned in this class, the one I had the least practice and felt most uncomfortable with was sorting and efficiency (especially run time analysis). However, the examples in class, the past test question, and writing this slog helped me understand it better! 

No comments:

Post a Comment