CSCI 262 Data Structures

Spring 2018

Lab 11: Analysis

(Back to Labs)

Goals


Overview


This lab will walk you through some exercises concerning analysis. As usual, we ask that you work with a partner or a team. If you end up solo, please raise your hand and we'll pair you with someone else (or make a three-person team if necessary). You are welcome to change partners throughout the semester!


Instructions


Step 1: analyze

For this lab you will try to find the "big-O" complexity of three common algorithms: binary search, insertion sort, and the Fisher-Yates shuffling algorithm. (Yes, you can look these up on Wikipedia, but don't until you've at least tried to analyze them yourself, first.)

To start with, look at the pseudo-code given below for each algorithm, and try to work out the "big-O" complexity using the techniques we discussed in class. For all of these, express the "big-O" in terms of the size of the input (which is a vector in the code we provided, but you can treat it as if it were an array - the operations we use on the vector all take constant time). If you find the pseudo-code too confusing, you can try the actual code implementing these functions in lab11.cpp, but the pseudo-code is likely to be easier to work with! Write up your analyses in the lab 11 quiz in Canvas.

Fisher-Yates shuffle

    shuffle(A) : A is a vector
        let i = A.size - 1
        while i > 0 do
            let index = a random integer between 0 and i, inclusive
            swap(A[i], A[index])
            i--

In words, Fisher-Yates starts at the end of the vector and swaps the last value with the value in a position chosen at random from all possible positions in the vector. The last position is then fixed, and the remaining values are shuffled, i.e., the second-to-last value is swapped with a value chosen at random from all but the last position, etc. When only one position is left, there is no reason to continue and the algorithm is finished. Given a true random value function, this algorithm produces a "perfect" shuffle (i.e., all permutations are equally probable).

Insertion sort

    shuffle(A) : A is a vector
        for i = 1 to A.size - 1 do
            let x = A[i]
            let j = i - 1
            while j >= 0 and A[j] > x do
                A[j + 1] = A[j]
                let j = j - 1  
            A[j + 1] = x

In words, insertion sort starts by assuming that the first value (e.g., the sub-vector of length 1 starting at index 0) is "sorted". Now, starting with the second element and moving up, each element in turn is shifted left (into the sorted part of the vector) until it finds its proper place in the sorted part (which is when the element is no longer less than the element to its left).

Binary search

    binary_search(A, x) : A is a vector, x is an integer
        if A.size == 0 return -1
        let pivot = floor(A.size / 2)
        if A[pivot] == x return pivot
        else if A[pivot] > x return binary_search(A[0..pivot-1], x)
        else return binary_search(A[pivot+1..A.size-1], x)

We briefly studied this algorithm in lecture, so you already know its "big-O" complexity. The basic approach is this: we know the input A is sorted. Look at the middle element - if the middle element is the value we are searching for, then we're done. Else, if the middle element is greater than what we're searching for, recursively search only in the lower half of A. Else, the middle element is less than the search value, so recursively search in the upper half of A. If we ever start our search on an empty vector, then we return -1 to indicate the element isn't there.

Step 2: experiment

How well do these algorithms perform in practice? You will do some experiments to find out. Download lab11.cpp and put it into a new project. The code should compile and run as is. You can do anything you want to with this code - it is your microscope for this lab, but you won't be turning it in.

Modify the example code to run timing experiments on each of the algorithms for various size inputs - you will need quite a few data points to get a good experimental result. Collect the timing data and (optionally) plot it in Excel (or the tool of your choice). Your x-axis should show the size of the input, while your y-axis should indicate timing information. You will want to experiment with and plot each algorithm separately, as it is difficult to get them all on one plot in a useful form. If you choose not to plot your data, please present it in Canvas in a well-labeled tabular form, along with some text explaining what you see in the data.

Based on your experimental data, revisit your "big-O" analyses. Provide an analysis in the lab 11 quiz in Canvas based on your experimental data.