Week 3 Notes — Algorithms

Saturday, Jan. 16 to Friday, Jan. 22

Week 3 Video (duration 2:25:25)

Malan opens with a discussion of algorithmic cost. The number of times a loop is executed is often an appropriate metric for the cost of a routine, but rest assured, it is far from the only metric or the only important one. Assessing algorithmic cost is our second topic from the subject of computer science. (The first that we have only scratched was the grammar of a computer language.) The main job in this course is to learn meat-and-potatoes programming techniques (which aren’t really computer science, any more than word processing is literature). Why introduce more computer science? Well pragmatically, because it is very easy to write horribly inefficient code, and some time spent on the topic of algorithmic cost can help you think about how to make your code efficient. On the other hand, code correctness is even more important than efficiency, so try to write a correct algorithm first, including tests that it is correct, and then use the correct algorithm and the tests of it as cross-checks on whatever more efficient algorithms you later come up with. Worrying about algorithmic efficiency too early when solving a particular problem is as common as failing to worry about it at all, and it even a name, “premature optimization.”

When I said that lower bounds are generally misleading or unimportant, I alluded to specific circumstances. Here is an example: suppose that you are given a list that was sorted, but then exactly one item has been prepended to the list. Now that is a very specific circumstance — but one that you can easily imagine occurring in a software system, such as software for a business that needs to consult its customer database far more often than it adds or removes customers from the database — and you can see that if you do just one pass of the bubble sort, you will put the new item in the correct position and that after this new item has bubbled to its position then nothing more needs to be done because by assumption all the other items were already sorted. The upshot is that if you know that you are going to re-sort a list each time an item is added to it, then you might be ok with bubble sort even though its upper bound is O(n2) because this special circumstance is going to put the routine near its lower bound.

Although structs are a bit fancy to already be introducing in your second week of C programming (and indeed one of the student’s questions when structs were introduced was essentially, “I’m overwhelmed by all the C syntax, what now?”), it is worthwhile, and this is the only new C syntax for this week. For motivation, structs have some (but most definitely not all!) of the behavior of objects, and objects are an enormously powerful and popular programming paradigm. This is not the time to go investigate “object-oriented programming.” I’m mentioning it just for motivation and to describe the programming language landscape. What you are learning right now by learning C is called “procedural programming” and structs give you just a taste of how objects can group data and behavior.

Also, since you are studying C you might like to know that the C++ extension of C grafts objects onto C — and does it in a bad way! — but C was so popular that C++ caught on for a decade or two until object-oriented languages that you can be much more productive in caught on (especially influential have been Smalltalk, Objective-C, Java, and Swift). The features of the influential object-oriented languages were also grafted onto C++ and so C++ is now a kitchen sink. Malan is not going to go into any of that. He will stick with the 1999 version of ANSI C.

Just for completeness, I’ll mention that yet another powerful paradigm is called “functional programming” and very many things in a functional language are accomplished by passing functions themselves around as if they were variables. Maybe if you want all the elements of an array to add themselves up, you somehow pass the array the function add. So array somehow operates on add instead of add operating on array. You could also pass array the function multiply and get the product of the elements. At present, you want to just hammer on procedural programming until your main limitation in banging out lines of code is simply how fast you can accurately type them.

Problem Set 3

This week’s problem set consists of two programs, plurality.c and one of either runoff.c or tideman.c.

This first program is really quite easy because most of the code is provided. You just fill in the implementation for two functions. I see no reason to use any of the fancy search or sort strategies you have learned. Just use linear search. The only thing that makes it tricky is that you will be accessing the members (name and votes) of the candidate struct, and this involves syntax that you have only just been shown.

Interestingly, the function that prints the winner doesn’t take any arguments. So how can it compute which candidate won? This is an example of what is called “scope” or “visibility.” In particular, candidates is declared outside of main, and it has “global” scope. Any code that follows the declaration can use it. Declarations that occur inside curly braces are generally only visible to code within those curly braces. That is “local” scope.

My plurality.c output:

Brian and Jesse Tie

For runoff.c we have run into the first program where I’d say it is a little hard to figure out exactly what is being asked (rather than perhaps hard implement what is being asked). I copied all the comments in the directions into the code so that I could repeatedly refer to what was being asked.

This program also used a lot of variables with global scope as a way of sharing information between functions.

My runoff.c output for a case where the last voter’s second-ranked choice caused a tie to occur:

Alice and Chen Tie