Home

Advertisement

Customize
Zack
22 August 2009 @ 01:25 pm

Having been challenged with sorting a linked list, and trying to brush up on my C++ at the same time, I set out this morning to best the challenge. I decided I would use the STL list type so that I could avoid the boilerplate of writing a linked list class and also have a built-in sort() algorithm against which to test the vailidity and performance of my version.

So, the first question is which sorting algorithm to use? Quicksort is the canonical answer from arrays, because it requires constant auxillary space in which to operate (typically just one element). However, quicksort also relies upon constant time access to any element in the array in order to maintain its O(N log(N)) performance. So, since quicksort won't work, the other obvious solution from arrays is mergesort. When operating on arrays, mergesort requires O(N) additional space to perform the merging step. (Technically this isn't true, but without the additional space mergesort takes longer to run.)

The key fact about mergesort on linked lists is that it no longer requires O(N) additional space. In fact, it can be done with zero additional space! This is because of the advantage linked lists have over arrays in insertion speed. Arbitrary insertion in a linked list (assuming you already have a pointer to the location you're inserting to) is constant time, whereas arrays require O(N) time to shift back all the items following the insertion by one space. Now instead of interleaving two sorted arrays into a "merged" array, we can simply insert items from the second sorted list into the first sorted list where they belong.

Typically, mergesort works by splitting the list in half and then recursing. Once you get down to zero or one element lists, you can guarantee that you are sorted and can start going back up the call stack. I want to say something like "this is impractical for linked lists", but the reality is that you have to walk through the entire list log(N) times, one way or another. I wanted to write a version that didn't recurse, so I did my mergesort backwards. On the first pass, we simply sort pairs of items. After that, each pair is sorted, so we can merge pairs. Next step is to merge adjacent 4-element pairs, and so on and so forth. Another way of looking at it is that instead of splitting the list into 2 parts, then 4, then 8, and so on, I'm splitting it into N/2 parts, then N/4, then N/8, and so on. This lets me re-use the right boundary pointer from the previous merge as the left boundary pointer for the next merge.

My first pass was as follows:
#include <list>

using namespace std;

template <typename T>
void mySort(list<T> &l)
{
    int i, stepsize = 1, listsize = l.size();
    typename list<T>::iterator left, middle, right, end=l.end();    
    for (stepsize = 1; stepsize < listsize; stepsize <<= 1) {
        right = l.begin();
        while ( right != end ) {
            left = middle = right;
            for (i = 0; i < stepsize && middle != end; i++) middle++;
            right = middle;
            for (i = 0; i < stepsize && right != end; i++) right++;
            while ( left != middle && middle != right ) {
                while ( left != middle && *left <= *middle )
                    left++;
                while ( middle != right && *middle < *left ) {
                    l.insert(left, *middle);
                    l.erase(middle++);
                }
            }
        }
    }
}
A few notes about the above code:

I use a local variable to store a reference to l.end() for the entire function. This saves me about half a second on a 10,000 element list versus calling l.end() each time. I would do the same thing for l.begin(), but because I might change the first element of the list I can't. Luckily, I only need to call l.begin() log(N) times.

I would use the advance() function to replace the for loops stepping through the list, but advance doesn't check against l.end(), and (at least my version of) the STL list is circular, in that ++l.end() == l.begin()! So, I have to write my own.

Finally, I wasn't sure if l.erase(middle++) was going to work like I wanted. lists are much more flexible when it comes to keeping iterators valid through insertions and deletions, but the one thing my STL book was sure to point out was that pointers to deleted elements would become invalidated. An hour or so after I wrote this and saw that it did indeed work, I found that on the next page it pointed out this exact line in another example and said that it was ok, but the seemingly similar l.erase(middle); middle++ was not. The difference is that in the correct version, middle already points to the next element before the previous element is deleted.

So, I wrote some boilerplate testing code and compared the result of this function against list.sort and found that it worked just fine, but it took approximately 3 times as long to run. I loaded up my profiler and tried to see where all the time was going, but all I could see was that a lot of time was spent freeing memory. Taking a glance at how the STL had done it, I noticed they were making use of a function named splice(). I looked at it and realized that it did exactly what I had meant when I wrote the insert / erase pair. Once I replaced that line with:

l.splice(left, l, middle++);

My version was only 10% slower than the STL's version. Not half bad; in two hours I had come very close to the reference implementation. I spent some more time trying to understand the STL's version, and found some very cool tricks with pointers (and an array of 64 lists, which empty only take up 8 bytes apiece), but felt that my version was more immediately readable. Of course, I had just written mine, y'all can be the judge of readability.

Over breakfast I had the idea that instead of using a set stepsize I could simply increment the middle and right pointers as long as the previous element was less than it, effectively taking advantage of any natural sorting the list might have. Unfortunately, with all the additional variables and assignments (plus a need to move onto other things) I was unable to get it to perform better than my original implementation. As it stands here, it runs about 15% slower than the STL's sort, or about 2% slower than my original.

template <typename T>
void mySort2(list<T> &l)
{
    typename list<T>::iterator left, middle, right, end=l.end(), 
                               lastleft=end, temp;
    while ( lastleft != l.begin() ) {
        lastleft = right = l.begin();
        while ( right != end ) {
            left = temp = middle = right;
            while ( ++middle != end && *middle >= *temp) { temp = middle; }
            if ( middle != end ) {
                temp = right = middle;
                while ( ++right != end && *right >= *temp) { temp = right; }
                lastleft = left;
            } else if ( left == lastleft ) {
                break;
            } else { // middle = end, so merge [lastleft, left) w/ [left, end)
                right = end;
                middle = left;
                left = lastleft;
            }
            while ( left != middle && middle != right) {
                while ( left != middle && *left <= *middle )
                    left ++;
                while ( middle != right && *middle < *left )
                    l.splice(left, l, middle++);
            }
        }
    }
}

As a final note, replacing the merging section with temporary lists and calls to list.splice and list.merge doesn't seem to improve performance by an appreciable degree. What they would offer is an easier way to encapsulate a variety of comparison operators instead of just using less than. Oh well, enough duplication of library code for now.

 
 
Zack
04 August 2009 @ 07:52 am
 Fell off my bike again yesterday.  No broken bones this time, but my knees aren't going to be thanking me for anything anytime soon.

Also had a horrible nightmare last night that someone had gotten root access on my machines and was making my life very difficult.  Combined with heal-thyself sleep, there were more than a few times I woke up and had a hard time convincing myself it hadn't actually happened.

Also, I'm buying a house this month.  That's pretty cool.
 
 
Zack
05 June 2009 @ 05:40 pm
Cynthia and I want to go hiking this weekend.  I've been working like a maniac all week, so I thought she'd get it all figured out, but I keep forgetting that she just moved here, and I've got about 8 years of experience on her.  So we're going to go hike Mount Pilchuck tomorrow.  Why there?  Because I've never been and I've heard great things about it, but mostly because I want to understand how Adam got lost there my sophomore year :-p

If you want to come, let us know!  According to google maps (here) it's a little less than 2 hours away.  Add in ~4 hours for the hike (detailed here) and we're looking at 8 hour round trip.  I'm doing all this math to justify leaving at 10am.  We've got room for two more (three at the outside) in our car, but we can take more if additional car owners want to come.  The more the merrier, we say.

Since I like bulleted lists, here's the information again in a readable format:
  • Where: Mt. Pilchuck (right next to Mt. Baker)
  • When: Saturday 6/6/09 leaving 10am from Seattle (start hiking a little before noon)
  • Who: Anyone who wants to come
  • Contact: Me via comment, phone, email, whatever
That's all for now!  Recap to follow.
 
 
Zack
25 May 2009 @ 11:53 am
So yes, I broke my arm.  However, it is not as dire as it could be; in fact I am almost fully functional.  Here's what happened and what's up:

Got back from our honeymoon Friday night (thanks again to Mr. Hacking who gave us a ride from SeaTac to our door).  Saturday morning woke up early because of jet lag, decided to get started on our promise to start biking with each other in the mornings.  After getting tires inflated and everything set, we set off to ride the burke (a nearby bike trail).  We weren't even off the first block when I found myself much too close behind cynthia and slammed on my brakes.  Apparently I slammed too hard because I went over the handlebars.  Left arm felt pretty bad, and I was again glad to be wearing a helmet.  Nothing felt too bad, so we hiked back to the apartment and put some ice on the arm.  Ended up sitting around at home for three hours reasoning that it was just a bad strain.  Eventually tried to get up to wash my hands, only to realize that I was in a lot more pain than I had bargained for.  We went to the hospital.

We were joined in the elevator to the emergency room by a nice woman who asked me a few questions about what I was there for.  Turned out she was the head nurse in the pit that day, so I was being diagnosed before I even signed in.  Three hours, a few x-rays, and some painkillers later, I was diagnosed as having a broken radius (right through the neck near the elbow), and sentenced to a week in a sling, with more x-rays to follow.  Oh, and a small prescription for painkillers

So, how am I now?  Doing alright.  I'm arguably able to take care of myself again, but am not much use around the house, sadly.  also, my right wrist is starting to complain about the one handed typing, so I'm gonna try to keep that to a minimum.  Of course, I'm back to work tomorrow, so we'll see how long that lasts...
 
 
Current Mood: okay
 
 
Zack
16 May 2009 @ 08:23 am
So I've been working on some toy code, and after a few passes I decide...

that pressing preview in the LJ editor deletes 95% of my post?!?  wtf fail.
 
 
Current Mood: disappointed
 
 
Zack
13 May 2009 @ 07:31 pm
Having been soundly reprimanded for even considering sharing the fun, I will simply note that there have been numerous small annoyances with the accommodations.  One of which required a carpenter.  NOT OUR FAULT.

Also dried avocado leaves make a very interesting spice.

And now back to whatever it was I was doing.  Ah yes, enjoying the sunset. 
Tags:
 
 
Current Mood: happy
 
 
Zack
12 May 2009 @ 11:35 am
Cynthia and I are now safely at our resort in Zihuatanejo, and the bay is absolutely gorgeous.  Getting here, however, was not without its fair share of difficulties.

We enjoyed another session of Dungeons and Dragons with the Hot Dice group (working title), and between our honeymoon and other member's other engagements it looks like it was our last for a month, I'm supposed to be sending another email about availability sometime this week.  Afterwards we prepped Ry for housesitting for us while we were gone, and then began packing.  Mike took us to the airport at about 10, and we were there in plenty of time for our flight, which was scheduled for an 11:45p takeoff.

And then our flight reservation didn't exist anymore.

It took a few minutes to figure out what was being communicated, but somehow we had ended up with flight reservations that looked like this:

Seattle to Houston
Depart: Sunday, May 10th 11:45pm PDT
Arrive: Monday, May 11th 5:50am CDT

Houston to Ixtapa / Zihuatanejo
Depart: Sunday, May 10th 11:30am CDT
Arrive: Sunday, May 10th 2:00pm CDT

Needless to say, this isn't what I had arranged when I purchased the tickets.  So I asked them to place us on the Monday flight to Zihuatanejo from Houston, and the friendly rep told us that Continental only flew from Houston to Ixtapa on Wednesday, Friday, and Sunday.

Crap.

A word of advice for situations like this: Don't Panic.  Almost unanimously situations like this are the airline's fault (as they would end up being in this case), and they will get you to where you belong as close to on-time as humanely possible as long as you apply calm, gentle pressure for them to do so.  In our case, it took an hour on the phone, bouncing back to my parents between each call for more numbers to dial since I didn't have the internet at my own fingertips.  The winning phone number ended up being Expedia, whom quickly connected me to a Continental rep (Continental put me on indefinite hold when I called them directly).  The rep started by saying I was SOL, but eventually discovered that Continental had originally scheduled the Houston to Ixtapa flight on Monday, and then had changed when the flight would be.  After that, we found ourselves the proud owners of an Alaska Air flight, scheduled to leave at 7am the next morning and get us there only two hours later than our original reservations.

The flight down here was nice and not overly long (it helped that we were able to sit in the exit row for both legs of the trip).  On the LAX to ZIH leg we sat across from another newlywed couple who has just been married two days prior in Long Beach, Zach and Lauren.  Customs was fairly straightforward (zapped in the head with an instant read thermometer), and immediately after exiting we met a representative from the resort who took us to a taxi which took us straight here (about a 15 minute drive).  The view is stunning, a 100 degree unobstructed view of almost the entire bay from about 100 feet above the water, and visible from almost anywhere in our suite.

More on the suite in the next post, it seems our trip is destined to be with quirks.  For now, I think I might go enjoy the swim-up bar.  With cute little psuedo-stools build into the floor of the pool.  and 2 for 1 Daquiries.
 
 
Current Mood: relaxed
 
 
Zack
28 April 2009 @ 09:57 am
Cynthia and I are going to learn the dance to "Thriller" tonight at our apartment, and we'd love for people to come by and learn it with us.  We'll be doing the dance at our reception on Saturday.  The fun starts at 8pm, call me if you need directions. 
 
 
Zack
12 April 2009 @ 11:47 am
When I purchased my Apple G5 Tower in 2004, I went all out.  Shiny options, a huge monitor (23", zomg), and a huge price tag to match.  I was willing to put forth so much money because of two things:  1.  I wanted to give Macs the same chance I would give PCs, and 2.  I promised myself that I would buy no other computer for 5 years.  Looking back, I ended up breaking that promise to buy a 12" G4 laptop on clearance (for class, I realized I type much faster than I write), but I believed that I was still staying true to the spirit of the promise, which was to not replace the G5.

The five year mark came and went, and though Apple had just released shiny new computers, I found myself bogged down by a simple question.  Did I really need a new computer?  Despite two Video Card failures (one under AppleCare in the first two months, and one after 4 and a half years), the machine was still running quite strong.  I experienced none of the problems which would have been inevitable with a PC (I have yet to reinstall the OS, actually), and I was still finding new and exciting ways to use the computer.

What was worse, I thought Cynthia was going to make me get rid of the G5 once I got a new computer.  Even if I was ready for an Intel-based Mac, I wasn't ready to get rid of the machine that had introduced me to Unix (and OS X!), that had seen me through the bulk of my CS education.  Plus, I can see uses for the machine even after I move on to a new day-to-day machine.  Luckily, this turned out to be a misunderstanding.  Cynthia didn't see why I needed two laptops, and had been talking about the 12" G4 I had purchased after the G5.

In expressing to my father the oddity of having waited so long for something only to decide I didn't want it, he mentioned that he, too, was surprised I hadn't bought a new machine.  I don't remember the whole contents of the conversation, but I walked away with a new understanding that sometimes, it isn't about what you need, but what you want.

But what did I want?  Telecommuting, it seemed that perhaps a laptop was a more useful option than a new desktop.  Plus, almost my entire office uses laptops as desktop machines (I was definitely in the minority with my two towers, one running XP, one running Ubuntu), so I knew that the gap between laptop and tower was closing.  But which laptop did I want?  Certainly at least 15", but even those were running for quite a penny, and I'm trying to pay for a wedding in the month.

The answer came a week ago early monday morning.  I decided to peruse the Apple store, and found the links for clearance and refurbished machines in the lower left corner.  The clearance options were fairly limited, but they did have a 17" MacBook Pro (I have since learned that MBP is the preferred spelling), of the previous generation, for a very reasonable price.  After a quick call to their phone reps to make sure it would come with 10.5 and iLife, the purchase was made, and arrived thursday evening.  I spent the evening upgrading various software components and tweaking a few obvious parameters (the dock does not need icons for every installed app on the machine).

In the morning, I ran boot camp and installed Windows 7 Beta on it's own partition.  It runs very nicely.  It also runs games very nicely.  In the afternoon I installed VMWare Fusion, to try running the two OSes side by side.  Visio ran acceptably, games did not.  So much for their advertisements.  Played through Braid on Saturday, a very enjoyable game, if a bit convoluted story-wise.

Notes on OS X software: Migration assistant failed spectacularly, but without damaging anything.  In copying feed urls over (for comics), I discovered using curl and wc that RSS 2.0 is, in general, a more concise feed format (in comparison to 1.0 and Atom).  iTerm is an excellent Terminal.app replacement, though it took a while to find that the xterm title controls the window title, and the xterm icon title controls the tab title.  Machine is still clean as a whistle, but then again, I've only installed 5 new applications.  We'll see how long that lasts.
 
 
Zack
01 March 2009 @ 09:59 am
So, the customer came back and said "we need user permissions." Thus far, our idea of permissions goes something like:

if self.id == "admin":
self.admin = 1
Now, we have this nice user database code, which nicely encapsulates the notion of users and checking logins, etc. It was this code that I opened to see what I could twiddle to hack in some permissions in the week before release. It looked pretty standard (for us, which means that we had to write our own file-locking library), and I was thinking this would be an easy hack until I realized something was amiss. The web interface lets you change your password, but there was no exposed method for modifying a user's password. So I looked at the web code and found this:

def edit(self, **kwargs):
usercfg = cfg(locations.userdb)
userdict = usercfg.getcfg()
# validation code here...
userdict[kwargs["username"]] = kwargs["password"]
usercfg.write()
usercfg.done()
Notice the lack of calls to, say, the userdb library (the cfg library is our file-locking library).  So instead of writing this code at the library level, we wrote it at the web level, using the same low-level libraries we would have used in the userdb library.  Fine, this isn't the first time I've seen this; it won't be the last.  But what is maddening is that even those places where we did have corresponding functionality in the library, we wrote it from scratch for the web!

So even if I do hack the base library, I'll basically have to redo all of the web stuff anyway, instead of just a few select parts.  I wish this didn't seem like just business as usual.

Tags:
 
 
Zack
07 January 2009 @ 09:37 am
Dear professional internet communication users,

When in a professional setting, please refrain from using the common web-ism "Thx" in conversation. Using "Thx" gives the impression that you think the other person's time is less important that your own. That certainly isn't polite, so save yourself the keystrokes and don't say anything if you can't squeeze out a real "thanks".

Thx,
- Zack
 
 
Zack
24 November 2008 @ 12:02 pm
Hey! Cynthia and I found an apartment in Seattle! It's at Wallingford, basically on Lake Union, 1/2 mile from gasworks park in the UW direction. We can see the Seattle skyline and most of space needle from main room! We're moving in two weeks! w00t!

(with thanks to [info]infryq for actually typing this)
 
 
Current Mood: ecstatic
 
 
Zack
06 September 2008 @ 05:10 pm
I put on NPR's recording of Nickel Creek to warm the empty office a bit, and started crying. I guess I hadn't really faced into the fact that they're gone yet, now every time I try to sing along I can't even finish a line.
 
 
Current Music: NPR's recording of Nickel Creek's Farewell Tour concert (Helena)
 
 
Zack
04 September 2008 @ 08:52 am
Couldn't sleep this morning. Woke up at 4am thinking about some code I had worked on a few months ago. Brain revved up, so I figured I was hosed for going back to bed. I found the code I was thinking about, but remembered that I hadn't designed it well for testability, and that I wanted to be able to verify every last line of code in that sucker. Too large of a task to take on that early in the morning.

So instead, I wanted to finish setting up the subversion server on Heimdallr. It took a few hours, but most of the steps were pretty simple. The most difficult part was reconciling the apparent differences between the Subversion Manual's instructions and the reality of how things were installed on my system after using aptitude. I'm going to give a technical rundown under the cut of the steps I took to make this happen.

Installing subversion and apache on debian etch )

Of course, I also configured my existing router to forward port 80 to this machine (eventually the tables will be turned!), so you can now behold the glory at http://heimdallr.dnsalias.com! It's not much to look at, but the subversion repositories are under the svn directory, check out my subversioned home directory (also a work in progress).

In other news, Cynthia just left for her consult with an Oral Surgeon. Looks like she might have two front teeth in time for the wedding!
 
 
Zack
03 September 2008 @ 07:54 pm
Heimdallr is up and running! Much of my plan has changed since I first conceived of it, but I'm fairly certain I can say that it is running the operating system I anticipate it will be for some time.

I eventually settled on Debian, after considering a number of other options. They were:

LFS - Too much work for a year-old Linux without SSH by default. I have better things to do than watch source compile, which I learned from...

Gentoo - The Linux Fit-PC actually comes pre-loaded with Gentoo Linux, and I seriously considered simply using this OS. However, they fubar-ed the install pretty bad (it was pretty clear they'd configured the portage system to use their own servers, which were horribly out of date). After my 15th error plus workaround trying to bring the system to a modern state, I was fed up. I still considered making a clean install over the Ubuntu partition, but their installation instructions were too opaque for me.

FreeBSD - I'm really sad that this wasn't the operating system for me. Not only would I have free tech support from my FreeBSD fanatic buddy Seth, but I'd have one of the most respected firewall / router OSes on the market. Unfortunately, all reports of others' attempts to install FreeBSD involved pulling the HDD and installing it using a separate machine, which I was not super excited about trying.

Slackware - haha, just kidding.

Very Bare-bones right now. Only things I've installed above a bare-minimum Debian install are subversion (to back up my home directory, among other things), irssi and dircproxy (I have dreams of lurking on IRC), less and screen (necessary), and ddclient (to support a dyndns.com subdomain address). There's still more to install (python, for example), and more to be done (learn how to setup a firewall / router), but progress is easy now (thanks aptitude).

A webserver should be coming soon too,at which point I will post the address I'm using until cynthia and I agree on a real hostname. I should probably use apache so subversion works over http, but I was excited about trying lighttpd... oh well, another time.
 
 
Zack
11 August 2008 @ 02:03 pm
Noted from yesterday. Gin and Bawls is not a good combination. The two flavors kinda cancel each other out, and you're left with what tastes like slightly sweet, slightly carbonated water. Of course, that water is alcoholic and caffeinated, but I was kinda sad to lose any semblance of flavor.
 
 
Zack
06 August 2008 @ 07:23 pm

I live near a mass transit hub, so I've learned to basically ignore any screaming going on outside my apartment (and there is a fair bit). But I just heard something that made me smile:

You don't understand.
It's the same thing.
It's the same thing.
It's the same thing.

It certainly is.

 
 
Zack
05 August 2008 @ 10:17 am
I bought myself a Fit-PC a week ago, and it shipped yesterday, so I'm continuing to get excited about what I might do with this little box. Key specs are: 5w power consumption (up to 7w when spinning up disks, but in general it is supposed to be much lower), 5" x 5" x 1.5" form factor, 500 MHz, 256 MB RAM, and two 100Mbps ethernet ports (sadly, not gigabit).

I'm thinking about installing this as a firewall / router box. I've been somewhat frustrated by the clumsy port-mapping interfaces in most hardware routers (go to 192.168.0.1 with a predictable password, map one port at a time), and having gained some proficiency in Linux over the past two years, I figure this is a good way to continue my education.

Since the box will act as a gatekeeper, and Cerberos is already a well spoken for name in the world of computer security (see Kerberos), I turned to Norse Mythology. Heimdallr gets to play gatekeeper between Midgard and Asgard, and has other nifty associations like being able to hear the grass grow.

There are other nice uses for a box that is always on. I can start my own website and host it either on Heimdallr or on my Mac, which will be handy for certain upcoming weddings, among other things. As an IRC Proxy, I might finally be able to understand the underworld that is IRC (unlikely). As a bittorrent client, I might finally be able to achieve that 1:1 ratio that is the pinnacle of honor amongst thieves. As an SVN server, so I can subversion my home directory and use consistent profiles wherever I go. Other ideas ...

Also, it seems like a good platform to try LFS on. I always wanted to compile my own libc.

I will try to catalogue my experiences with this machine, though that journal may end up hosted on the machine itself. I'll be sure to keep this journal posted of developments at least until then.
 
 
Zack
24 April 2008 @ 05:24 am
So, one of my new year's resolutions was to write 1000 lines of personal code per month. Thus far, I've written 72 (Woot WoW Mods), and that took a whole weekend. Since I don't have 13 weekends a month, I think I'm going to scale that back to 100 lines a month.

Just had a nice chat with Katie. Sounds like she's doing well. She pointed out (in a very friendly manner) that I'm still struggling with self-pity issues, which can frequently be traced to "holding onto something too tightly." I like to think I'm pretty relaxed, but she's probably right. Her camp sounds pretty good, but to be fair I think I missed 30% of what she said due to my phone's poor reception around the apartment (but hey, at least no dropped call!).

Also - yes, I'm up early.
 
 
Zack
12 March 2008 @ 11:09 am

From IM with my love this morning:

Zack Spencer: [...] I could have easily said "has qa", "keeps a regualr schedule", "has a spec"
Cynthia Turpin: what is qa?
Cynthia Turpin: is it like ka?
Zack Spencer: that may have been one of the most unintentionally profound things you have ever said

(For the uninitiated, "Ka" is the word used to mean destiny (especially in the inevitability sense) in Steven King's "Dark Tower" series)

 
 
 
 

Advertisement

Customize