What is Run-Time?
"Independent of hardware, programming language and random events" run-time refers to the number of steps that is required to complete a task. When analyzing the run-time of a program, we generally focus on the size n of a list. Consequently, as the size of a list changes, the run-time changes. The run-time of an algorithm is thus modeled as a function, with n as the independent variable and time as the dependent variable.
What is Big-Oh?
Even if a program's run-time is modeled as a function, the information is not very useful if the function is too complicated. Consequently, an upper bound on the run-time is needed. This is where big-Oh comes in. If function f is O(g), we say that the run time of f is like function g in the worst case. Why is this useful information, you ask? First, this allows us to simplify the run-time of a program (function g) so that the performance comes more intuitively. Second, computer programmers and their clients want to achieve their goal in the most efficient way and be certain about it. By providing the performance of code under the worst conditions, one makes a guarantee about your program (i.e. "it can't get any worse than that").
Not All Lists are Equal
When you think about the variables that may factor into a sorting algorithm's run-time, there are several things to consider:
> How large is the list? A smaller list will generally be sorted faster than a larger list.Divide and Conquer!
> Is the list sorted? For some sorts, a sorted list requires very little work to be done.
> Is the list reverse sorted? For some sorts, a reverse sorted list is the worst-case.
> Maybe items in the list are in random order? You can consider this the average case.
In CSC108 we learned about bubble sort, insertion sort and selection sort. However, this term we've focused on a two new sorting algorithms (merge and quick sort) that use a method of dividing-and-conquering.
Recall the concept of dividing-and-conquering that was first introduced with binary search trees. By knowing whether a value is less than or greater than the root, the searcher can ignore half of the tree. Each time you go one level deeper into the tree, you split the tree in half and ignore one side. How many times you can split the tree is log n times. Thus, binary search is O(log n).
Similarly, merge sort and quick sort both split the given list and run recursively on the sublists. Thus the depth of recursive calls is log n. However, with each split of the list, every element in the sublist is compared. By adding up the size of the sublists, we naturally get a total of n comparisons. Thus by dividing and conquering, merge sort and quick sort (for a good pivot choice) run at O(n log n).
An Opportunistic Sort
Although dividing-and-conquering algorithms obviously run much better than linear and quadratic ones, the built-in sorting function in Python is even better. Called "timsort", the built-in sort begins by briefly looking at the list it is given. Depending on the circumstances, the algorithm will choose a different method of sorting the list.
Why is this important? First of all, with many algorithms in computer science, there is always a "special" case where the algorithm runs extremely well or terribly. One algorithm is never the best choice for every type of list. Methods like timsort thus better follow the human process of problem solving (as I have mentioned in older posts).
