Skip to content

New Blog

I’ve jumped on the tumblr bandwagon and moved my blog over here. New content will be posted there. Please update your rss links accordingly (that means you Joe ;) ).

A very short history my time with Circle/Square

I landed in Shanghai  and my little nokia buzzed with an automated sms from China Mobile, something along the lines of “We welcome you very much in China. There is happy for your stay.” I couldn’t believe I was receiving this from a multi-billion dollar company only two years before the Beijing olympics, so I did some research with my friend and future business partner, Mathias. Translation by native Mandarin speakers cost 2-4 $/hour, where translation by native English speakers was in 50 $/hour range. There was no middle ground so everyone - even China Mobile - went local.

It was a pretty obviously opportunity. We could hire students at UBC to polish the English for 10 $/hour,  get the first pass done locally for 3 $/hour, and offer essentially the same product as the 50 $/hour guys - but for 1/3rd the price! Fantastic!

The crux of the business was being able to collaborate between Beijing and Vancouver. We decided (in hindsight quite naively), to build a custom internal web app. We found some funding and then some developers and got to work. Circle/Square English Auditing opened for business in Beijing about 6 months later - and closed about 6 months after that.

I learned a ton of things about business and particularly business in China - most saliently that competing on price alone is far from sufficient - but it frustrated me that all our value was being delivered by a web app and I knew almost nothing about *that*. So I went back to school.

Out of historical interest, here is the last revised version of the Circle/Square Business Plan. If you want to know more of what we were up to, it’s *all* in here.  It was the real first business plan I ever wrote and while I inevitably cringe when looking back on it, we did manage to raise 60K USD with it so I suppose that’s something.

Learning is what happens while you’re not at school

Here’s something I think the europeans got right: Taking only a few courses in a semester makes for a better education than taking 5. This is because you actually get time to reflect on what you’ve studied, or even better, surprise yourself when your learning pops up unexpectedly and makes sense of your extracurricular life.

This past year I took networking. Networking is entirely about protocols and not by design. Protocols - shared meaning attributed to arbitrary data - is the foundational building block of all communication - human, animal, or computer. This insight is undoubtedly the most interesting thing I learnt from networking, but I didn’t get that in class.

I spent sleepless nights re-implementing TCP, building an HTTP proxy server, studying all levels of the network stack, but I didn’t get it. I could ace my midterm, but I didn’t get it. My deeper understanding of protocols didn’t come until I took a night off to have fun. I was at a loud funk show at Richards on Richards watching two of my friends communicate with hand gestures. Then I understood protocol.

When I was in Copenhagen I took less class than I ever had before or have since. I had almost no responsibilities. The only assignment throughout the whole semester was a single ‘test’ paper that was a simple pass/fail. And out of my 7 years of University, it was undoubtedly the year I learnt the most. I was having my mind slowly blown open by phenomenology but mostly had a lot of free time. And phenomenology kept showing up in my normal life, making sense of so many things, from the way different people behaved in Aikido class to how I read a book or looked at a painting. In those moments my studies suddenly became relevant and a part of part of who I am. That’s exactly what education should do: grow a person.

I really think UBC (and probably most north american universities) is doing a disservice to their students by expecting 5 courses per semester. The WORK WORK WORK and get the best marks you can so you can get the best job you can is a great way to train future employees - but a horrible way to educate. Alma Mater is latin for “Nourishing Mother.” It’s a beautiful metaphor, but I fear its meaning has been lost in North American universities.

A university should be a place to sow seeds of thought, but we need time to nurture the seeds ourselves. This is the source of original insight - a mind is unique and provides a particular chemistry to grow in. Innovation comes when the seed of an idea has matured into a unique realization and spreads its own seeds to the fertile minds around it. I fear that much schooling is cramming heads full of seeds only to have them rot before given a chance to grow.

Portfolio & Lessons from Applications

Two things I’ve learned over the last few months:

1) GPA doesn’t really matter. Sure it’s nice to have a high GPA, but potential co-workers and employers are looking for evidence of ability, not evidence of the potential for ability. Not to mention I get the sense that if your GPA is *too* high it actually turns people off - (”can this person balance? Are they socially adept? Do they have interests outside of school?” and so forth.)

The exceptions are if you’re planning on applying to grad school. Or if you want to work for Google (on that note, I did interview with Google for their Associate Product Manager position).

2) The work you’ve done *does* matter. The key observation is this: If you’re applying to be a developer, you’re applying to be maker. Your first cousins are designers, painters, filmmakers and other makers of things, not mathematicians. So like others in your family, you need to have a portfolio - evidence of what can and have created. It shows you can hack it.

Busy / Taking Responsibility

Note: I wrote this post a few weeks ago so the ‘this morning’ is a little outdated. I wanted to sit on it for awhile before publishing. In the meantime David posted another great and related post. Check it out.

I woke up this morning, dragged myself to class and discovered in my reader this post by David Hansson of 37signals. David tears apart the notion of “I just don’t have enough time (to do the things I want)”. It really hit home - I found myself agreeing with everything he wrote while simultaneously being its prototypical victim. This was, quite obviously, a conflict.

This excuse is particularly depressing when it comes from students. Oh, I have so many classes. Oh, I have so much home work. There’s simply no time to learn outside of school. Then you’re doing it wrong!
Never let your schooling interfere with your education, someone clever once said.

Zing. That someone clever was Mark Twain and, coincidentally enough, I happened across that quote just last night.

So I pondered this for a minute. Clearly I’m doing something. I’m busy. But I keep saying I don’t have enough time to do things I’d like, making myself the victim of my own scheduling. So what am I doing if it’s not what I want? And why? Maybe I don’t really know what I want? Maybe I need to be able to prioritize?

Take a look at this chart. I’m sure some of you have seen it before (I’ve come across it in the Stephen Covey books and the Randy Pausch time management lecture). What do you think would be the ideal distribution of your time between quadrants 1 to 4? 3:4:2:1? Now try to place your typical daily activities. it’s an interesting exercise, though admittedly broad.

the classic important/not pressing/not matrix

When someone pulls the “not enough time” card, it’s because their quadrants 1 & 3 have taken over.

Prioritizing isn’t easy. In fact it’s really, really hard. To do it effectively, you have to know yourself and where you’re going. This is tough. This takes time.

The crowding at the top of my priority list was a direct result of my unwillingness to commit to a path: Do I want to go to grad school (prioritize grades) or do I want to work in industry (prioritize internships and side projects)? Or do I want to start a company (prioritize planning and networking)? Ironically enough, I was too busy to know. So I did all three. Quadrant 1 became my only quadrant. And that made me miserable.

Both Pausch and Covey recommend focusing on quadrant 2, the one most likely to be neglected. I agree, so I’m shifting focus starting now. For me, blogging (or any kind of reflective writing) is quadrant 2. And prioritizing, that’s quadrant 2 as well.

All taken, David’s post was a welcomed reminder. It reminded me how often discontent stems from the unnoticed cavities in our personal agency - thoughts that our lives are at the will of fate, scheduling, other people, society, or anything other than ourselves. Your life is yours. Take responsibility, know that you are in control, fill these cavities. It is nothing less than stealing back your own happiness.

Free Software is obsolete

A few weeks ago I went to see a talk by Richard Stallman on “The Free Software movement in Ethics and Practice.” Stallman based his talk on what he calls the four freedoms of software. Here’s a summary (from Wikipedia).

  • Freedom 0: The freedom to run the program for any purpose.
  • Freedom 1: The freedom to study and modify the program.
  • Freedom 2: The freedom to copy the program so you can help your neighbor.
  • Freedom 3: The freedom to improve the program, and release your improvements to the public, so that the whole community benefits.

As the talk progressed, it became increasingly obvious how outdated the premises of his philosophy are. His ideas were formed in the early 1980s, they come from a world where software was run on a single computer, deriving its value from its own hardware and data. They come from a world without internet. The idea that control of source = control of program simply isn’t true anymore.

An Illustration: Facebook is not a bunch of code. It is a community built on top of a bunch of code. Facebook could be completely “free software” and it wouldn’t make a smidgen of difference to my freedoms. Perhaps I should clarify that I am arguing that the value of the program is the value of the software as you may want to use it - the value of the program ‘facebook’ is the value I currently receive by visiting www.facebook.com. Its source code just happens to be its only readily redistributable component. For the sake of argument, suppose I download the server software and get it running.

  • I would not have freedom 0: I could not run the program for any purpose. For example, I couldn’t run it to send my friends messages. Because I would have no friends.
  • I would not have freedom 1. I could study the program, but I couldn’t modify it. No amount of tinkering with my personal copy of the facebook server software will affect the official one. So I’ll still have to use the official one because all my friends are on it.
  • I would not have freedom 2. I could give my neighbour a copy of the software, but it would be equally useless to them.
  • I would not have freedom 3. I could improve my deployment but it wouldn’t help the facebook community at all.

The idea that a user should be able to modify any piece of their computing environment is based upon a fundamental oversight: Stallman doesn’t realize that the value created between devices now far surpasses the value inside any device. People generate value. That’s why nobody has tried to pirate Twitter.

As cloud computing increasingly becomes the norm, the principles of The Free Software Movement will continue to fade into anachronism.

This leads into a different discussion about business models for software, Open Source and pirating. I’ll leave that for another post.

One last clarification: I think Open Source is here to stay. Open Source is great. Like Stallman, I draw a clear distinction between Open Source and “free software” based on the principles of The Free Software Movement. The former is a development strategy. “Free software” is an obsolete philosophy.

Skip list backwards traversal

A recent assignment in my algorithms course asked us to design an efficient iterator for the backwards traversal of a skip list. Like any good student, the first thing I did was Google it. But I couldn’t find anything at all - no BB questions, definitely no answers (aside: I did come across this blog - a pretty good personal blog on programming by a vigorously self-studying Latvian - and this skip list article in particular.).
So to help future students I’m going to post my solution. This is meant only to stimulate thought and to share how I tackled it - by no means is it correct. I only handed it in yesterday, it hasn’t been marked yet. In fact, there is an absolutely crucial sub-part (the ‘truncated search’) that I didn’t have time to formally prove as being O(1), which is required (If you have a proof, please post it!). The whole thing hinges on this and it is only now ‘intuitively’ O(1).

A Short review of skip lists

A skip list is a data structure that offers, with a very high probability, performance similar to that of a balanced tree (O(lgn) Insert, Search, Delete) but is quite a bit easier to implement. It also offers certain advantages over trees, especially in applications such as parallel and network computer, since insertions and deletions can be done without having knowledge of the entire list.

The basic skip list is just an augmented singly-linked list. Certain nodes have i extra pointers, each pointing to the next node with at least i pointers (e.g. a Node with three pointers will point to the next node with 1, the next with at least 2 levels with pointer 2, and the next with at least 3 levels with pointer 3). This allows search (and therefore insert and delete) to skip over large chunks of the list by taking the fast-lane pointers whenever able.

Image:Skip list.svg
A simple skip list with 10 nodes, from Wikipedia

The basic skip list algorithms are (taken from my course notes, with permission):


SkipListSearch(S, x)

//
// Find the node containing element "x". Also build array
// "Pointer" with the node we were at when we went "down"
// from level lev.
//
Let N ← head(S)
lev ← maxLevel(S)
Do

//
// Go right as far as possible.
//
While (element(nextNode(N, lev)) < x) do
N ← nextNode(N, lev)
//
// Now try on the next level down.
//
Pointer[lev] ← N
lev ← lev - 1

While (lev > 0)

If element(nextNode(N, 1)) = x then

Return nextNode(N, 1)

Return null

End


SkipListDelete(S, x)

//
// Find where x is (assume it is in the skip list).
//
N ← SkipListSearch(S, x)
//
// Fix the pointers that used to point to N.
//
For lev ← level(N) downto to 1 do

nextNode(Pointer[lev], lev) ← nextNode(N, lev)

//
// Deal with possible decrease in value of MaxLevel.
//
While nextNode(head(S), maxLevel(S)) = null do

decrement maxLevel(S)

End

SkipListInsert(S, x)

// Find where x is (assume it is not in the skip list).
//
SkipListSearch(S, x)
// Create new node with Pr{level > i} = p^i
// Common value for p is 0.5
lev ← 1
While random(1, 1/p) = 1 and level < levelCap do
increment lev

N ← new node with element x and level lev
// Fix the pointers that will now point to N
//
For lev ← min(level(N), maxLevel(S)) downto to 1 do

nextNode(N, lev) ← nextNode(Pointer[lev], lev)
nextNode(Pointer[lev], lev) ← N

// Deal with possible increase in the number of levels.
//
While level(N) > maxLevel(S) do

increment maxLevel(S)
nextNode(N, maxLevel(S)) ← null
nextNode(head(S), maxLevel(S)) ← N

End

A backwards iterator

The problem is to design an efficient backwards iterator for a skip list. Specifically, it needs to have three operations:

  • init(), which initializes the skip list such that the next call to previous() returns the last element
  • hasPrevious(), which returns true iff the next call to previous() will return a valid node
  • previous() returns the next element in the skip list, moving backwards

The restriction is that n calls to previous (to traverse the whole list) runs in O(n).

Note this is almost trivial if traversing forwards - just follow all the pointers at the bottom level. Backwards is more tricky.

Oh ya and we also can’t have worse than Omega(n) space complexity, or restructure the skip list. This is too bad because there are two simple solutions that meet the O(n) restriction:

  1. Traverse the list forwards, pushing each element onto a stack. Pop off the stack.
  2. Add some lines to SkipListInsert so that the bottom level is a doubly instead of a singly linked list.

We are, however, allowed to alter the search algorithm.

An O(nlgn) solution

As a warm up, here’s an easy O(nlgn) solution: Go to the end of the list (go right as far as possible, go down. repeat until level = 0). The array Pointer (hereafter referred to as P[]) will then be pointing at the node containing the last element in P[1]. Then search for the last element. Now P[1] will point at the (node containing the) second last element. Doing this repetively, you will visit every node in reverse. That’s a cost of O(lg) for n elements, giving a total running time of O(nlg)

An O(n) solution

Look at the skip list above. Trace the search path for element 9, 8, and 7: the paths are almost identical. Note also that the pointer array, aside from P[1], will be populated with exactly the same elements in each search, precisely P[4]->1 P[3]->6 P[2]->6

What we need to do is eliminate the repeated steps. So we need to modify the search algorithm slightly so that we can specify from where in the list to start our search. Then, if we know the last common ‘down’ node in a path, we can just start our search from there. E.g. Once we’ve searched for 9, we could just search for 8 and 7 starting from node 6 since everything before it would be repeated. So let’s define

SkipListSearch2(S, start, x)
// Identical to SkipListSearch, but replace the very first line with

Let N ← start
//everything else the same as SkipListSearch

End

This is just a generalization; we could define:

SkipListSearch(S, x){ return SkipListSearch2(S, head(S), x); }

Ok, so once we have that all we have that we can write the full solution:


init()
// This is just a slight modification of the first search algorithm

lev ← maxLevel(S)
Do

//
// Go right as far as possible.
//
While (nextNode(N, lev) != nil) do
N ← nextNode(N, lev)
//
// Go down.
//
Pointer[lev] ← N
lev ← lev - 1

While (lev > 0)

End


hasPrevious()

return not P[1] = head // could be null, or a special head value

End

prevous()

// Save element to return. We're going to mess with the Pointer array
to_return ← element(P[1])
// Find most recent ‘down’ node (not including the current one at P[1])
i ← 2
while (i <= maxLevel(S) and P[i] = P[1]) do
i++
// If P[1] was height maxLevel, we’ll have to search from the beginning
if (i = maxLevel(S))
start ← head(S)
// otherwise, P[i] points at the closest common down node
// on the search paths for current and the element preceding it
else
start ← P[i]
// Now search for current element from start to repopulate the
// part of the Pointer array that differs
SkipListSearch2(S, start, to_return)
// Now P[1] points at the node before the one containing to_return
return to_return

End

Analysis

We need to show that a single call to previous() runs in O(1). There are two parts that might cast doubt on a claim of O(1) complexity - the while loop and the truncated search call.

Proof 1 - Claim: The expected running time of the while loop is a constant as n goes to infinity

The number of times the loop runs is proportional to the number of pointers in the current node. If P[1] is height maxHeight, the test P[i] = P[1] will always be true. But this is vary rare. Remember that the probability of a node having i pointers decreases geometrically with each level, or Pr{ level > i} = p^i. The expected height of a node is equal to the sum of all probability-weighted possible heights. Since the probabilities decrease geometrically and the heights increase linearly, we have an infinitely decreasing geometric series as we let height go to infinity. This, as we all know, is equal to a constant. more formally:

s \;=\; \sum_{k=0}^\infty ar^k = \frac{a}{1-r}

Proof 2 - Claim: The expected running time of SkipListSearch2(S, start, to_current) is constant

A similar analysis as was done for the loop can be applied here. The number of steps performed by SkipListSearch2 is proportional to the height of the P[1] node. If P[1] is short, start will be quite close and the number of steps small. As shown in proof 1, since the probability of a node having i pointers decreases geometrically as i goes to infinity, this leads to an infinitely decreasing geometric series.

An alternative proof would be to just use a corollarly given to us on the assignment handout: The expected number of level i nodes between two nodes that are next to each other at level i + 1 is a constant. We are only ever searching between two nodes of height i+1, and while the probability of height decreases geometrically, the extra steps only increase linearly - namely c * i, one constant weight per level. Again we have an infinitely decreasing geometric series.

Ok, I admit that proof 2 isn’t air tight. But it feels right. And if you actually visualize the algorithm, the search step usually only performs a very, very small number of steps - it is extremely rare that it performs close to the number of steps needed for a full search starting from the head and even this worse case is only O(lgn). It certainly appears to be expected O(1).

I guess I’ll find out when I get my assignment back :)

Next year’s halloween costume

I already have a costume for this year: I’m going as a dwarven ninja.
It’s an ode to my past (and extreme geekism, holy crap) - in rpg games (mostly Stick in the Mud - a text-based multiplayer rpg that I first logged into about 12 years ago Stick in the MUD Official Site) I’m almost always a dwarven (race) ninja (class, when available). Thought halloween would be a good opportunity to bring fantasy to reality. Plus it’s easy: Big beard, lots of black clothing, and some death yells.
But I came up with a much more clever costume  and I’m writing it up here so I can remember for next year: Napolean Complex. Get a Napolean costume and stick a complex molecule on your chest. Maybe this:

Image:Cellulose-3D-balls.png

As a bonus: I’m 5′8 and French (descent)!