<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;Ck4FQns9eSp7ImA9WhVUFUU.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411</id><updated>2012-05-21T02:15:13.561-04:00</updated><title>The Lowly Programmer</title><subtitle type="html">Musings in code</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://www.thelowlyprogrammer.com/" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>24</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/TheLowlyProgrammer" /><feedburner:info uri="thelowlyprogrammer" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;A04HQnk4fip7ImA9WhRUGEQ.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-5436567093424877225</id><published>2012-01-30T00:05:00.000-05:00</published><updated>2012-01-30T00:05:33.736-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-30T00:05:33.736-05:00</app:edited><title>On hosting static files</title><content type="html">&lt;p&gt;You may have noticed that I have a blog. Shocking, I know. It’s predominantly composed of text, as many are, but that’s not the &lt;em&gt;only&lt;/em&gt; content I like to share. Posts are more approachable and memorable if they contain images, &lt;a title="Marriage Sort, a visualization" href="http://www.thelowlyprogrammer.com/2012/01/marriage-sort-visualization.html"&gt;sorting visualizations&lt;/a&gt; require Javascript frameworks, etcetera. Unfortunately, all these support files need to be stored somewhere.&lt;/p&gt;&lt;p&gt;This blog is hosted on Blogger, a decision I’m very happy with. Blogger doesn’t do arbitrary file hosting (that I know of), but it &lt;em&gt;does&lt;/em&gt; support pushing images to &lt;a title="Picasa Web Albums" href="http://picasaweb.google.com"&gt;Picasa&lt;/a&gt;. So images are hosted by Picasa, another Google service, no sweat. Now, what about other files?&lt;/p&gt;&lt;p&gt;Hmm.&lt;/p&gt;&lt;p&gt;It turns out the answer to this question is a lot trickier than I would have hoped. I was hoping for another Google service, to keep my dependencies down, so I started with Sites.&lt;/p&gt;&lt;h2&gt;&lt;/h2&gt;&lt;h2&gt;&lt;/h2&gt;&lt;h2&gt;Google Sites &lt;/h2&gt;&lt;p&gt;“&lt;em&gt;Google Sites&lt;/em&gt; is a free and easy way to create and share webpages”, the tagline goes. And it is – we use it internally all the time. One quick form later, I have a site, and another couple clicks puts a single attachment on it. Great! Update the post to reference it, double-check in incognito mode, and my work here is done. Right?&lt;/p&gt;&lt;p&gt;Well…not quite. It turns out that Sites uses some interesting redirect magic to actually link you to your content. Redirect magic that, for reasons I don’t understand, doesn’t actually resolve when you’re using it to hotlink content. For exactly this reason I guess, I don’t know. Anyways, since I had visited this content &lt;em&gt;while on Sites&lt;/em&gt;, my browser had it cached, and even incognito mode could access it, but it wouldn’t resolve anywhere else. Which is a good reason to test things on more than one computer, I suppose.&lt;/p&gt;&lt;p&gt;Ok, not sites. App Engine?&lt;/p&gt;&lt;h2&gt;Google App Engine&lt;/h2&gt;&lt;p&gt;App Engine &lt;em&gt;does&lt;/em&gt; do static file hosting. Instructions are &lt;a title="Google App Engine - Using Static Files" href="http://code.google.com/appengine/docs/python/gettingstarted/staticfiles.html"&gt;here&lt;/a&gt; – you must simply download the SDK, create the app.yaml file appropriately, upload your new App (the files must belong to something, after all), etc. This looks doable, but certainly non-trivial. I also cannot figure out how this is going to be priced, nor the latency to expect from it. So let’s keep looking.&lt;/p&gt;&lt;p&gt;I’m running out of Google services (I considered and rejected a few more). Time to look further afield. I don’t know of any good, trustworthy solutions offhand, and a quick search isn’t taking me anywhere I really want to go, so lets look at Amazon AWS. They have a service for every occasion, right?&lt;/p&gt;&lt;h2&gt;Amazon AWS&lt;/h2&gt;&lt;p&gt;As it turns out, yes, yes they do. A quick look through the &lt;a title="Amazon Web Services - Products &amp;amp; Services" href="http://aws.amazon.com/products/"&gt;options&lt;/a&gt; (there are so many!) says that Amazon &lt;a title="Amazon Simple Storage Service" href="http://aws.amazon.com/s3/"&gt;Simple Storage Service&lt;/a&gt; (S3) will do nicely, optionally backed by &lt;a title="Amazon CloudFront" href="http://aws.amazon.com/cloudfront/"&gt;CloudFront&lt;/a&gt; if I ever really need to scale. A quick signup, upload of my files, and one new &lt;a title="Customizing Amazon S3 URLs with CNAME" href="http://imtips.co/customizing-amazon-s3-urls-with-cname.html"&gt;CNAME&lt;/a&gt;, and I’ve got my files living under &lt;em&gt;static.thelowlyprogrammer.com&lt;/em&gt;, backed by S3. Nice! Doubly so since the free usage tier should make this entirely free for the next year or so, and pennies after.&lt;/p&gt;&lt;p&gt;Finally, in researching this blog post, I found one more option. And it’s a Google service, which was my original goal. Let’s check it out!&lt;/p&gt;&lt;h2&gt;Google Cloud Storage&lt;/h2&gt;&lt;p&gt;New on the block, Google Cloud Storage appears to be a competitor to Amazon S3. Priced a tiny bit cheaper, the features seem roughly comparable. The documentation is a bit rough still, which partly explains why I didn’t figure this out earlier, but everything is there if you look hard enough. One significant distinction is that you do not choose where data is stored (unless you want to add restrictions), and it will get replicated wherever needed. Note that this &lt;em&gt;includes&lt;/em&gt; automatic edge caching for popular files, so this is pretty much S3 and CloudFront all rolled into one. Fancy! It supports the same &lt;a title="Google Cloud Storage - Request URIs" href="https://developers.google.com/storage/docs/reference-uris"&gt;CNAME aliases&lt;/a&gt;, so I’ve got this set up as a hot-spare for my S3 data. I’ll leave S3 as primary for now since I’ve got it all configured and tested happily, but it looks like I’d be well served either way.&lt;/p&gt;&lt;p&gt;Maybe in a future post I’ll do a head-to-head comparison of S3 and GCS, if I can figure out a fair way of measuring performance all over the globe. Until then, I’m happy to stick with either.&lt;/p&gt;&lt;p&gt;Mission accomplished – static files hosted. Time for bed.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-5436567093424877225?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/AMKJ4_TVurM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/5436567093424877225/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2012/01/on-hosting-static-files.html#comment-form" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/5436567093424877225?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/5436567093424877225?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/AMKJ4_TVurM/on-hosting-static-files.html" title="On hosting static files" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><thr:total>3</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2012/01/on-hosting-static-files.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck4ARXY4fSp7ImA9WhRUGEU.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-415822424499778135</id><published>2012-01-16T23:49:00.000-05:00</published><updated>2012-01-29T18:49:04.835-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-29T18:49:04.835-05:00</app:edited><title>Marriage Sort, a visualization</title><content type="html">&lt;i&gt;You'll need a modern browser that handles SVG (a recent Chrome, Firefox, or IE9 will do) to properly see this post.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
On Saturday, &lt;a href="http://bost.ocks.org/mike/"&gt;Mike Bostock&lt;/a&gt; posted a link on Hacker News to &lt;a href="http://bost.ocks.org/mike/shuffle/"&gt;an explanation of the Fisher-Yates Shuffle&lt;/a&gt; he had written. Alongside the explanation he included a very slick visualization where you could see it in action. Now, at a fundamental level, shuffle algorithms and sorting algorithms are simply the reverse of each other. So if this visualization does well on shuffle algorithms, why not a sorting algorithm as well?&lt;br /&gt;
&lt;br /&gt;
A couple years ago I wrote a sorting algorithm, which I gave the tongue-in-cheek name Marriage Sort (an homage to the problem that inspired it - see &lt;a href="http://www.thelowlyprogrammer.com/2010/04/introducing-marriage-sort.html"&gt;the introduction post&lt;/a&gt; for more details). I was looking for a project to play with on the weekend, so I thought, why not try the visualization on that? Mike has kindly let me re-use (butcher?) his code, so here's the same visualization of Marriage Sort.&lt;br /&gt;
&lt;div id="chart"&gt;&lt;/div&gt;&lt;style&gt;

.line {
  stroke: #999;
  stroke-width: 2px;
}

.play path {
  stroke: #fff;
  stroke-width: 6px;
}

.play:hover path {
  fill: red;
}

.play rect {
  fill: none;
  pointer-events: all;
  cursor: pointer;
}

#disclaimer {
  text-align: center;
  width: 548px;
  margin-left: 24px;
  margin-right: 24px;
}

&lt;/style&gt;&lt;br /&gt;
&lt;script src="http://static.thelowlyprogrammer.com/files/d3/2.7.3/d3.min.js"&gt;&lt;/script&gt;&lt;script&gt;

var margin = {top: 10, right: 24, bottom: 10, left: 24},
    width = 500,
    height = 60 - margin.top - margin.bottom,
    size = height * .8;

var n = 60;

var tick = 600,
    animate = 450;  // Should be a multiple of 3.

var COLOR_IDLE = "#999",
    COLOR_DONE = "#000",
    COLOR_ANIMATING = "#000",
    COLOR_KEY = "red",
    COLOR_PARTITION = "#900",
    COLOR_KEY_ANIMATING = "red";

var STROKE = "2px",
    STROKE_BOLD = "4px";

var x = d3.scale.ordinal()
    .domain(d3.range(n))
    .rangePoints([0, width]);

var y = d3.scale.linear()
    .domain([0, n - 1])
    .range([-Math.PI / 6, Math.PI / 6]);

// In place shuffle.
function shuffle(list) {
  for (rem = list.length; rem &gt; 0; --rem) {
    var next = Math.floor(Math.random() * rem)  // [0..rem)
    var tmp = list[next]
    list[next] = list[rem-1];
    list[rem-1] = tmp;
  }
  return list;
}

// Swap of two lines. Colors can optionally be specified for during the transition,
// however post-transition all lines are made black.
function swap(line, a, b, color_a, color_b) {
  if (color_a == undefined) {
    color_a = COLOR_ANIMATING;
  }
  if (color_b == undefined) {
    color_b = COLOR_ANIMATING;
  }
  if (a == b ) {
    d3.select(line[0][a])
        .style("stroke", COLOR_DONE)
        .style("stroke-width", STROKE);
    return;
  } else if (a &gt; b) {
    // Swap indices for simplicity.
    var t = b;
    b = a;
    a = t;
    t = color_b;
    color_b = color_a;
    color_a = t;
  }
  var b_out = line[0].splice(b, 1)[0],
      a_out = line[0].splice(a, 1)[0];
  line[0].splice(a, 0, b_out);
  line[0].splice(b, 0, a_out);

  d3.select(a_out)
      .style("stroke", color_a)
      .style("stroke-width", STROKE_BOLD)
    .transition()
      .duration(animate)
      .attr("transform", "translate(" + x(b) + ", " + height + ")");
  setTimeout(function() {
    d3.select(a_out)
        .style("stroke", COLOR_DONE)
        .style("stroke-width", STROKE);
  }, animate);

  d3.select(b_out)
      .style("stroke", color_b)
      .style("stroke-width", STROKE_BOLD)
    .transition()
      .duration(animate)
      .attr("transform", "translate(" + x(a) + ", " + height + ")");
  setTimeout(function() {
    d3.select(b_out)
        .style("stroke", COLOR_DONE)
        .style("stroke-width", STROKE);
  }, animate);
}

// Remove line at 'src' and place it at 'dst', moving over all the intermediate
//  lines in the process. src will end up immediately to the left of the value
//  currently in 'dst'.
function insert(line, src, dst) {
  if (dst - src == 2) {
    swap(line, src, src+1);
    return;
  } else if (src - dst == 1) {
    swap(line, src, dst);
    return;
  }

  var out = line[0].splice(src, 1)[0];
  if (src &gt; dst) {
    line[0].splice(dst, 0, out);
  } else {
    line[0].splice(dst-1, 0, out);
  }

  // Animate primary line:
  d3.select(out)
      .style("stroke", COLOR_ANIMATING)
      .style("stroke-width", STROKE_BOLD)
    .transition()
      .duration(animate)
      .attr("transform", "translate(" + x(src &gt; dst ? dst : dst-1) + ", " + height + ")");
  setTimeout(function() {
    d3.select(out)
        .style("stroke", COLOR_DONE)
        .style("stroke-width", STROKE);
  }, animate);

  // Animate intermediate lines:
  var min, max, dir;
  if (src &gt; dst) {
    min = dst + 1;
    max = src;
    dir = 1;
  } else {
    min = src;
    max = dst - 1;
    dir = -1;
  }
  var count = max - min + 1;
  var mid = (count - 1) / 2;
  line.filter(function(d, i) { return i &gt;= min &amp;&amp; i &lt;= max; })
    .transition()
      .duration(animate/3)
      .delay(function(d, i) { return animate/3 + animate/6 * (i - mid) / mid * -dir; })
      .attr("transform", function(d, i) { return "translate(" + x(min+i) + ", " + height + ")"; });
}

function chart(stage1, stage2) {
  var m = n;

  // Combat disqus script duplication.
  if (d3.selectAll("svg#chart1")[0].length != 0) {
    return;
  }

  var svg = d3.select("div#chart").append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .attr("id", "chart1")
      .style("margin-left", margin.left + "px")
      .style("margin-right", margin.right + "px")
    .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  var line = svg.selectAll(".line")
      .data(d3.range(n))
    .enter().append("line")
      .attr("class", "line")
      .attr("x2", function(d) { return size * Math.sin(y(d)); })
      .attr("y2", function(d) { return -size * Math.cos(y(d)); })
      .attr("transform", function(d, i) { return "translate(" + x(i) + "," + height + ")"; });

  var play = svg.append("g")
      .attr("class", "play")
      .on("click", start);

  play.append("path")
      .attr("d", "M-30,-30L30,0L-30,30Z")
      .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(.6)");

  play.append("rect")
      .attr("width", width)
      .attr("height", height);

  function start() {
    play.style("display", "none");

    line = svg.selectAll(".line")
        .data(shuffle(d3.range(n)))
        .attr("x2", function(d) { return size * Math.sin(y(d)); })
        .attr("y2", function(d) { return -size * Math.cos(y(d)); })
        .attr("transform", function(d, i) { return "translate(" + x(i) + "," + height + ")"; })
        .style("stroke", COLOR_IDLE);

    var pause = 0,
        pass = 1,
        stage1Data = new Object();
    var interval = setInterval(function() {
      if (pause &gt; 0) {
        pause--;
        return;
      }
      switch(pass) {
        case 1:
          if (!(m = stage1(line, stage1Data, m))) {
            m = n;
            pass = 2;
            pause = 2;
            // Wait for all trailing animations before running this.
            setTimeout(function() {
              line.transition()
                  .duration(animate*1/3)
                  .delay(function(d, i) { return (animate*2/3)/n * i })
                  .style("stroke", COLOR_IDLE)
                  .style("stroke-width", STROKE);
            }, tick);
          }
          break;
        case 2:
          if (!(m = stage2(line, m))) {
            m = n;
            pass = 3;
          }
          break;
        case 3:
          clearInterval(interval);
          setTimeout(function() {
            play.style("display", null);
            line = svg.selectAll(".line")
                .data(d3.range(n))
                .attr("x2", function(d) { return size * Math.sin(y(d)); })
                .attr("y2", function(d) { return -size * Math.cos(y(d)); })
                .attr("transform", function(d, i) { return "translate(" + x(i) + "," + height + ")"; })
                .style("stroke", COLOR_IDLE);
          }, 5000);
          break;
      }
    }, tick);
  }

  return svg;
}

function stage1(line, data, m) {
  if (data.prefix == undefined) {
    // Initialize data container.
    data.prefix = null;
    data.on = 0;
    data.key = null;
  }
  if (data.key == null) {
    // Start of a new pass.
    data.prefix = ~~Math.sqrt(m-1);
    if (data.prefix &lt;= 0) {
      // stage is done.
      return 0;
    }

    // Find maximum bar from prefix, to use as the key.
    data.key = 0;
    for (var i = 1; i &lt; data.prefix; ++i) {
      if (line[0][i].__data__ &gt; line[0][data.key].__data__) {
        data.key = i;
      }
    }

    // Re-color the active region for this pass.
    line.select(function(d, i) { return i &lt; m ? this : null })
      .style("stroke", function(d, i) { return i &lt; data.prefix ? (i == data.key ? COLOR_KEY : COLOR_PARTITION) : COLOR_IDLE; })
      .style("stroke-width", function(d, i) { return i == data.key ? STROKE_BOLD : STROKE; });

    // Next entry, start scanning immediately after the prefix.
    data.on = data.prefix;
    return m;
  }

  // Mid pass: start by checking for elements at end to skip.
  while (m &gt; data.prefix &amp;&amp; line[0][m-1].__data__ &gt;= line[0][data.key].__data__) {
    d3.select(line[0][m-1])
        .style("stroke", COLOR_DONE);
    m--;
  }

  // Next, find one element from the front to swap forward.
  while (data.on &lt; m - 1 &amp;&amp; line[0][data.on].__data__ &lt; line[0][data.key].__data__) {
    data.on++;
  }
  if (data.on &gt;= m - 1) {
    // None left - pass is done.
    swap(line, data.key, m-1, COLOR_KEY_ANIMATING);
    data.key = null;
    return m-1;
  }
  swap(line, data.on, m-1);
  return m-1;
}

function stage2(line, m) {
  // Insertion sort, from right to left.
  if (m == n) {
    d3.select(line[0][m-1])
        .style("stroke", COLOR_DONE);
    return m-1;
  }
  while (m &gt; 0 &amp;&amp; line[0][m-1].__data__ &lt; line[0][m].__data__) {
    d3.select(line[0][m-1])
        .style("stroke", COLOR_DONE);
    m--;
  }
  if (m == 0) {
    return 0;
  }
  
  // m-1 needs to be slid forward. Find the destination:
  var dst = m;
  for (; dst &lt; line[0].length; dst++) {
    if (line[0][m-1].__data__ &lt; line[0][dst].__data__) {
      break;
    }
  }
  insert(line, m-1, dst);
  return m-1;
}

chart(stage1, stage2);

&lt;/script&gt;
&lt;p id="disclaimer"&gt;



&lt;small&gt;&lt;i&gt;Disclaimer: The visualization I'm building off looks great across platforms, so if there is anything wrong with this one, it's from my changes. Go see the original if you don't believe me.&lt;/i&gt;&lt;/small&gt;&lt;/p&gt;For those that don't remember the algorithm, here's a synopsis. There are two stages:
&lt;ul&gt;
&lt;li&gt;Stage 1:&lt;ol&gt;
&lt;li&gt;Take a prefix of size &amp;radic;m, where m is the size of the working set.&lt;/li&gt;
&lt;li&gt;Pick the maximum (in this case, most-right-leaning) of the prefix, and take it as the pivot.&lt;/li&gt;
&lt;li&gt;Walk through the list, pulling out anything greater than the pivot and moving it to the end of the working set.&lt;/li&gt;
&lt;li&gt;Move the pivot to the end of the working set.&lt;/li&gt;
&lt;li&gt;Go back to step 1 and repeat.
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;Stage 2: a bog-standard insertion sort.&lt;/li&gt;
&lt;/ul&gt;As the passes proceed the pivots decrease in size and more values are matched, allowing the array to be put into a "mostly sorted" state. On average, every element is within &amp;radic;n spots of the correct position, which the insertion sort then corrects. Overall complexity O(n&lt;sup&gt;1.5&lt;/sup&gt;) time, O(1) extra space.


Code for the visualization is at &lt;a href="https://gist.github.com/1628602"&gt;https://gist.github.com/1628602&lt;/a&gt; for easy perusing.&lt;br /&gt;
&lt;br /&gt;
Looks good to me! What do you think?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-415822424499778135?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/gFwciMUdfRA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/415822424499778135/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2012/01/marriage-sort-visualization.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/415822424499778135?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/415822424499778135?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/gFwciMUdfRA/marriage-sort-visualization.html" title="Marriage Sort, a visualization" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2012/01/marriage-sort-visualization.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ak8FQH06eCp7ImA9WhRQFk4.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-39495923321889634</id><published>2011-05-10T23:53:00.001-04:00</published><updated>2011-12-11T16:26:51.310-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-11T16:26:51.310-05:00</app:edited><title>The Game of Life, part 2: HashLife</title><content type="html">&lt;p&gt;&lt;a href="http://www.thelowlyprogrammer.com/2011/03/game-of-life-part-1.html"&gt;&lt;em&gt;Last time I wrote&lt;/em&gt;&lt;/a&gt;&lt;em&gt; I gave a brief introduction to the Game of Life and a very simple Python implementation for visualizing it. I will freely admit that was a teaser post; this post gets into the real meat of the topic with an overview of the &lt;/em&gt;&lt;a href="http://en.wikipedia.org/wiki/Hashlife"&gt;&lt;em&gt;HashLife&lt;/em&gt;&lt;/a&gt;&lt;em&gt; algorithm and a much more interesting implementation.&lt;/em&gt;&lt;em&gt;&lt;a href="http://lh6.ggpht.com/-w5f1Tmq22Sg/TuUe-p82A5I/AAAAAAAAEwI/DMrwntN7KgU/s1600-h/life%252520570%25255B4%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="life 570" border="0" alt="life 570" src="http://lh5.ggpht.com/-LYMdYV890tg/TuUe-w3sYNI/AAAAAAAAEwQ/FRzvI4gzAAg/life%252520570_thumb%25255B2%25255D.png?imgmax=800" width="590" height="342" /&gt;&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;h2&gt;Introduction&lt;/h2&gt;&lt;p&gt;This entry has taken me an embarrassingly long time to post. As is my habit, I wrote the code and 90% of the post, and then left it for months and months. Whoops! &lt;/p&gt;&lt;p&gt;If you haven’t played with a Game of Life viewer before they are legitimately fun to toy around with - I encourage you to check this one out (code is &lt;a href="https://github.com/EricBurnett/GameOfLife/tree/v2"&gt;here&lt;/a&gt;). Since the last version everything is much improved. The viewer supports a larger set of controls (see the &lt;a href="https://github.com/EricBurnett/GameOfLife/blob/v2/README"&gt;README&lt;/a&gt; for details) and basic file reading is implemented so it’s possible to try new starting patterns on the fly. And, as promised, I’ve implemented the HashLife algorithm to massively speed up iterations, so enormous patterns billions of generations forward are easily within your reach.&lt;/p&gt;&lt;h2&gt;Algorithm&lt;/h2&gt;&lt;p&gt;HashLife is a simple yet interesting algorithm. Invented in 1984 by Bill Gosper (of &lt;em&gt;Gosper glider gun&lt;/em&gt; fame), it exploits repeated patterns to dramatically cut down the work required to support large patterns over vast numbers of iterations. Between the &lt;a href="http://en.wikipedia.org/wiki/Hashlife"&gt;Wikipedia page&lt;/a&gt; and the enigmatically named “&lt;a href="http://drdobbs.com/high-performance-computing/184406478"&gt;An Algorithm for Compressing Space and Time&lt;/a&gt;” in Dr. Dobb’s Journal I think it’s decently well explained, but it took me a couple read-throughs to really wrap my head around so I’m going to try to give an overview of the key insights it utilizes. &lt;/p&gt;&lt;p align="center"&gt;&lt;a href="http://lh4.ggpht.com/_4uUxYbT6E0Q/TcoIJZjoFXI/AAAAAAAAEO4/UHdzGf6nr4w/s1600-h/quadtree5.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: right; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="quadtree" border="0" alt="quadtree" align="right" src="http://lh6.ggpht.com/_4uUxYbT6E0Q/TcoIJ-UEkQI/AAAAAAAAEO8/f_09uSaJ3Kg/quadtree_thumb3.png?imgmax=800" width="240" height="240" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;At it’s heart, HashLife is built around the concept of a quadtree. If you’re unfamiliar with it, a quadtree takes a square region and breaks it into four quadrants, each a quarter the size of the original. Each quadrant is further broken down into quadrants of its own, and on down. At the bottom, in squares of some minimum size like 2x2, actual points are stored. This structure is usually used to make spatial queries like “what points intersect this bounding box” efficient, but in this case two other properties are taken advantage of. First, nodes at any level are uniquely defined by the points within their region, which means duplicated regions can be backed by the same node in memory. For the Game of Life, where there are repeated patterns and empty regions galore, this can drastically reduce the space required. Second, in the Game of Life a square of&amp;#160; (n)x(n) points fully dictates the inner (n-2)x(n-2) core one generation forward, the inner (n/2)x(n/2) core n/4 generations forward, irrespective of what cells are adjacent to it. So the future core of a node can be calculated once and will apply at any future point in time, anywhere in the tree. &lt;/p&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/-_AEnxrB-FWQ/TuUe_QFwbwI/AAAAAAAAEwY/PZJcyOwQaMA/s1600-h/Inner%252520nodes%25255B6%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 10px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Inner nodes" border="0" alt="Inner nodes" src="http://lh4.ggpht.com/-owLYsNy9RSc/TuUfBQl50EI/AAAAAAAAEwg/YOren56OfIc/Inner%252520nodes_thumb%25255B4%25255D.png?imgmax=800" width="404" height="111" /&gt;&lt;/a&gt;Together these properties allow for ridiculous speedups. Hashing and sharing nodes drastically reduces the space requirements, with exponentially more sharing the further down the tree you go. There are only 16 possible leaf nodes, after all! From this, calculating the future core for a node requires exponentially less time than a naïve implementation would. It can be done by recursively calculating the inner core of smaller nodes, where the better caching comes into play, and then combining them together into a new node. You might be wondering if the gains from caching are lost to the increasing difficulty of determining which nodes are equal, but with a couple careful invariants we actually get that for free. First, nodes must be immutable - this one’s pretty straightforward. Second, nodes must be unique at all times. This forces us to build the tree from the bottom up, but then checking if a new node duplicates an existing one is simply a matter of checking if there are any existing nodes that point to the same set of quadrants in the same order, a problem that hash tables trivially solve. &lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:f9a9f18d-25ee-40e2-a10b-2ed4abf31dc8" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: python;"&gt;def __hash__(self):
# Hash is dependent on cells only, not e.g. _next.
# Required for Canonical(), so cannot be simply the id of the current
# object (which would otherwise work).
return hash((id(self._nw), id(self._ne), id(self._sw), id(self._se)))

def __eq__(self, other):
"""Are two nodes equal? Doesn't take caching _next into account."""
if id(self) == id(other):
return True
return (id(self._nw) == id(other._nw) and
id(self._ne) == id(other._ne) and
id(self._sw) == id(other._sw) and
id(self._se) == id(other._se))&lt;/pre&gt;&lt;/div&gt;&lt;h2&gt;Implementation&lt;/h2&gt;&lt;p&gt;As before, the code I’ve written is for Python 2.6 and makes use of PyGame, although neither dependency is terribly sticky. The code lives in a &lt;a href="https://github.com/EricBurnett/GameOfLife/tree/v2"&gt;repository on github&lt;/a&gt;, and I welcome any contributions you care to make. As the code here is complicated enough to be almost guaranteed a bug or two, there is a basic set of unit tests in life_test.py and the code itself is liberally sprinkled with asserts. Incidentally, removing the asserts nets a 20% performance gain (as measured by the time it takes to run the ‘PerformanceTest’ unit test), although I find the development time saved by having them is easily worth keeping them in forever. As noted later, the performance of the implementation isn’t all that important anyways. Which is a good thing, since I coded it in Python! &lt;/p&gt;&lt;p&gt;A comment on rewrites: during the transition from version 1 - a simple brute force algorithm - to version 2 - the Node class that implements HashLife - I had both algorithms implemented in parallel for a while. This let me have every second frame rendered by the old algorithm so I could ensure that at different times and different render speeds that the algorithms were coming up with the same results. I’ve seen this pattern used at work for migrating to replacement systems and it’s very much worth the extra glue code you have to write or the confidence it gives. John Carmack recently &lt;a href="http://altdevblogaday.com/2011/11/22/parallel-implementations/"&gt;wrote about parallel implementations&lt;/a&gt; on his own blog, if you want to hear more on the topic.&lt;/p&gt;&lt;h2&gt;Performance&lt;/h2&gt;&lt;p&gt;The performance is hard to objectively detail for an algorithm like this. For example, it takes ~1 second to generate the billionth generation of the &lt;a href="http://www.conwaylife.com/wiki/index.php?title=Backrake_3"&gt;backrake 3&lt;/a&gt; pattern, which has around 300,000,000 live cells; it takes ~2 seconds to generate the quintillionth generation with 3x10^17 live cells. But this is a perfect patterns to showcase HashLife - a simple spaceship traveling in a straight line, generating a steady stream of gliders. In comparison, a chaotic pattern like &lt;a href="http://www.conwaylife.com/wiki/index.php?title=Acorn"&gt;Acorn &lt;/a&gt;takes almost 25 seconds to generate just 5000 generations with at most 1057 alive at any time. As it stands the properties of the algorithm drastically outweigh the peculiarities of the implementation for anything I care to do. Although I must say, if you want to compare it to another implementation in an apples to apples comparison I’d love to hear the numbers you get.&lt;/p&gt;&lt;p&gt;As always, I’d love to hear what you think! &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-39495923321889634?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/jo9t5idHBSE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/39495923321889634/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2011/05/game-of-life-part-2-hashlife.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/39495923321889634?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/39495923321889634?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/jo9t5idHBSE/game-of-life-part-2-hashlife.html" title="The Game of Life, part 2: HashLife" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh5.ggpht.com/-LYMdYV890tg/TuUe-w3sYNI/AAAAAAAAEwQ/FRzvI4gzAAg/s72-c/life%252520570_thumb%25255B2%25255D.png?imgmax=800" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2011/05/game-of-life-part-2-hashlife.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkYBRnc9cCp7ImA9WhRVGUQ.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-936349929642113617</id><published>2011-03-12T11:03:00.002-05:00</published><updated>2012-01-19T13:35:57.968-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-19T13:35:57.968-05:00</app:edited><title>The Game of Life, part 1</title><content type="html">&lt;p&gt;&lt;i&gt;Update: See &lt;a href="http://www.thelowlyprogrammer.com/2011/05/game-of-life-part-2-hashlife.html"&gt;part 2&lt;/a&gt; for the implemented HashLife algorithm.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
The &lt;a title="Conway&amp;#39;s Game of Life - Wikipedia" href="http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life"&gt;Game of Life&lt;/a&gt; is a fascinating system. It was invented by &lt;a title="John Horton Conway - Wikipedia" href="http://en.wikipedia.org/wiki/John_Horton_Conway"&gt;John Conway&lt;/a&gt; in 1970 and has been studied continuously ever since. For those reading who haven’t heard of it before, a brief explanation: The world is an infinite grid of points, all either alive or dead. After each generation – or ‘iteration’ if you’d prefer – cells are updated according to the following three rules:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;If a cell is alive and it has two or three live neighbours, it stays alive. &lt;/li&gt;
&lt;li&gt;If a cell is dead and it has exactly three live neighbours, it becomes alive (tripartite reproduction?). &lt;/li&gt;
&lt;li&gt;Any other cell is dead. &lt;/li&gt;
&lt;/ol&gt;&lt;p align="center"&gt;&lt;table border="0" cellspacing="30" cellpadding="2" width="587"&gt;&lt;tbody&gt;
&lt;tr&gt;         &lt;td valign="top" width="210"&gt;&lt;a href="http://lh3.ggpht.com/_4uUxYbT6E0Q/TXuXkRdKDfI/AAAAAAAAEMM/IAhKgfgnQQc/s1600-h/Blinker%5B6%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Blinker" border="0" alt="Blinker" src="http://lh3.ggpht.com/_4uUxYbT6E0Q/TXuXkjv93BI/AAAAAAAAEMQ/LgEjUHUyIAM/Blinker_thumb%5B1%5D.png?imgmax=800" width="203" height="60" /&gt;&lt;/a&gt;&lt;/td&gt;          &lt;td valign="top" width="5"&gt;&lt;a href="http://lh4.ggpht.com/_4uUxYbT6E0Q/TXuXlL-k5ZI/AAAAAAAAEMU/FEsHV_He5tI/s1600-h/infinite%20growth%5B4%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="infinite growth" border="0" alt="infinite growth" src="http://lh5.ggpht.com/_4uUxYbT6E0Q/TXuXluF7oRI/AAAAAAAAEMY/Ko0COpp_JT0/infinite%20growth_thumb%5B2%5D.png?imgmax=800" width="60" height="60" /&gt;&lt;/a&gt;&lt;/td&gt;          &lt;td valign="top" width="210"&gt;&lt;a href="http://lh4.ggpht.com/_4uUxYbT6E0Q/TXuXmHVckoI/AAAAAAAAEMc/J3yTTvdAsuk/s1600-h/Glider%5B4%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Glider" border="0" alt="Glider" src="http://lh4.ggpht.com/_4uUxYbT6E0Q/TXuXmZWTgXI/AAAAAAAAEMg/1pqofvRsrTY/Glider_thumb%5B2%5D.png?imgmax=800" width="203" height="60" /&gt;&lt;/a&gt;&lt;/td&gt;       &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/p&gt;&lt;p&gt;From these simple rules amazing complexity can arise. Some configurations are stable, like the period two “blinker” [above left], or the period four “glider” [above right] that moves one row over and one row down with every cycle. Other configurations, like the one above centre, grow infinitely – this one spits out two gliders then lays down a zig-zag strip of blocks forever after. &lt;/p&gt;&lt;p&gt;There is more to the Game of Life than pretty patterns and curious growth, I must hasten to add. It has been studied by a host of people in a variety of fields and has gone on to start a new branch of mathematics (&lt;a title="Cellular automaton - Wikipedia" href="http://en.wikipedia.org/wiki/Cellular_automaton"&gt;cellular automata&lt;/a&gt;) and spur discussions on whether a &lt;a title="Emergence - Wikipedia" href="http://en.wikipedia.org/wiki/Emergence"&gt;sufficiently complicated pattern&lt;/a&gt; could be considered intelligent. It has also been proven to be &lt;a title="Life Universal Computer" href="http://www.igblan.free-online.co.uk/igblan/ca/"&gt;Turing complete&lt;/a&gt;, so any computation your computer can run can be run by simulating the Game of Life with the correct starting state.&lt;/p&gt;&lt;p&gt;I have implemented a basic python program for simulating the Game of Life &lt;a title="EricBurnett/GameOfLife - GitHub" href="https://github.com/EricBurnett/GameOfLife/tree/v1"&gt;on GitHub&lt;/a&gt;. It allows for infinite patterns, grows the field of view automatically, and allows speed to be controlled with Up/Down, but otherwise is a very simple implementation. The goal here is to eventually implement some of the more interesting algorithms for speeding up the simulation.&amp;#160; There are numerous such algorithms, although the one I find the most interesting is called &lt;a title="An Algorithm for Compressing Space and Time" href="http://www.drdobbs.com/high-performance-computing/184406478"&gt;Hashlife&lt;/a&gt; and exploits repeated patterns through space and time to achieve an exponential speedup in running the simulation. More details in part 2, whenever I write it :).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-936349929642113617?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/7lYTB6bvCP4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/936349929642113617/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2011/03/game-of-life-part-1.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/936349929642113617?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/936349929642113617?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/7lYTB6bvCP4/game-of-life-part-1.html" title="The Game of Life, part 1" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/_4uUxYbT6E0Q/TXuXkjv93BI/AAAAAAAAEMQ/LgEjUHUyIAM/s72-c/Blinker_thumb%5B1%5D.png?imgmax=800" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2011/03/game-of-life-part-1.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkQFRH4-eyp7ImA9WxFWGUo.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-7720975805796492880</id><published>2010-06-08T02:25:00.000-04:00</published><updated>2010-06-08T02:25:15.053-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-08T02:25:15.053-04:00</app:edited><title>New Job!</title><content type="html">Today I officially started at Google! Exciting stuff. I will be working on the DoubleClick Ad Exchange doing something-or-other for the time being — however as I am neither a senior engineer nor a corporate figurehead, I do not plan to write about my work. This should, however, explain my recent absence :).
&lt;br /&gt;&lt;br /&gt;
That is all.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-7720975805796492880?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/NXxrfMKWUeI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/7720975805796492880/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2010/06/new-job.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/7720975805796492880?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/7720975805796492880?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/NXxrfMKWUeI/new-job.html" title="New Job!" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2010/06/new-job.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUQEQXw9eSp7ImA9WxFRFEU.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-8001471963766584930</id><published>2010-04-28T14:23:00.014-04:00</published><updated>2010-04-28T16:41:40.261-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-28T16:41:40.261-04:00</app:edited><title>Robustly hot swapping binaries (with code)</title><content type="html">&lt;table border="1"&gt;&lt;tbody&gt;
&lt;tr&gt;       &lt;td style="background-color: black; color: rgb(200,200,200)"&gt;         &lt;pre&gt;Going down for swap
Coming up from swap!
This is generation 2 running 0.2 software (initial version 0.1)
Running state machine&lt;/pre&gt;

&lt;/td&gt;
&lt;/tr&gt;

&lt;/tbody&gt;&lt;/table&gt;

&lt;br/&gt;
&lt;p&gt;
&amp;#160; 
A while ago I remember reading an article by Nathan Wiegand on &lt;a href="http://nathanwiegand.com/wp/2010/02/hot-swapping-binaries/"&gt;hot swapping binaries&lt;/a&gt;. This was a very eye-opening article for me – before reading it, hot swapping was one of those black arts I never really thought about, and certainly wouldn’t have thought was at all &lt;em&gt;easy.&lt;/em&gt; I highly recommend you read it for yourself. Go ahead, I’ll wait. &lt;/p&gt;


&lt;p&gt;
There. Did you notice it? The elephant in the room? One thing the article doesn’t address is how to design programs to &lt;em&gt;use&lt;/em&gt; this fancy new ability, without being fragile and crashing and all those bad things. I’ve been mulling this over since reading it, and have settled on a basic design that I’ll present here. But don’t worry, this isn’t &lt;em&gt;just &lt;/em&gt;a thought design…I’ve actually coded it up and made sure it works as expected. Feel free to &lt;a href="#code"&gt;jump down&lt;/a&gt; and check it out before reading the design. Still, caveat emptor and all that.&lt;/p&gt;


&lt;h2&gt;
Design Goals&lt;/h2&gt;


&lt;p&gt;
For this design, I am focusing on three key goals:&lt;/p&gt;


&lt;ul&gt;
&lt;li&gt;Allow updating to any future version &lt;/li&gt;


&lt;li&gt;Allow updating to any &lt;em&gt;previous&lt;/em&gt; version &lt;/li&gt;


&lt;li&gt;Make it easy to be crash-free &lt;/li&gt;

&lt;/ul&gt;


&lt;p&gt;
Simple enough? Updating forward is a pretty obvious goal, as is crash-free code. I want to allow updating backwards as well for the simple expedient that I don’t expect all new code to be bug free, and so it might be desirable to roll back when a bug is introduced.&lt;/p&gt;


&lt;h2&gt;
&lt;/h2&gt;


&lt;h2&gt;
Design&lt;/h2&gt;


&lt;p&gt;
To achieve this, I’ve settled on a shortlist of constraints for the code:&lt;/p&gt;


&lt;ol&gt;
&lt;li&gt;Use protocol buffers to store all state &lt;/li&gt;


&lt;li&gt;Provide suitable defaults for everything, and be robust against weird state &lt;/li&gt;


&lt;li&gt;Structure the code as a state machine &lt;/li&gt;

&lt;/ol&gt;


&lt;p&gt;
I did say the list was &lt;em&gt;short&lt;/em&gt;. Let’s look at these in detail:&lt;/p&gt;


&lt;ol&gt;
&lt;li&gt;Protocol buffers are an obvious choice for persisting state, as they are forward and backward compatible by design. Care must be taken to not re-use tag numbers and to never add ‘required’ fields, but this is an easy requirement to satisfy. Now, using protocol buffers to store&lt;em&gt; everything&lt;/em&gt; does incur some overhead, but they are quite efficient by design and we really only need to store all state between state machine transitions so local variables are still quick. &lt;/li&gt;


&lt;li&gt;Hand in hand with 1), we cannot always expect to have the data we want in the format we want version to version. To accommodate this we must pick suitable defaults for fields, and if necessary be able to get new values at runtime. At the same time, if the meaning or format of a field is changing, it is probably better to use a new field. We will always try to handle weird data that may show up, but this shouldn’t be abused. &lt;/li&gt;


&lt;li&gt;Finally, structure the code as a state machine. This surfaces all state machine transitions as potential points for upgrading versions, and forces state to be in the protocol buffer when these transitions are crossed to ensure important data isn’t forgotten. And like everything else, the next state data can be stored in the protocol buffer. &lt;/li&gt;

&lt;/ol&gt;


&lt;p&gt;
There is one problem with 3), however. What happens when new states are added? Going forward is easy, but if we update to a previous version when we’re in one of these new states, it will have no idea where to start running again. We could try storing fallback states or something like that, but that seems too fragile. Instead, I would recommend not allowing updates to occur when transitioning to these new states. Then, a few versions down the line when you’re sure you won’t need to downgrade past where they were added, remove that restriction.&lt;/p&gt;


&lt;pre style="border-bottom: #000000 1px solid; border-left: #000000 1px solid; padding-bottom: 5px; background-color: #c0c0c0; min-height: 40px; padding-left: 5px; width: 590px; padding-right: 5px; overflow: auto; border-top: #000000 1px solid; border-right: #000000 1px solid; padding-top: 5px"&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;enum State {
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    // Special states supported by the program infrastructure.
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    STATE_NONE  = 0;
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    STATE_DONE  = 1;
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    STATE_ERROR = 2;
&lt;/pre&gt;
&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    // Program states. Unknown state transitions lead to ERROR and terminate the
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    // program, so should be avoided at all costs.
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    STATE_INIT          = 3;
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    STATE_PROCESS_LINE  = 4;
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    STATE_MUTATE_LINE   = 5;
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;  }
&lt;/pre&gt;
&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;  optional State prev_state = 2 [default = STATE_NONE];
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;  optional State cur_state  = 3 [default = STATE_ERROR];
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;&lt;/pre&gt;&lt;/pre&gt;


&lt;h2&gt;
What About Threads?&lt;/h2&gt;


&lt;p&gt;
You may have noticed that this design is inherently single-threaded. Threading can be added easily enough if the main thread owns all the work, and can easily and safely wait for or cancel all worker threads without losing anything. In that case, spin down the workers when you’re about to swap, and spin them up again when it completes. If your program doesn’t fit that description, however, this design may not be for you.&lt;/p&gt;


&lt;h2&gt;
Testing?&lt;/h2&gt;


&lt;p&gt;
Of course! I would recommend trying all transitions on a test instance first before upgrading the real process. You could also build in consistency checks that auto-revert if the state doesn’t meet expectations, regression tests for certain upgrade patterns, etc. This design is meant to make it easy to hot swap successfully, but it is no silver bullet.&lt;/p&gt;


&lt;h2&gt;
&lt;a name="code"&gt;&lt;/a&gt;Let's See the Code!&lt;/h2&gt;


&lt;p&gt;
As always, the code is up on &lt;a href="http://github.com/EricBurnett/hotswap"&gt;GitHub&lt;/a&gt; for you to peruse. It is broken into two demonstration applications, ‘v1’ and ‘v2’, that can be swapped between at will. While looping they respond to ‘u’ and ‘q’ (&lt;strong&gt;update&lt;/strong&gt; and &lt;strong&gt;quit&lt;/strong&gt;), although at times you may be prompted for other input. the makefiles build to the same target location, so build whichever one you want run next and press ‘u’ to swap to it.&lt;/p&gt;


&lt;p&gt;
The code is structured so you can use it as a framework to play with yourself easily enough. You should only need to write an init method, update the state machine and .proto file, and write the respective state methods to do the real work. The state machine and state methods will look something like this: 

&lt;/p&gt;


&lt;pre style="border-bottom: #000000 1px solid; border-left: #000000 1px solid; padding-bottom: 5px; background-color: #c0c0c0; min-height: 40px; padding-left: 5px; width: 590px; padding-right: 5px; overflow: auto; border-top: #000000 1px solid; border-right: #000000 1px solid; padding-top: 5px"&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;ReturnCode runStateMachine(ProgramState&amp;amp; state) {
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    cerr &amp;lt;&amp;lt; &amp;quot;&lt;span style="color: #8b0000"&gt;Running state machine\n&lt;/span&gt;&amp;quot;;
&lt;/pre&gt;
&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    &lt;span style="color: #008000"&gt;// Put stdin into non-blocking, raw mode, so we can watch for character&lt;/span&gt;
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    &lt;span style="color: #008000"&gt;// input one keypress at a time.&lt;/span&gt;
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    setStdinBlocking(&lt;span style="color: #0000ff"&gt;false&lt;/span&gt;);
&lt;/pre&gt;
&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    &lt;span style="color: #0000ff"&gt;while&lt;/span&gt; (&lt;span style="color: #0000ff"&gt;true&lt;/span&gt;) {
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;        ProgramState::State next;
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;        &lt;span style="color: #0000ff"&gt;switch&lt;/span&gt; (state.cur_state()) {
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;            &lt;span style="color: #0000ff"&gt;case&lt;/span&gt; ProgramState::STATE_INIT:
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;                next = runState_init(state);
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;                &lt;span style="color: #0000ff"&gt;break&lt;/span&gt;;
&lt;/pre&gt;
&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;            &lt;span style="color: #0000ff"&gt;case&lt;/span&gt; ProgramState::STATE_PROCESS_LINE:
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;                next = runState_process_line(state);
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;                &lt;span style="color: #0000ff"&gt;break&lt;/span&gt;;
&lt;/pre&gt;
&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;            &lt;span style="color: #0000ff"&gt;case&lt;/span&gt; ProgramState::STATE_DONE:
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;                setStdinBlocking(&lt;span style="color: #0000ff"&gt;true&lt;/span&gt;);
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;                &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; SUCCESS;
&lt;/pre&gt;
&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;            &lt;span style="color: #0000ff"&gt;case&lt;/span&gt; ProgramState::STATE_NONE:
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;            &lt;span style="color: #0000ff"&gt;case&lt;/span&gt; ProgramState::STATE_ERROR:
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;            &lt;span style="color: #0000ff"&gt;default&lt;/span&gt;:
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;                setStdinBlocking(&lt;span style="color: #0000ff"&gt;true&lt;/span&gt;);
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;                &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; FAILURE;
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;        }
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;&lt;/pre&gt;
&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;        ProgramState::State cur = state.cur_state();
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;        state.set_prev_state(cur);
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;        state.set_cur_state(next);
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;&lt;/pre&gt;
&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;        &lt;span style="color: #008000"&gt;// For now, simply let the user decide when to swap and quit. We can&lt;/span&gt;
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;        &lt;span style="color: #008000"&gt;// always change this later.&lt;/span&gt;
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;        ReturnCode code = checkForUserSignal();
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;        &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (code != CONTINUE) {
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;            setStdinBlocking(&lt;span style="color: #0000ff"&gt;true&lt;/span&gt;);
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;            &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; code;
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;        }
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    }
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;}
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;&lt;/pre&gt;
&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;ProgramState::State runState_init(ProgramState&amp;amp; state) {
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    cout &amp;lt;&amp;lt; &amp;quot;&lt;span style="color: #8b0000"&gt;Please provide a line of text for me to repeat ad-nauseum\n&lt;/span&gt;&amp;quot;;
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; line;
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    setStdinBlocking(&lt;span style="color: #0000ff"&gt;true&lt;/span&gt;);
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    getline(cin, line);
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    setStdinBlocking(&lt;span style="color: #0000ff"&gt;false&lt;/span&gt;);
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    state.set_line_text(line);
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    cout &amp;lt;&amp;lt; &amp;quot;&lt;span style="color: #8b0000"&gt;Thanks!\n&lt;/span&gt;&amp;quot;;
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    state.set_line_count(0);
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;&lt;/pre&gt;
&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;    &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; ProgramState::STATE_PROCESS_LINE;
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;}
&lt;/pre&gt;&lt;pre style="background-color: #c0c0c0; margin: 0em; width: 100%; font-family: consolas,&amp;#39;Courier New&amp;#39;,courier,monospace; font-size: 11px"&gt;&lt;/pre&gt;&lt;/pre&gt;


&lt;p&gt;
Easy, right? And here is an example transcript from going forward and then back between the versions in the repository (behind the scenes compiles not shown):&lt;/p&gt;


&lt;table border="1"&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="background-color: black; color: rgb(200,200,200)"&gt;
&lt;pre&gt;eric:~/code/hotswap/v1$ &lt;span style="color:green; margin:0em"&gt;../bin/hotswap.out&lt;/span&gt;
HotSwap example started - version 0.1
Initial call
Running state machine
Please provide a line of text for me to repeat ad-nauseum
&lt;span style="color:green; margin:0em"&gt;All work and no play makes jack a dull boy&lt;/span&gt;
Thanks!
0: All work and no play makes jack a dull boy
1: All work and no play makes jack a dull boy
2: All work and no play makes jack a dull boy
&lt;span style="color:green; margin:0em"&gt;u&lt;/span&gt;
Going down for swap
Coming up from swap!
This is generation 2 running 0.2 software (initial version 0.1)
Running state machine
3 mutations: All work and no play makes jack a dull boy
4 mutations: All work and no play maXes jack a dull boy
5 mutations: All workqand no play maXes jack a dull boy
6 mutations: All workqand nL play maXes jack a dull boy
&lt;span style="color:green; margin:0em"&gt;u&lt;/span&gt;
Going down for swap
HotSwap example started - version 0.1
Coming up from swap!
Running state machine
7: All workqand nL play maXes jack a dull boy
8: All workqand nL play maXes jack a dull boy
9: All workqand nL play maXes jack a dull boy
&lt;span style="color:green; margin:0em"&gt;q&lt;/span&gt;
Terminating with code 0&lt;/pre&gt;

&lt;/td&gt;
&lt;/tr&gt;

&lt;/tbody&gt;&lt;/table&gt;


&lt;p&gt;
&lt;br/&gt;

As you can see, version 0.2 mutates the line as it goes, while version 0.1 simply prints it forever. There are more differences than that, but you can find all that out from the code.&lt;/p&gt;


&lt;p&gt;
Enjoy! If you do end up playing with it, I’d love to hear about your experiences, or your thoughts on the design even if not.&lt;/p&gt;

&lt;br/&gt;
&lt;p&gt;
&lt;em&gt;This will probably be my last post for a while – on Saturday I leave the continent for 2 weeks and the city for 6. I will try to respond to emails and comments while I’m gone, but I may be a bit slower than usual.&lt;/em&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-8001471963766584930?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/NzT8URH33Qc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/8001471963766584930/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2010/04/robustly-hot-swapping-binaries-with.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/8001471963766584930?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/8001471963766584930?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/NzT8URH33Qc/robustly-hot-swapping-binaries-with.html" title="Robustly hot swapping binaries (with code)" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2010/04/robustly-hot-swapping-binaries-with.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEUHQHo6fSp7ImA9WhRVFk0.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-579732869948510119</id><published>2010-04-23T16:37:00.009-04:00</published><updated>2012-01-15T00:43:51.415-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-15T00:43:51.415-05:00</app:edited><title>Indexing and enumerating subsets of a given size</title><content type="html">&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_4uUxYbT6E0Q/S9IEdmTBKBI/AAAAAAAAD88/UIudROzO7Ys/s1600-h/9170.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="Subsets of a 9 element set containing up to 3 elements, in Banker&amp;#39;s order" border="0" alt="Subsets of a 9 element set containing up to 3 elements, in Banker&amp;#39;s order" src="http://lh3.ggpht.com/_4uUxYbT6E0Q/S9IEd7S1akI/AAAAAAAAD9A/0EImLHiNd1o/9_thumb168.png?imgmax=800" width="391" height="31" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I received an email yesterday from a gentleman named Calvin Miracle, asking my opinion on subset enumeration strategy. He also provided a copy of &lt;a title="Efficiently Enumerating the Subsets of a Set" href="http://applied-math.org/subset.pdf"&gt;this paper&lt;/a&gt;, which details an algorithm to generate a sequence of subsets in Banker’s order&lt;sup&gt;&lt;a href="#bankersOrdering"&gt;1&lt;/a&gt;&lt;/sup&gt;, and asked about an algorithm to generate these subsets in a stateless manner. I’ll let him describe it:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Given a call to the method banker(&lt;em&gt;n&lt;/em&gt;,&lt;em&gt;k&lt;/em&gt;,&lt;em&gt;i&lt;/em&gt;), where &lt;em&gt;n&lt;/em&gt; is the size of a set, &lt;em&gt;k&lt;/em&gt; is the subset size under consideration, and &lt;em&gt;i&lt;/em&gt; is the i'th subset of size &lt;em&gt;k&lt;/em&gt;, the method will return a boolean vector of size &lt;em&gt;n&lt;/em&gt;, with only &lt;em&gt;k&lt;/em&gt; TRUE values, that selects the i'th&lt;em&gt; k&lt;/em&gt;-size subset from the overall set.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Now, prior to this I had never heard of Banker’s sequences nor really thought about enumerating subsets, but I’m always willing to be &lt;a href="http://xkcd.com/356/"&gt;nerd sniped&lt;/a&gt; so I gave it a go. Presented here is the algorithm I designed for him.&lt;/p&gt;&lt;h2&gt;Disclaimer&lt;/h2&gt;&lt;p&gt;After writing this algorithm, I did some more digging and found out that some recent research has been done into enumerating subsets of a specific size, although in lexicographic order rather than Banker’s order. &lt;a href="http://en.wikipedia.org/wiki/Combinatorial_number_system"&gt;Wikipedia&lt;/a&gt; has details on this, and provides an algorithm very similar to the one I created here for except lexicographic ordering. So none of this should be seen as especially new or groundbreaking, although it was new to me and hopefully will be to you as well.&lt;/p&gt;&lt;h2&gt;Algorithm&lt;/h2&gt;&lt;p&gt;The basic idea of this algorithm is that given any choice of the first element for our subset, we can calculate how many possible ways to choose the remaining elements there are, and we know that every subset with this first element will come before every subset with a later first element according to our ordering scheme. So we can iterate through the possible choices of first element, totalling up how many subsets each represents, until we find the range that the desired index falls within. We can then recursively determine the rest of the subset in the same manner.&lt;/p&gt;&lt;p&gt;So, Let &lt;em&gt;n&lt;/em&gt; be the number of items,&lt;em&gt; k&lt;/em&gt; the size of the subsequence, and &lt;em&gt;i &lt;/em&gt;the specific index we are looking for. We will enumerate sequences by considering all subsets that start with “1”, then all that start with “01”, then all that start with “001”, etc., where “0” represents skipping an item and “1” represents selecting it. There are &lt;em&gt;n&lt;/em&gt;-1 choose &lt;em&gt;k&lt;/em&gt;-1 of the first sort, &lt;em&gt;n&lt;/em&gt;-2 choose &lt;em&gt;k&lt;/em&gt;-1 of the second, &lt;em&gt;n&lt;/em&gt;-3 choose &lt;em&gt;k&lt;/em&gt;-1 of the third, etc. So:&lt;/p&gt;&lt;table border="0" cellspacing="0" cellpadding="2" width="440"&gt;&lt;tbody&gt;
&lt;tr&gt;       &lt;td valign="top" width="54"&gt;“1”:&lt;/td&gt;        &lt;td valign="top" width="143" align="right"&gt;0&lt;/td&gt;        &lt;td valign="top" width="52"&gt;&amp;lt;= &lt;em&gt;i&lt;/em&gt; &amp;lt; &lt;/td&gt;        &lt;td valign="top" width="189"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="n-1Ck-1" border="0" alt="n-1Ck-1" src="http://lh6.ggpht.com/_4uUxYbT6E0Q/S9IEeAyPkeI/AAAAAAAAD9E/wm8UemyXkxI/clip_image002%5B4%5D%5B3%5D.gif?imgmax=800" width="49" height="32" /&gt;&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign="top" width="54"&gt;“01”:&lt;/td&gt;        &lt;td valign="top" width="143" align="right"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="n-1Ck-1" border="0" alt="n-1Ck-1" src="http://lh6.ggpht.com/_4uUxYbT6E0Q/S9IEeAyPkeI/AAAAAAAAD9E/wm8UemyXkxI/clip_image002%5B4%5D%5B3%5D.gif?imgmax=800" width="49" height="32" /&gt;&lt;/td&gt;        &lt;td valign="top" width="52"&gt;&amp;lt;= &lt;em&gt;i&lt;/em&gt; &amp;lt;&lt;/td&gt;        &lt;td valign="top" width="189"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="n-1Ck-1 + n-2Ck-1" border="0" alt="n-1Ck-1 + n-2Ck-1" src="http://lh5.ggpht.com/_4uUxYbT6E0Q/S9IEedwJkgI/AAAAAAAAD9I/JBZ60f-oKPE/clip_image002%5B6%5D%5B3%5D.gif?imgmax=800" width="115" height="32" /&gt;&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign="top" width="54"&gt;“001”:&lt;/td&gt;        &lt;td valign="top" width="143" align="right"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="n-1Ck-1 + n-2Ck-1" border="0" alt="n-1Ck-1 + n-2Ck-1" src="http://lh5.ggpht.com/_4uUxYbT6E0Q/S9IEedwJkgI/AAAAAAAAD9I/JBZ60f-oKPE/clip_image002%5B6%5D%5B3%5D.gif?imgmax=800" width="115" height="32" /&gt;&lt;/td&gt;        &lt;td valign="top" width="52"&gt;&amp;lt;= &lt;em&gt;i&lt;/em&gt; &amp;lt;&lt;/td&gt;        &lt;td valign="top" width="189"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="n-1Ck-1 + n-2Ck-1 + n-3Ck-1" border="0" alt="n-1Ck-1 + n-2Ck-1 + n-3Ck-1" src="http://lh5.ggpht.com/_4uUxYbT6E0Q/S9IEfDWCAAI/AAAAAAAAD9Q/2C89T37gLSQ/clip_image002%5B8%5D_thumb.gif?imgmax=800" width="181" height="32" /&gt;&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign="top" width="54"&gt;…&lt;/td&gt;        &lt;td valign="top" width="143" align="right"&gt;&amp;#160;&lt;/td&gt;        &lt;td valign="top" width="52"&gt;&amp;#160;&lt;/td&gt;        &lt;td valign="top" width="189"&gt;&amp;#160;&lt;/td&gt;     &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;p&gt;Once we know what prefix &lt;em&gt;i&lt;/em&gt; has, we can recursively determine the next sequence using &lt;em&gt;n'&lt;/em&gt; = &lt;em&gt;n&lt;/em&gt;-(prefix length), &lt;em&gt;k' &lt;/em&gt;= &lt;em&gt;k&lt;/em&gt;-1, &lt;em&gt;i' &lt;/em&gt;= &lt;em&gt;i&lt;/em&gt;-(bottom of range).&lt;/p&gt;&lt;h2&gt;Example&lt;/h2&gt;&lt;p&gt;Let &lt;em&gt;n&lt;/em&gt; = 5, &lt;em&gt;k &lt;/em&gt;= 3, &lt;em&gt;i &lt;/em&gt;= 7:&lt;/p&gt;&lt;table border="0" cellspacing="0" cellpadding="2" width="348"&gt;&lt;tbody&gt;
&lt;tr&gt;       &lt;td valign="top" width="91"&gt;“1”&lt;/td&gt;        &lt;td valign="top" width="15" align="right"&gt;0&lt;/td&gt;        &lt;td valign="top" width="47"&gt;&amp;lt;= &lt;em&gt;i&lt;/em&gt; &amp;lt; &lt;/td&gt;        &lt;td valign="top" width="193"&gt;6&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; (4C2)&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign="top" width="91"&gt;“01”:&lt;/td&gt;        &lt;td valign="top" width="15" align="right"&gt;6&lt;/td&gt;        &lt;td valign="top" width="47"&gt;&amp;lt;= &lt;em&gt;i&lt;/em&gt; &amp;lt;&lt;/td&gt;        &lt;td valign="top" width="193"&gt;9 = 6+3&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; (4C2 + 3C2)&lt;/td&gt;     &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;So, initial prefix is “01”.&lt;/p&gt;&lt;p&gt;We recurse with &lt;em&gt;n&lt;/em&gt; = 3, &lt;em&gt;k&lt;/em&gt; = 2, &lt;em&gt;i&lt;/em&gt; = 1:&lt;/p&gt;&lt;table border="0" cellspacing="0" cellpadding="2" width="348"&gt;&lt;tbody&gt;
&lt;tr&gt;       &lt;td valign="top" width="91"&gt;“1”&lt;/td&gt;        &lt;td valign="top" width="15" align="right"&gt;0&lt;/td&gt;        &lt;td valign="top" width="47"&gt;&amp;lt;= &lt;em&gt;i&lt;/em&gt; &amp;lt; &lt;/td&gt;        &lt;td valign="top" width="193"&gt;2&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; (2C1)&lt;/td&gt;     &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;So, the next piece is “1”.&lt;/p&gt;&lt;p&gt;Finally, we recurse with &lt;em&gt;n&lt;/em&gt;=2,&lt;em&gt; k&lt;/em&gt;=1, &lt;em&gt;i&lt;/em&gt;=1:     &lt;br /&gt;
Trivially we can see that for “10” = 0, “01” = 1, so the last piece is “01”.&lt;/p&gt;&lt;p&gt;Putting this together, the sequence is “01101”,&amp;#160; so items 2, 3, and 5 of the 5 compose the 7th subset&lt;sup&gt;&lt;a href="#indexedFromZero"&gt;2&lt;/a&gt;&lt;/sup&gt; of length 3.&lt;/p&gt;&lt;h2&gt;Complexity&lt;/h2&gt;&lt;p&gt;This algorithm is quite efficient – it needs to check at most &lt;em&gt;n&lt;/em&gt; prefixes in total over all the levels of recursion, since each we consider shortens our candidate set by 1. There will be at most &lt;em&gt;k &lt;/em&gt;recursive levels, and &lt;em&gt;k&lt;/em&gt; ≤ &lt;em&gt;n&lt;/em&gt;. So if we handwaive the “&lt;em&gt;a &lt;/em&gt;choose &lt;em&gt;b” &lt;/em&gt;calculations, we find that this can be done in O(&lt;em&gt;n&lt;/em&gt;) time, which is O(1) in the size of the output, and thus optimal.&lt;/p&gt;&lt;p&gt;If we &lt;em&gt;don’t&lt;/em&gt; handwaive the choice functions, we find that &lt;em&gt;n&lt;/em&gt;C&lt;em&gt;k&lt;/em&gt; is O(&lt;em&gt;n&lt;/em&gt;!) in the worst case, so the result requires O(log(&lt;em&gt;n&lt;/em&gt;!)) =O(&lt;em&gt;n&lt;/em&gt;log&lt;em&gt;n&lt;/em&gt;) bits to store. If addition is O(1) we still may need to add up to &lt;em&gt;n &lt;/em&gt;of these, so the total worst-case runtime is O(&lt;em&gt;n&lt;/em&gt;&lt;sup&gt;2&lt;/sup&gt; log&lt;em&gt;n&lt;/em&gt;), or O(&lt;em&gt;n&lt;/em&gt;log&lt;em&gt;n&lt;/em&gt;) in the size of the output. However, with small values for k we don't get anywhere close to that worst case, and indeed are closer to O(log&lt;em&gt;n&lt;/em&gt;) in size of the input. This is still quite efficient, and probably about the best that can be achieved in a function of this type. We should also consider the time needed to &lt;em&gt;calculate&lt;/em&gt; the choice values, but if we are iterating over these indices the values can be pre-cached first and then the amortized cost per subsequence lookup goes to effectively zero.&lt;/p&gt;&lt;h2&gt;Code&lt;/h2&gt;&lt;p&gt;If you want to play with this algorithm yourself, I have placed some java code implementing it &lt;a href="http://github.com/EricBurnett/EnumeratedSubsets"&gt;on GitHub&lt;/a&gt;. It is as efficient as expected, and should be plenty fast enough for anything you could conceivably need it for. For example, it takes essentially no time to determine that the 160,000,000,000,000,000,000,000,000,000th subset of size 12 from a 10,000 item set is {0, 1, 2, 69, 1212, 1381, 4878, 5291, 5974, 6139, 6639, 8979}. So have at it!&lt;/p&gt;&lt;p&gt;&lt;b&gt;Update:&lt;/b&gt; Ligon Liu has kindly provided a C# port of this code, which I have added to the repository (it the c# directory).&lt;/p&gt;&lt;p&gt;&lt;b&gt;Update 2:&lt;/b&gt; Josiah Carlson has kindly provided a python port of this code, also in the repository now.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Update 3:&lt;/b&gt; &lt;a href="http://richardlyon.co.uk"&gt;Richard Lyon&lt;/a&gt; has kindly provided ports to both JavaScript and PHP, on GitHub as well. Thanks guys!&lt;/p&gt;&lt;p&gt;&lt;b&gt;Update 4:&lt;/b&gt; Corin Lawson has a C implementation in his &lt;a href="https://github.com/au-phiware/bankers"&gt;GitHub repository&lt;/a&gt;, along with other Banker's order functions. Great!&lt;/p&gt;&lt;h2&gt;&lt;font color="#666666"&gt;Footnotes&lt;/font&gt;&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;&lt;a name="bankersOrdering"&gt;&lt;/a&gt;The easiest way to think of Banker’s ordering is to think of comparing the &lt;em&gt;indices &lt;/em&gt;of the items that make up the subsets. So to order two subsets, take the list of indices of elements in the first subset, and compare it to that of the second subset in standard dictionary (lexicographic) order. So {2, 3, 4} comes before {2, 3, 5} but after {1, 3, 5}, for example. &lt;br /&gt;
&lt;br /&gt;
The other way to think about it is that it is reverse lexicographic order on the sequence of choices. So in our example, "01110" comes before "01101" but after "10101". So when I talk about Banker's order versus lexicographic order, they are merely reversals of each other.&lt;br /&gt;
&lt;br /&gt;
The image at the top of this post depicts the Banker’s ordering for subsets of&amp;#160; lengths 1, 2, and 3, respectively, from a set of size 9.&lt;/li&gt;
&lt;li&gt;&lt;a name="indexedFromZero"&gt;&lt;/a&gt;Subsets are indexed from 0, so you may consider this the 8th subset.&lt;/li&gt;
&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-579732869948510119?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/zLxVLJuOPo4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/579732869948510119/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2010/04/indexing-and-enumerating-subsets-of.html#comment-form" title="10 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/579732869948510119?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/579732869948510119?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/zLxVLJuOPo4/indexing-and-enumerating-subsets-of.html" title="Indexing and enumerating subsets of a given size" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/_4uUxYbT6E0Q/S9IEd7S1akI/AAAAAAAAD9A/0EImLHiNd1o/s72-c/9_thumb168.png?imgmax=800" height="72" width="72" /><thr:total>10</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2010/04/indexing-and-enumerating-subsets-of.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU8FQ348fyp7ImA9WhRVGUQ.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-6097403193265206323</id><published>2010-04-14T01:45:00.012-04:00</published><updated>2012-01-19T13:30:12.077-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-19T13:30:12.077-05:00</app:edited><title>Introducing: Marriage Sort</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S8VHzLfVdkI/AAAAAAAAD6o/gs1zjLSHEF4/s1600/Marriage.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S8VHzLfVdkI/AAAAAAAAD6o/gs1zjLSHEF4/s576/Marriage.png" width="576" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;i&gt;Update: I've created a live visualization of this algorithm, so you can see it in action - see &lt;a href="http://www.thelowlyprogrammer.com/2012/01/marriage-sort-visualization.html"&gt;Marriage Sort, a Visualization.&lt;/a&gt;&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
Two weeks ago, a link showed up on Hacker News on how to (mathematically) &lt;a href="http://www.mathpages.com/home/kmath018/kmath018.htm"&gt;select the best wife&lt;/a&gt;. The key takeaway from the article is that knowing only the relative rankings of women you've dated (and assuming you can never go back to a previous one), you can maximize your chances of marrying the best one by letting N/e go by and then marrying the next one you meet that's better than all those. Further, to maximize your &lt;i&gt;expected value&lt;/i&gt; you only need to let the first √N - 1 go by. After rattling around in the back of my mind for a couple weeks, yesterday this tidbit coalesced into an actual Idea. Could I turn this selection routine into a sorting algorithm? &lt;br /&gt;
&lt;br /&gt;
And thus, Marriage Sort was born. I present it here purely out of interest, not because it is especially fast - if nothing else, because it is the only sorting algorithm I know of that has an average complexity of O(n&lt;sup&gt;1.5&lt;/sup&gt;)&lt;sup&gt;&lt;a href="#AlgorithmRuntimes"&gt;1&lt;/a&gt;&lt;/sup&gt;. &lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Algorithm&lt;/h2&gt;The basic idea of this algorithm is to repeatedly choose the maximum element from the first √N - 1 elements of our working set, and then scan to the end looking for elements bigger than it. When one is found, swap it to the end of the working set, and decrease the working set by one. After the pass is complete, swap out the maximum element from the first √N - 1 as well, and start again. When everything is finished (√N - 1 &amp;lt;= 0), use insertion sort to put the array into &lt;i&gt;true&lt;/i&gt; sorted order.&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S8VUcrOjHBI/AAAAAAAAD7A/-z455W-MUls/s1600/one+pass.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="145" src="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S8VUcrOjHBI/AAAAAAAAD7A/-z455W-MUls/s400/one+pass.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;One pass of the Marriage Sort. Two elements are found and swapped to the end, followed by the maximum element from the first √N - 1.&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
This works because, as we have noted from the article linked above, each element bigger than the first √N - 1 is expected to be &lt;i&gt;close&lt;/i&gt; to the largest remaining element in the array. By repeatedly taking these elements and moving them to the end, we put the array into an approximately sorted order, where elements should be reasonably close to their 'true' positions. When this is done insertion sort will do the final rearranging, moving elements the short distances to their true positions. &lt;br /&gt;
&lt;br /&gt;
In pseudocode:&lt;br /&gt;
&lt;blockquote&gt;def marriagesort(array):&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; end = array.length&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; while true: &lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;skip = sqrt(end) - 1&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;if skip &amp;lt;= 0: break&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;# Pick the best element in the first vN - 1:&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;bestPos = 0, i = 1&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;while i &amp;lt; skip:&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;if array[i] &amp;gt; array[bestPos]: bestPos = i&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;i += 1&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;# Now pull out elements &amp;gt;= array[bestPos], and move to the end:&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;i = skip&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;while i &amp;lt; end:&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;if array[i] &amp;gt;= array[bestPos]: &lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;array.swap(i, end-1)&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;end -= 1&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;else: &lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;i += 1&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;# Finally, move our best pivot element to the end&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;swap(bestPos, end-1)&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;end -= 1&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; # Finish off with insertion sort to put the elements into true sorted order&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; insertionsort(array)&lt;/blockquote&gt;&lt;br /&gt;
Here you can see this algorithm working on a randomized array. Many thanks to Aldo Cortesi for the wonderful &lt;a href="http://corte.si/posts/code/visualisingsorting/index.html"&gt;sorting algorithm visualization framework&lt;/a&gt;! &lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S8VHzLfVdkI/AAAAAAAAD6o/gs1zjLSHEF4/s1600/Marriage.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S8VHzLfVdkI/AAAAAAAAD6o/gs1zjLSHEF4/s576/Marriage.png" width="576" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;Update: &lt;/b&gt;Here is another visualization made using Aldo Cortesi's tools, of a 512 element array this time. If you take a close look at the insertion sort phase (the slope on the right side), you can see that it is composed out of a lot of little triangles. Each of these triangles corresponds to one pass of the marriage selection routine, and shows how far elements can be from their true sorted position.&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_4uUxYbT6E0Q/S8eU2mLT6oI/AAAAAAAAD7Q/QFOnAcQZOcE/s1600/marriagesort512.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_4uUxYbT6E0Q/S8eU2mLT6oI/AAAAAAAAD7Q/QFOnAcQZOcE/s576/marriagesort512.png" width="576" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;Complexity&lt;/h2&gt;Some quick back-of-the-napkin calculations indicate that this algorithm takes O(n&lt;sup&gt;1.5&lt;/sup&gt;) compares in the average case, although the worst case looks to be O(n&lt;sup&gt;2&lt;/sup&gt;). For the first stage (i.e. without the final insertion sort) each pass will take at most n compares and move approximately √N items to the end, requiring about √N passes. I don't know if this is a tight bound - the passes will actually speed up and require fewer compares as the working set shrinks, however I didn't want to try including that in the calculations. Similarly, after each pass all the items moved are guaranteed to be greater than all the items left, capping the distance moved items are from "home" to √N on average for the insertion sort. This holds the insertion sort to O(n&lt;sup&gt;1.5&lt;/sup&gt;) compares on average as well. &lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_4uUxYbT6E0Q/S8VJVc4b71I/AAAAAAAAD6w/KO-A7_pTCvQ/s1600/algorithm+complexity+-+Comparisons.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_4uUxYbT6E0Q/S8VJVc4b71I/AAAAAAAAD6w/KO-A7_pTCvQ/s576/algorithm+complexity+-+Comparisons.png" width="576" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Similarly, this algorithm takes O(n&lt;sup&gt;1.5&lt;/sup&gt;) swaps in the average case (and O(n&lt;sup&gt;2&lt;/sup&gt;) in the worst case). The number of swaps could certainly be decreased if a different final sort were used - the first pass only takes Θ(n) by itself - although in practice this isn't much use since the compares will still dominate the runtime.&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_4uUxYbT6E0Q/S8VJavj01_I/AAAAAAAAD64/-kWRvytpuB8/s1600/algorithm+complexity+-+swaps.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_4uUxYbT6E0Q/S8VJavj01_I/AAAAAAAAD64/-kWRvytpuB8/s576/algorithm+complexity+-+swaps.png" width="576" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Note that both of these graphs are log-log plots, so the important feature to look at is the slope of lines rather than the absolute position. For example, in the "Swaps" plot quicksort appears to be hovering around the n∙log(log(n)) line, however looking at the slope we can see that it is actually approximating n∙log(n), just with a better constant factor. &lt;br /&gt;
&lt;br /&gt;
Note 2: The x axis of these graphs is the number of elements in the array, and the y axis is the number of comparisons/sorts required. I should have labeled these better. &lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Postscript&lt;/h2&gt;Some sample code implementing this algorithm is up &lt;a href="http://gist.github.com/365449"&gt;on GitHub&lt;/a&gt; - this is the code to generate the log-log plots shown above. &lt;br /&gt;
&lt;br /&gt;
As always, questions and comments are welcome! &lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;&lt;span style="color: #666666;"&gt;Footnotes&lt;/span&gt;&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;&lt;a name="AlgorithmRuntimes"&gt;&lt;/a&gt;Most sorting algorithms are either Θ(n&lt;sup&gt;2&lt;/sup&gt;) or Θ(n∙log(n)) in the average case. This one falls between those two extremes, making it faster (asymptotically) than algorithms like insertion sort or bubble sort but slower than quicksort or merge sort.&lt;/li&gt;
&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-6097403193265206323?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/xNr32SK1Rec" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/6097403193265206323/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2010/04/introducing-marriage-sort.html#comment-form" title="20 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/6097403193265206323?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/6097403193265206323?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/xNr32SK1Rec/introducing-marriage-sort.html" title="Introducing: Marriage Sort" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S8VHzLfVdkI/AAAAAAAAD6o/gs1zjLSHEF4/s72-c/Marriage.png" height="72" width="72" /><thr:total>20</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2010/04/introducing-marriage-sort.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A04EQHs6fyp7ImA9WxBbGUk.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-3705254461079453753</id><published>2010-03-17T00:38:00.004-04:00</published><updated>2010-03-18T18:05:01.517-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-03-18T18:05:01.517-04:00</app:edited><title>Visualizing RGB, take two - Update</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_4uUxYbT6E0Q/S6BUiQr0FFI/AAAAAAAAD6Y/8rXYjAEGZek/s1600-h/MonaLisa_allRGBv2_cap40_downsampled.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_4uUxYbT6E0Q/S6BUiQr0FFI/AAAAAAAAD6Y/8rXYjAEGZek/s320/MonaLisa_allRGBv2_cap40_downsampled.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
When I posted my &lt;a href="http://www.thelowlyprogrammer.com/2010/02/visualizing-rgb-take-two.html"&gt;'Visualizing RGB, take two'&lt;/a&gt; article last month, an anonymous commenter going by the name &lt;i&gt;Full-size&lt;/i&gt; had a good suggestion - I should use a form of &lt;a href="http://en.wikipedia.org/wiki/Error_diffusion"&gt;error diffusion&lt;/a&gt; to better hide the pixel errors when selecting the colours to use. Within a couple days I had this implemented, and wow does it make an improvement! Unfortunately, between school and needing to reinstall Windows I never ended up posting the results. So, three weeks late, here they are.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Results for Barcelona image&lt;/h2&gt;As you may recall, the goal of the algorithm was to transform a source image into a 4096x4096 version that uses every RGB colour exactly once. As a reminder, here is the original image, and what my algorithm previously did with it:&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_4uUxYbT6E0Q/S3-B5LqQ7uI/AAAAAAAAD4M/YwDGf-g10zw/s1600-h/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://2.bp.blogspot.com/_4uUxYbT6E0Q/S3-B5LqQ7uI/AAAAAAAAD4M/YwDGf-g10zw/s200/2.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S393hgdd98I/AAAAAAAAD3c/smS15rdghuM/s1600-h/2_allRGBv2_big_downsampled.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S393hgdd98I/AAAAAAAAD3c/smS15rdghuM/s200/2_allRGBv2_big_downsampled.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div width="400px"&gt;&lt;div style="float: left; text-align: center; width: 200px;"&gt;Original&lt;/div&gt;&lt;div style="float: right; text-align: center; width: 200px;"&gt;Result from previous version&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
Now, take a look at how it looks using a form of error diffusion (with error terms capped at 60):&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_4uUxYbT6E0Q/S6BUMBlnEFI/AAAAAAAAD6I/KpVSGAymuVw/s1600-h/3_allRGBv2_cap60_downsampled.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://2.bp.blogspot.com/_4uUxYbT6E0Q/S6BUMBlnEFI/AAAAAAAAD6I/KpVSGAymuVw/s320/3_allRGBv2_cap60_downsampled.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
(Full 50 meg png &lt;a href="http://www.mediafire.com/file/2gmfkxhzmyo/3_allRGBv2_cap60_big.zip"&gt;available here&lt;/a&gt;). Much nicer to look at! Take a look at the sky in particular - where it used to go psychedelic trying to deal with all that near white, large portions now end up simply turning into a uniform gray. The new version is worse in a couple spots (e.g. the wheel wells of the car), but overall I think it is hugely improved. Now, I wonder how the new version would do on a harder image?&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Results for the Mona Lisa&lt;/h2&gt;As promised, I am also posting the results of running this algorithm on an image of the Mona Lisa. This is an especially difficult image, because the colour palette is very limited - notice the lack of blue in particular. First, let's take a look at the original, and the result from the previous version of the code:&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S6BUVgNU5RI/AAAAAAAAD6Q/2G-IOFI2O2g/s1600-h/MonaLisa_downsampled.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S6BUVgNU5RI/AAAAAAAAD6Q/2G-IOFI2O2g/s200/MonaLisa_downsampled.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://3.bp.blogspot.com/_4uUxYbT6E0Q/S6BZSxJRx9I/AAAAAAAAD6g/KLUlYx8s4d0/s1600-h/MonaLisa_allRGBv2_cap0.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://3.bp.blogspot.com/_4uUxYbT6E0Q/S6BZSxJRx9I/AAAAAAAAD6g/KLUlYx8s4d0/s200/MonaLisa_allRGBv2_cap0.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div width="400px"&gt;&lt;div style="float: left; text-align: center; width: 200px;"&gt;Original&lt;/div&gt;&lt;div style="float: right; text-align: center; width: 200px;"&gt;Result from previous version&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
Ouch. Poor Lisa. Still, let's forge on and see how the new version does, shall we?&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_4uUxYbT6E0Q/S6BUiQr0FFI/AAAAAAAAD6Y/8rXYjAEGZek/s1600-h/MonaLisa_allRGBv2_cap40_downsampled.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_4uUxYbT6E0Q/S6BUiQr0FFI/AAAAAAAAD6Y/8rXYjAEGZek/s320/MonaLisa_allRGBv2_cap40_downsampled.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
(Full 50 meg png &lt;a href="http://www.mediafire.com/file/cd4mzzniuzl/MonaLisa_allRGBv2_cap40_big.zip"&gt;available here&lt;/a&gt;). Considerably better overall, although the colour burning on the forehead and neck is pretty ugly to look at. Still, considering we are trying to use every RGB colour once, I think the results are quite decent.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Postscript&lt;/h2&gt;The code to achieve this is in the same place as before, updated &lt;a href="http://github.com/EricBurnett/AllRGBv2"&gt;on GitHub&lt;/a&gt;. Right now you have to change the source code to edit the error diffusion cap, sorry - but feel free to fix that!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-3705254461079453753?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/x4W1KXUAKVA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/3705254461079453753/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2010/03/visualizing-rgb-take-two-update.html#comment-form" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/3705254461079453753?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/3705254461079453753?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/x4W1KXUAKVA/visualizing-rgb-take-two-update.html" title="Visualizing RGB, take two - Update" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_4uUxYbT6E0Q/S6BUiQr0FFI/AAAAAAAAD6Y/8rXYjAEGZek/s72-c/MonaLisa_allRGBv2_cap40_downsampled.png" height="72" width="72" /><thr:total>3</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2010/03/visualizing-rgb-take-two-update.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D04EQ3Y_cCp7ImA9WxFSFk4.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-3027817597499477553</id><published>2010-03-02T22:57:00.007-05:00</published><updated>2010-04-18T20:11:42.848-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-18T20:11:42.848-04:00</app:edited><title>Writing an efficient Sieve of Eratosthenes</title><content type="html">I recently came across &lt;a href="http://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf"&gt;a paper&lt;/a&gt; by Melissa E. O'Neill on implementing a lazily evaluated &lt;a href="http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes"&gt;Sieve of Eratosthenes&lt;/a&gt;. It was a very interesting read, although for a non-Haskeller understanding the code was certainly tricky. And by happy happenstance, not two weeks later I ended up needing a prime generator of my own for one of the &lt;a href="http://projecteuler.net/"&gt;Project Euler&lt;/a&gt; problems. This, I thought, was the perfect opportunity to try implementing the algorithm from that paper! That thought ended up leading me on a many-day journey to see how efficient an implementation I could make. This post details the key changes my code went through in that time, culminating in a C# algorithm that can sieve around 20 million candidates per second (or a Python version that can do 1.4 million). &lt;br /&gt;
&lt;h2&gt;Disclaimers&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;During the course of this journey I learned about segmented sieves and the &lt;a href="http://en.wikipedia.org/wiki/Sieve_of_Atkin"&gt;Sieve of Atkin&lt;/a&gt;, but I decided not to make use of either of these, instead sticking to the algorithm I chose from the start. Maybe next time.&lt;/li&gt;
&lt;li&gt;If you are simply looking for the fastest implementation possible, I point you towards &lt;a href="http://cr.yp.to/primegen.html"&gt;http://cr.yp.to/primegen.html&lt;/a&gt;, which is written in highly optimized C and at least an order of magnitude faster than mine.&lt;/li&gt;
&lt;/ol&gt;&lt;h2&gt;Version 1: Straight re-implementation of algorithm from paper&lt;/h2&gt;&lt;u&gt;Language:&lt;/u&gt; Python&lt;u&gt;&lt;br /&gt;
Time complexity:&lt;/u&gt;&lt;b&gt; &lt;/b&gt;O(n&amp;#8729;log(n)&amp;#8729;log(log(n)))&lt;br /&gt;
&lt;u&gt;Space complexity&lt;/u&gt;&lt;b&gt;: &lt;/b&gt;O(&amp;#8730;n/log(n))&lt;br /&gt;
&lt;u&gt;Candidates in 1 sec:&lt;/u&gt; ~300,000 &lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://gist.github.com/320271#file_v1.py"&gt;Code&lt;/a&gt; &lt;a href="javascript:expandContract('v1.py')"&gt;(expand/contract)&lt;/a&gt;&lt;br /&gt;
&lt;div id="v1.py" class="expandable"&gt;&lt;script src="http://gist.github.com/320271.js?file=v1.py"&gt;&lt;/script&gt;&lt;/div&gt;&lt;br /&gt;
This is simply a re-implementation in Python of the Haskell code from the paper above, although it loses some of the elegance in the translation.  You'll note that I am measuring performance with the very unscientific 'candidates in 1 sec' - since I am trying to compare algorithms with different asymptotic complexities in different languages with order-of-magnitude performance differences, it seemed like the easiest way to get a feel for the general magnitudes in this apples-to-oranges comparison. &lt;br /&gt;
&lt;br /&gt;
The key idea here is that for each prime we encounter, we make a generator for multiples of it (starting at p&lt;sup&gt;2&lt;/sup&gt;) that we can use to filter out our candidates. For efficiency, all multiples of 2 and 3 are skipped automatically as well, since that reduces the number of candidates to check by two thirds. These generators are all added to a min-heap, keyed by the next value each will generate. Then we can keep checking the next candidate and seeing if it matches the top of the heap. If it does, pop that, increment it, and push it back on keyed by the next value. If not, the candidate must be prime, so build a new generator and add that instead. &lt;br /&gt;
&lt;br /&gt;
 So, let's try an example of this. The first candidate we try is 5 (remembering that all multiples of 2 and 3 are already gone), and our heap is empty. This becomes the first item on the heap, and then 7 is checked: &lt;blockquote&gt;5? []&lt;br /&gt;
&lt;div class="indented"&gt;Nope, prime&lt;/div&gt;7? [25: 25,35,55,...]&lt;br /&gt;
&lt;div class="indented"&gt;Nope, prime&lt;/div&gt;11? [25:25,35,55,... 49:49,77,91,...]&lt;br /&gt;
&lt;div class="indented"&gt;Nope, prime&lt;/div&gt;13? [25:25,35,55,... 49:49,77,91,... 121:121,143,187...]&lt;br /&gt;
&lt;div class="indented"&gt;Nope, prime&lt;/div&gt;17? [25:25,35,55,... 49:49,77,91,... 121:121,143,187... 169:169,221,247,...] &lt;br /&gt;
&lt;div class="indented"&gt;Nope, prime&lt;/div&gt;19? [25:25,35,55,... 49:49,77,91,... 121:121,143,187... 169:169,221,247,...  ...] &lt;br /&gt;
&lt;div class="indented"&gt;Nope, prime&lt;/div&gt;23? [25:25,35,55,... 49:49,77,91,... 121:121,143,187... 169:169,221,247,...  ...] &lt;br /&gt;
&lt;div class="indented"&gt;Nope, prime&lt;/div&gt;25? [25:25,35,55,... 49:49,77,91,... 121:121,143,187... 169:169,221,247,...  ...]&lt;br /&gt;
&lt;div class="indented"&gt;Aha! Matches the top of our heap...not prime&lt;/div&gt;29? [35:35,55,65,... 49:49,77,91,... 121:121,143,187... 169:169,221,247,...  ...]&lt;br /&gt;
&lt;div class="indented"&gt;Nope, prime&lt;/div&gt;...&lt;/blockquote&gt;&lt;br /&gt;
Make sense? This is the algorithm straight from the paper, so if my explanation is lacking you could always try there. When I write it down this way, however, an optimization springs out at me - why are we using a heap at all? At any given time we are querying for a single value, and don't actually care about the other items. Instead of updating a heap structure all the time, we can simply use a hash table instead. Strictly speaking a multi-way hash table, since some values will have multiple prime factors. &lt;br /&gt;
&lt;h2&gt;Version 2: Using a hash table&lt;/h2&gt;&lt;u&gt;Language:&lt;/u&gt; Python&lt;u&gt;&lt;br /&gt;
Time complexity:&lt;/u&gt;&lt;b&gt; &lt;/b&gt;O(n&amp;#8729;log(log(n)))&lt;br /&gt;
&lt;u&gt;Space complexity&lt;/u&gt;&lt;b&gt;: &lt;/b&gt;O(&amp;#8730;n/log(n))&lt;br /&gt;
&lt;u&gt;Candidates in 1 sec:&lt;/u&gt; ~1,400,000 &lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://gist.github.com/320271#file_v2.py"&gt;Code&lt;/a&gt; &lt;a href="javascript:expandContract('v2.py')"&gt;(expand/contract)&lt;/a&gt;&lt;br /&gt;
&lt;div id="v2.py" class="expandable"&gt;&lt;script src="http://gist.github.com/320271.js?file=v2.py"&gt;&lt;/script&gt;&lt;/div&gt;&lt;br /&gt;
Not bad, under 50 lines of code and with good complexity. This is as far as I know how to go with Python, and it is a dynamic language anyways which isn't really suited to this kind of number crunching, so for the next version I'm switching to C#. There I know the time required for most operations, and more importantly, I have a good profiler I know how to use. &lt;br /&gt;
&lt;h2&gt;Version 3: Re-implement in C#&lt;/h2&gt;&lt;u&gt;Language:&lt;/u&gt; C#&lt;u&gt;&lt;br /&gt;
Time complexity:&lt;/u&gt;&lt;b&gt; &lt;/b&gt;O(n&amp;#8729;log(log(n)))&lt;br /&gt;
&lt;u&gt;Space complexity&lt;/u&gt;&lt;b&gt;: &lt;/b&gt;O(&amp;#8730;n/log(n))&lt;br /&gt;
&lt;u&gt;Candidates in 1 sec:&lt;/u&gt; ~10,000,000 &lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://gist.github.com/320271#file_v3.cs"&gt;Code&lt;/a&gt; &lt;a href="javascript:expandContract('v3.cs')"&gt;(expand/contract)&lt;/a&gt;&lt;br /&gt;
&lt;div id="v3.cs" class="expandable"&gt;&lt;script src="http://gist.github.com/320271.js?file=v3.cs"&gt;&lt;/script&gt;&lt;/div&gt;&lt;br /&gt;
Wow, significant speedup there! And with a modest 25 line code hit (plus a main method for convenience), that is certainly reasonable. A little profiling tells me that the bottleneck is the list operations, which isn't too much of a surprise. Of course, C#'s native list implementation isn't necessarily going to be the fastest for our exact situation - as is often the case when optimizing, the next step is to write our own data structure. Here I use a dead simple data structure, essentially just an array with an Add operation that dynamically resizes it. &lt;br /&gt;
&lt;h2&gt;Version 4: Use a custom structure for the 'multi' in multi dictionary&lt;/h2&gt;&lt;u&gt;Language:&lt;/u&gt; C#&lt;u&gt;&lt;br /&gt;
Time complexity:&lt;/u&gt;&lt;b&gt; &lt;/b&gt;O(n&amp;#8729;log(log(n)))&lt;br /&gt;
&lt;u&gt;Space complexity&lt;/u&gt;&lt;b&gt;: &lt;/b&gt;O(&amp;#8730;n/log(n))&lt;br /&gt;
&lt;u&gt;Candidates in 1 sec:&lt;/u&gt; ~12,000,000 &lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://gist.github.com/320271#file_v4.cs"&gt;Code&lt;/a&gt; &lt;a href="javascript:expandContract('v4.cs')"&gt;(expand/contract)&lt;/a&gt;&lt;br /&gt;
&lt;div id="v4.cs" class="expandable"&gt;&lt;script src="http://gist.github.com/320271.js?file=v4.cs"&gt;&lt;/script&gt;&lt;/div&gt;&lt;br /&gt;
Not bad, a modest speedup in this version. But I must admit, I cheated a little. The main benefit of this new data structure is not the speedup gained at this step. No, the real speedup is allowing an essentially free Clear operation. Because as useful as a hash table is, we can re-write it too to get even more of a speedup. The idea here is that most of our current iterators will have a 'next' value that differ by at most &amp;#8730;n or so, so a circular array will be better for cache locality, remove the cost of hashing, and let us re-use the 'multi' objects. Note that a circular array is essentially a giant array, except only a small sliding window is actually backed by memory and allowed to contain values at any given time. &lt;br /&gt;
&lt;h2&gt;Version 5: Use a multi cyclic array instead of hash table&lt;/h2&gt;&lt;u&gt;Language:&lt;/u&gt; C#&lt;u&gt;&lt;br /&gt;
Time complexity:&lt;/u&gt;&lt;b&gt; &lt;/b&gt;O(n&amp;#8729;log(log(n)))&lt;br /&gt;
&lt;u&gt;Space complexity&lt;/u&gt;&lt;b&gt;: &lt;/b&gt;O(&amp;#8730;n)&lt;br /&gt;
&lt;u&gt;Candidates in 1 sec:&lt;/u&gt; ~20,000,000 &lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://gist.github.com/320271#file_v5.cs"&gt;Code&lt;/a&gt; &lt;a href="javascript:expandContract('v5.cs')"&gt;(expand/contract)&lt;/a&gt;&lt;br /&gt;
&lt;div id="v5.cs" class="expandable"&gt;&lt;script src="http://gist.github.com/320271.js?file=v5.cs"&gt;&lt;/script&gt;&lt;/div&gt;&lt;br /&gt;
Ok, that is about as far as I'm willing to go. Beyond this point I expect I'll start getting into low-benefit, high-complexity optimizations, and I don't want to go down that road. Although by some counts I am already there - from version 3 there has been a 2x speedup, but at the cost of doubling the code size and ending up with algorithms that fall firmly within the realm of 'tricky'. If I actually had to maintain a prime sieve in a professional setting that wasn't absolutely time critical, I think I would be going with version 3 - the later code starts to impose too much of a mental tax. &lt;br /&gt;
&lt;br /&gt;
And there you have it, a reasonably efficient Sieve of Eratosthenes, and for the most part without 'tricky' optimizations. Let me know what you think! &lt;br /&gt;
&lt;h2&gt;Postscript&lt;/h2&gt;In the comments, Isaac was wondering how these compared to "vanilla array" implementations (essentially, pre-allocate the array and cross-off), so I have added two array implementations to GitHub (&lt;a href=http://gist.github.com/320271#file_array1.py&gt;Python&lt;/a&gt;, &lt;a href=http://gist.github.com/320271#file_array2.cs&gt;C#&lt;/a&gt;) for comparision. Both are about 4 times faster than the comparative best in their language, testing 5.5 million and 75 million candidates in one second (respectively). The Python version runs out of memory somewhere before 500 million candidates, and the C# version can't get beyond about 2 billion due to limitations of the BitArray class.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-3027817597499477553?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/JFp_bNDnJAQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/3027817597499477553/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2010/03/writing-efficient-seive-of-eratosthenes.html#comment-form" title="10 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/3027817597499477553?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/3027817597499477553?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/JFp_bNDnJAQ/writing-efficient-seive-of-eratosthenes.html" title="Writing an efficient Sieve of Eratosthenes" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><thr:total>10</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2010/03/writing-efficient-seive-of-eratosthenes.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0YNSXg4eyp7ImA9WxBbGE0.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-8407591851905561974</id><published>2010-02-20T01:40:00.005-05:00</published><updated>2010-03-17T00:46:38.633-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-03-17T00:46:38.633-04:00</app:edited><title>Visualizing RGB, take two</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://allrgb.com/barcelona" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S393hgdd98I/AAAAAAAAD3c/smS15rdghuM/s320/2_allRGBv2_big_downsampled.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;Update:&lt;/b&gt; The follow-up post can be found &lt;a href=http://www.thelowlyprogrammer.com/2010/03/visualizing-rgb-take-two-update.html&gt;here&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
About 3 weeks ago, I &lt;a href="http://www.thelowlyprogrammer.com/2010/01/visualizing-rgb.html"&gt;wrote about&lt;/a&gt; a program I created to transform pictures into all-RGB images (4096x4096 images using each possible RGB colour exactly once). It worked by ordering pixels based on a Hilbert ordering of the 3d colour cube and then re-colouring them in order, and while it produced interesting images, it pretty much failed at the stated goal of “keep[ing] the overall look untouched”. The problem was that the general hue of the pixels was often very shifted from the input, so the overall features were preserved but the colour balance was not. So for the past week or so I’ve been working on a new program, one that will (hopefully!) do a better job of keeping the base look intact. As with last time, I’m using an image I took in Barcelona for testing – let me know if you have a different one you’d like to see.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_4uUxYbT6E0Q/S3-B5LqQ7uI/AAAAAAAAD4M/YwDGf-g10zw/s1600-h/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://2.bp.blogspot.com/_4uUxYbT6E0Q/S3-B5LqQ7uI/AAAAAAAAD4M/YwDGf-g10zw/s200/2.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://4.bp.blogspot.com/_4uUxYbT6E0Q/S2UdRBAo-_I/AAAAAAAAD2I/1EvJZ5nBfyM/s1600-h/2_allrgbify_small.png" imageanchor="1" style="margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="200" src="http://4.bp.blogspot.com/_4uUxYbT6E0Q/S2UdRBAo-_I/AAAAAAAAD2I/1EvJZ5nBfyM/s200/2_allrgbify_small.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div width="400px"&gt;&lt;div style="float: left; text-align: center; width: 200px;"&gt;Original&lt;/div&gt;&lt;div style="float: right; text-align: center; width: 200px;"&gt;Result From Take One&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;Choose the closest colour…&lt;/h2&gt;My idea this time was that instead of choosing an ordering of the pixels, it would be better to try to minimize the distance between the source and destination colours overall. The easiest way I could think of was to simply choose pixels at random, and assign them the “closest” colour remaining. Hopefully deviations would occur in all directions equally, so the average colour of a region would be as close as possible to the source. By popular demand, I will try to make this algorithm a little more explicit this time: &lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Go through the pixels in the source image in a random order. &lt;br /&gt;
&lt;ol type="a"&gt;&lt;li&gt;For each, select the closest remaining unused colour, by Euclidean distance between the coordinates in the colour space.&lt;/li&gt;
&lt;li&gt;Assign the found colour as the output colour of the pixel, and mark it used.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;h2&gt;But in which colour space?&lt;/h2&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_4uUxYbT6E0Q/S39-g9UWZcI/AAAAAAAAD4E/pdVczze0W9Y/s1600-h/colour+spaces.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://3.bp.blogspot.com/_4uUxYbT6E0Q/S39-g9UWZcI/AAAAAAAAD4E/pdVczze0W9Y/s400/colour+spaces.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;Sources: &lt;a href="http://en.wikipedia.org/wiki/File:RGB_farbwuerfel.jpg"&gt;one&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/File:Hsl-hsv_models.svg"&gt;two&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
A key question I had was which colour space would be best for preserving hues? There are a number of different “colour solids” that I could use coordinates from, with RGB being only one of many. I had a strong suspicion that something like HSL would do better than using RGB directly, but the easiest way to find out which to do a direct comparison. I tried the RGB cube as well as HSL and HSV cylinders for the comparison. My test images are presented below. &lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_4uUxYbT6E0Q/S3-B5LqQ7uI/AAAAAAAAD4M/YwDGf-g10zw/s1600-h/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://2.bp.blogspot.com/_4uUxYbT6E0Q/S3-B5LqQ7uI/AAAAAAAAD4M/YwDGf-g10zw/s200/2.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S3-B9qflWmI/AAAAAAAAD4U/-2OG2GQTHAE/s1600-h/2_allRGBv2_RGB.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S3-B9qflWmI/AAAAAAAAD4U/-2OG2GQTHAE/s200/2_allRGBv2_RGB.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="float: left; text-align: center; width: 200px;"&gt;Original&lt;/div&gt;&lt;div style="float: right; text-align: center; width: 200px;"&gt;RGB&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_4uUxYbT6E0Q/S3-CAvWr2VI/AAAAAAAAD4c/x5JrmdQ11Ug/s1600-h/2_allRGBv2_HSL.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://3.bp.blogspot.com/_4uUxYbT6E0Q/S3-CAvWr2VI/AAAAAAAAD4c/x5JrmdQ11Ug/s200/2_allRGBv2_HSL.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/_4uUxYbT6E0Q/S3-CD2O5dyI/AAAAAAAAD4k/remvw6QLI_4/s1600-h/2_allRGBv2_HSV.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://2.bp.blogspot.com/_4uUxYbT6E0Q/S3-CD2O5dyI/AAAAAAAAD4k/remvw6QLI_4/s200/2_allRGBv2_HSV.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="float: left; text-align: center; width: 200px;"&gt;HSL&lt;/div&gt;&lt;div style="float: right; text-align: center; width: 200px;"&gt;HSV&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, HSL and HSV give essentially the same results, which are both much better than RGB (look closely at the wheel wells, or the buildings in the trees on the right to see the differences). I like to think that HSV is slightly better, but I might be imagining differences that really aren’t there. Either way, I chose to use HSV for the final copy. &lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://allrgb.com/barcelona" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S393hgdd98I/AAAAAAAAD3c/smS15rdghuM/s320/2_allRGBv2_big_downsampled.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Looks good! Certainly a lot closer to the source image – I’m satisfied with this one for now. &lt;br /&gt;
&lt;h2&gt;Postscript&lt;/h2&gt;As with last time I am using a conceptually simple algorithm, however this time the implementation was considerably more difficult. The problem is that choosing the closest remaining colour to a source pixel is a hard problem to do efficiently, especially since the set of candidate colours changes at every step. I wrote the code in C# for performance this time, but I have still had to spend quite a few hours optimizing the code to get the program to finish at all. The final version can take 30+ hours to generate an image, and peak at over 4 GB of ram. I based my code around a &lt;a href="http://www.cs.wlu.edu/%7Elevy/software/kd/"&gt;KD-tree I found&lt;/a&gt; online, then rewrote to optimize for the 3D, single-nearest-neighbour case as well as to support branch pruning on delete. The rewritten tree – as well as the rest of my code – is available in a repository on GitHub: &lt;a href="http://github.com/EricBurnett/AllRGBv2"&gt;http://github.com/EricBurnett/AllRGBv2&lt;/a&gt;. Feel free to try it out for yourself - if you do, I’d love to hear about it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-8407591851905561974?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/O_yIJ6CIUVg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/8407591851905561974/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2010/02/visualizing-rgb-take-two.html#comment-form" title="8 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/8407591851905561974?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/8407591851905561974?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/O_yIJ6CIUVg/visualizing-rgb-take-two.html" title="Visualizing RGB, take two" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S393hgdd98I/AAAAAAAAD3c/smS15rdghuM/s72-c/2_allRGBv2_big_downsampled.png" height="72" width="72" /><thr:total>8</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2010/02/visualizing-rgb-take-two.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C04FRHY8fSp7ImA9WxBVEE8.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-2507128517942818763</id><published>2010-02-12T19:45:00.000-05:00</published><updated>2010-02-12T19:45:15.875-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-12T19:45:15.875-05:00</app:edited><title>Buzz: The perfect spam platform?</title><content type="html">Buzz is out, genius or a privacy fiasco or just another me-too, depending on your view. Those topics have been covered to death already, but one thing I haven't seen talked about is how easy it makes spamming. And I don't mean shouting inanities to all your friends - that's what it's for, after all - I'm talking about targeted spam by spammers, like the blog spam that used to be a huge problem.&lt;br /&gt;
&lt;br /&gt;
Here is the problem as I see it:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;You can follow anyone you want simply by adding their email address.&lt;/li&gt;
&lt;li&gt;When they "buzz", you are notified.&lt;/li&gt;
&lt;li&gt;When you comment, &lt;i&gt;they see your response&lt;/i&gt;.&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
Does this seem like a bad idea to anyone else? I have a feeling that Google is going to have to allow people to "accept" followers, bringing it that much closer to Facebook.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-2507128517942818763?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/q3XcWdLu6rI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/2507128517942818763/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2010/02/buzz-perfect-spam-platform.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/2507128517942818763?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/2507128517942818763?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/q3XcWdLu6rI/buzz-perfect-spam-platform.html" title="Buzz: The perfect spam platform?" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2010/02/buzz-perfect-spam-platform.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEEMRHo4cSp7ImA9WxBVEE0.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-3251657052186216410</id><published>2010-02-12T15:31:00.000-05:00</published><updated>2010-02-12T15:31:25.439-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-12T15:31:25.439-05:00</app:edited><title>Straining the limits of C#</title><content type="html">Two weeks ago I &lt;a href="http://www.thelowlyprogrammer.com/2010/01/visualizing-rgb.html"&gt;wrote about&lt;/a&gt; an algorithm to generate All-RGB images from pictures. I am currently working on a follow-up post about a new algorithm, in C# this time. This one is a bit more computationally intensive, and despite the language change it is running into scaling issues. So while I wait for it to finish, I thought I'd write about a few of them.&lt;br /&gt;
&lt;h2&gt;Good data structures are hard to find&lt;/h2&gt;When you start processing large numbers of items in different ways, choosing the right data structure to store them becomes an absolute necessity. They can mean the difference between an O(n&amp;#8729;log(n)) and an O(n&lt;sup&gt;2&lt;/sup&gt;) and algorithm, which can be the difference between taking 1 hour to run or 100 years. For this project, the requirements were simple – a data structure mapping points to objects that supported nearest-neighbour searches and deletion. To me, that immediately translated to &lt;a href="http://en.wikipedia.org/wiki/Kd-tree"&gt;kd-tree&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Usually in cases like this I end up needing to roll my own structure, but this time I was lucky. After some Googling I found exactly &lt;a href="http://www.cs.wlu.edu/~levy/software/kd/"&gt;one&lt;/a&gt; viable implementation to use, and better yet, it was open source. I'm glad it was; it turned out later that there was a bug&lt;sup&gt;&lt;a href="#KDTreeBug"&gt;1&lt;/a&gt;&lt;/sup&gt; that needed fixing, and I needed to compile a 64-bit version anyways (I wonder if there's a lesson in here?). It is unfortunate that this was the only option, however. I mean, there are a ton of data structure libraries for most languages you can imagine, but the vast majority of them implement the same set of structures, are buggy, unsupported, and incompatible. I would love to see a Stack Overflow-style site to address this – community edited and supported code, implementations ranked together for comparison, searching by requirements if you don't know what you need, and the list goes on.&lt;br /&gt;
&lt;br /&gt;
But even with the appropriate structure, the algorithm I have chosen will take more than a day to run and  4+ GB of memory. That is fine, I knew the approximate complexity when I started, but it does lead to the next set of issues.&lt;br /&gt;
&lt;h2&gt;Good algorithms are hard to find&lt;/h2&gt;Or should I say, good implementations of algorithms are hard to find. By way of introduction, a brief digression: my computer is unstable. Not terribly unstable, not enough to for me to actually take the time to fix it, but my sound card is slightly unsupported on Windows 7 so every once in a blue moon something like skipping a song will blue-screen the computer. Just about all my programs know how to pick up where they left off, but of course that doesn't hold for these projects I throw together in an evening. So when my computer decided to crash this morning, I decided to add some basic checkpointing. Checkpointing is easy, right? Hah!&lt;br /&gt;
&lt;br /&gt;
&lt;u&gt;Attempt 1&lt;/u&gt;: tag classes with [Serializable], run needed structures through a BinaryFormatter, streaming to file.&lt;br /&gt;
So, anyone want to guess what the problem is here? If you said object-graph size, you're right on the money. BinaryFormatter doesn't support object graphs with more than 6M items or so, and arrays get no special treatment. So serializing an array of 16.7M items throws a very unhelpful error message (&amp;quot;The internal array cannot expand to greater than Int32.MaxValue elements&amp;quot;)&lt;sup&gt;&lt;a href="#BinaryFormatterBug"&gt;2&lt;/a&gt;&lt;/sup&gt;. Fine, I can unwrap my own arrays easily enough.&lt;br /&gt;
&lt;br /&gt;
&lt;u&gt;Attempt 2&lt;/u&gt;: unwrap arrays manually.&lt;br /&gt;
With each array element being serialized as a separate object, the overhead in the file is huge. If I had to guess, I'd say that the size on disk is about 10 times the size in memory. And since I'm trying to write about 1 GB of data...you can probably guess where this is going. Something in the output stack explodes when more than 4 GB of data is written, a number suspiciously close to the max size of an Int32. This is simply poor implementation, since it's not like I'm trying to mmap the data, and large files have been supported in all modern OS' for years. Not a big deal though, that data is going to be very redundant and I/O is expensive, so writing a compressed stream is probably faster in the long run.&lt;br /&gt;
&lt;br /&gt;
&lt;u&gt;Attempt 3&lt;/u&gt;: write to the file using a System.IO.Compression.GZipStream wrapper.&lt;br /&gt;
With compressed data, I expect the on-disk size to be comparable to the in-memory size, or a bit better. So the 4 GB limit should be no, problem, right? Wrong! The GZipStream has the same problem, and refuses to support more than 4 GB uncompressed. The fix here is even simpler – swap in a better GZip library.&lt;br /&gt;
&lt;br /&gt;
&lt;u&gt;Attempt 4&lt;/u&gt;: write to the file using a &lt;a href=http://www.icsharpcode.net/OpenSource/SharpZipLib/&gt;SharpZipLib&lt;/a&gt;.GZipOutputStream wrapper.&lt;br /&gt;
Success! The output file is about 700 MB and takes somewhere around 20 minutes to write, for a compression rate of about 9 MB/sec and space savings of about 93%.&lt;br /&gt;
&lt;br /&gt;
Now, I could chalk these problems up as a failing of C#, but that wouldn't be accurate. By playing with this much data I am working outside the limits expected by the library designers, and I know it. I have focused on C#, but the issues are far more general than that – I can't even find a 64-bit version of python 2.6 for Windows to test with at all, but I'm sure I would run into a different set of problems if I could use it, and the same goes for the rest of the languages out there. The real issue is that versatile algorithm implementation is &lt;i&gt;hard&lt;/i&gt;, and not getting much easier with time. And that I don't have a workaround for.&lt;br /&gt;
&lt;h2&gt;&lt;span style="color: rgb(102, 102, 102);" &gt;&lt;br /&gt;
Footnotes&lt;/span&gt;&lt;/h2&gt;&lt;ol&gt; &lt;li&gt;&lt;a name=KDTreeBug&gt;&lt;/a&gt;The problem is that &amp;quot;deletions&amp;quot; are supported by tombstoning, so you periodically have to rebuild the index to clean them up. That is fine, except the range() method used to get the current entries out doesn't check the deleted flag! Don't worry, I'll be sending a fix back upstream.&lt;/li&gt;
 &lt;li&gt;&lt;a name=BinaryFormatterBug&gt;&lt;/a&gt;Someone else did the digging, and it seems the list of prime sizes for some internal hash table stops at 6 million, so the next prime it tries to resize to is something enormous (-1 unsigned?). Microsoft decided this is a non-issue, so no fix is coming. Their suggested workaround was to use the NetDataContractSerializer and a binary xml writer, but when I tested it the performance was too terrible to consider.&lt;/li&gt;
&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-3251657052186216410?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/ojURgceMKqc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/3251657052186216410/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2010/02/straining-limits-of-c.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/3251657052186216410?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/3251657052186216410?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/ojURgceMKqc/straining-limits-of-c.html" title="Straining the limits of C#" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2010/02/straining-limits-of-c.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CU4HSHg-cSp7ImA9WxBbGEU.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-3628252323629566646</id><published>2010-01-31T02:50:00.008-05:00</published><updated>2010-03-17T23:45:39.659-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-03-17T23:45:39.659-04:00</app:edited><title>Visualizing RGB</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://allrgb.com/mandelbrot" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_4uUxYbT6E0Q/S2XUfMWXRZI/AAAAAAAAD2w/H4htF_Giy7c/s320/10_allrgbify_downsampled.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;Update:&lt;/b&gt; See the follow-up post &lt;a href=http://www.thelowlyprogrammer.com/2010/02/visualizing-rgb-take-two.html&gt;here&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://corte.si/index.html"&gt;Aldo Cortesi&lt;/a&gt; posted a link today to &lt;a href="http://allrgb.com/"&gt;allrgb.com&lt;/a&gt;, a site dedicated to images visualizing the RGB colour space - in particular, 4096x4096 images that use each RGB value exactly once. Inspired by his &lt;a href="http://corte.si/posts/code/hilbert/portrait/index.html"&gt;hilbert curve visualization&lt;/a&gt; and the urge to spend a day programming, I present to you: the all-RGB Mandelbrot set.&lt;br /&gt;
&lt;h2&gt;Sort by colour...&lt;/h2&gt;My idea was this: instead of trying to visualize the colour space directly, why not use a base image for the "shape", and then map the RGB spectrum onto it? I thought that if I could find an image with an even spread of colours, this would let me make each pixel unique yet keep the overall look untouched.&lt;br /&gt;
To perform this mapping, I chose to define an ordering based on the 3 dimensional Hilbert curve. &lt;a href="http://corte.si/posts/code/hilbert/portrait/index.html"&gt;Cortesi explains it&lt;/a&gt; far better than I can, but the basic idea is this: the Hilbert curve can be used to find an ordering of all 16.8 million colours so that if you were to stretch them out on a line, every colour would be there and they would flow smoothly from one to another. Like this, except a lot smoother and a &lt;i&gt;lot&lt;/i&gt; longer.&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S2Utg-jmFkI/AAAAAAAAD2Y/mXBYuE9ve8E/s320/hilbertLine.png" /&gt;&lt;/div&gt;With this ordering in hand it is easy to find the index into this line for each pixel in the source image, sort the pixels, and then assign them the colour they line up with.&lt;br /&gt;
&lt;h2&gt;Choosing an image&lt;/h2&gt;When I started this morning, I had the idea that the output images would look reasonably close to the source images. I was half right; the images certainly have all the same features as before, but the colouring is all wrong. In hindsight the reason is obvious - unless the original had a perfectly even spectrum of colours, the mapping would be stretched in some places and shifted in others, and in general not line up nicely.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S2UdPmwHHaI/AAAAAAAAD2A/Ur5UVY8ZvCE/s1600-h/2_small.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://1.bp.blogspot.com/_4uUxYbT6E0Q/S2UdPmwHHaI/AAAAAAAAD2A/Ur5UVY8ZvCE/s200/2_small.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://4.bp.blogspot.com/_4uUxYbT6E0Q/S2UdRBAo-_I/AAAAAAAAD2I/1EvJZ5nBfyM/s1600-h/2_allrgbify_small.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://4.bp.blogspot.com/_4uUxYbT6E0Q/S2UdRBAo-_I/AAAAAAAAD2I/1EvJZ5nBfyM/s200/2_allrgbify_small.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
While interesting, this wasn't exactly what I was going for. Hmm...what image could I use where it wouldn't matter if the colours were all shifted? The first thing that came to mind was a visualization like the Mandelbrot set, where the colours are arbitrarily chosen anyways. A quick Google search found me &lt;a href="http://gwydir.demon.co.uk/jo/numbers/interest/i.htm#mandel"&gt;this&lt;/a&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://gwydir.demon.co.uk/jo/numbers/interest/i.htm#mandel" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="233" src="http://4.bp.blogspot.com/_4uUxYbT6E0Q/S2UyRXjHnII/AAAAAAAAD2g/QuPcW5eoguA/s400/mandelbrot.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
Which, when transformed, came out as this:&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://allrgb.com/mandelbrot" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_4uUxYbT6E0Q/S2XUfMWXRZI/AAAAAAAAD2w/H4htF_Giy7c/s320/10_allrgbify_downsampled.png" /&gt;&lt;/a&gt;&lt;/div&gt;Perfect!&lt;br /&gt;
&lt;h2&gt;Postscript&lt;/h2&gt;I created all the visualizations here using Python 2.6 and the Python Imaging Library. It isn't the most efficient code (it takes half an hour to render a 4096x4096 image), but the quick development time easily makes up for it. If anyone is interested in playing with it, I have placed the code &lt;a href=http://gist.github.com/300780&gt;on github&lt;/a&gt;. Or if you just want to see what something would look like, feel free to &lt;a href="mailto:ericburnett@gmail.com"&gt;send it to me&lt;/a&gt; and I'd be happy to run it for you.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-3628252323629566646?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/b9M9uM5veSI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/3628252323629566646/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2010/01/visualizing-rgb.html#comment-form" title="6 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/3628252323629566646?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/3628252323629566646?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/b9M9uM5veSI/visualizing-rgb.html" title="Visualizing RGB" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_4uUxYbT6E0Q/S2XUfMWXRZI/AAAAAAAAD2w/H4htF_Giy7c/s72-c/10_allrgbify_downsampled.png" height="72" width="72" /><thr:total>6</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2010/01/visualizing-rgb.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0QHRHk8fSp7ImA9WxBXGEo.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-2872458671001020305</id><published>2010-01-30T14:22:00.000-05:00</published><updated>2010-01-30T14:22:15.775-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-01-30T14:22:15.775-05:00</app:edited><title>Moved!</title><content type="html">&lt;p&gt;This blog has moved! It now lives at &lt;a href=http://www.thelowlyprogrammer.com&gt;www.thelowlyprogrammer.com&lt;/a&gt;. As is probably obvious, I have also changed the name to &lt;i&gt;The Lowly Programmer&lt;/i&gt; (see the updated &lt;a href=http://www.thelowlyprogrammer.com/2008/05/about-me.html&gt;about me&lt;/a&gt; for details). Nothing else is changing at the moment; in particular, I doubt I will be posting to it with any regularity yet :).&lt;br /&gt;
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-2872458671001020305?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/Ai7aHTaIOxg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/2872458671001020305/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2010/01/moved.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/2872458671001020305?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/2872458671001020305?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/Ai7aHTaIOxg/moved.html" title="Moved!" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2010/01/moved.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUQBRX0-fSp7ImA9WxBQFko.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-2343174528989396499</id><published>2010-01-16T16:29:00.000-05:00</published><updated>2010-01-16T16:29:14.355-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-01-16T16:29:14.355-05:00</app:edited><title>The value of a point</title><content type="html">&lt;span xmlns=''&gt;&lt;p&gt;Recently I have been spending a lot of time on &lt;a href="http://news.ycombinator.com/"&gt;Hacker News&lt;/a&gt;. I've lurked for quite a while, but it is only in the last month or so that I have actively started to comment. I usually try for informative comments rather than going for the inflammatory or popular topics. This only gets me a &lt;a href="http://www.randsinrepose.com/archives/2009/12/13/gaming_the_system.html"&gt;point&lt;/a&gt; or two per comment I post, but these are points I have &lt;i&gt;earned&lt;/i&gt; by contributing to the discussion in a positive way. I had almost reached 50 points this morning and was feeling quite good about my contributions overall.&lt;br /&gt;
&lt;/p&gt;&lt;p&gt;But then I made a submission.&lt;br /&gt;
&lt;/p&gt;&lt;p&gt;Most of my submissions go the way of my comments, earning with a point or two plus some interesting responses. It's these responses I am after; I find an interesting article somewhere, so I submit it to see what the HN community has to say about it. But &lt;a href="http://news.ycombinator.com/item?id=1057133"&gt;this one&lt;/a&gt;, for whatever reason, the community really appreciated, and within 20 minutes it was at the top of the front page. The discussion was lively and as interesting to read as I could hope for, and it was raking in the points.&lt;br /&gt;
&lt;/p&gt;&lt;p&gt;And therein lies the problem. Someone, a self proclaimed member of HN no less, took the time and effort to write a thought provoking piece and there I was earning scores of points off it, which left me feeling like a bit of a cheat. But even had I been the original author, I don't think I would want that many points for it. By the time it falls off the front page it will likely be over 50 points itself, surpassing a month's effort in one fell swoop and devaluing the points accordingly. I was looking at points as a general reflection of my value to the site, but I cannot do that anymore, at least not directly.&lt;br /&gt;
&lt;/p&gt;&lt;p&gt;So what can be done about the 'problem' with points? I can think of a few suggestions.&lt;br /&gt;
&lt;ol start="1" style="margin-top: 0in;" type="1"&gt;&lt;li&gt;Nothing. After all, I am only a single opinion, and the system seems to be working fine as it is. &lt;/li&gt;
&lt;li&gt;Don't count any points past a certain cutoff. They would still be useful for display and ranking, but for karma the extra would be discarded. I kind of like this idea, but any cutoff would be an arbitrary one. &lt;a href="http://stackoverflow.com/"&gt;Stack Overflow&lt;/a&gt; uses a system like this, although there it's points per day rather than per action.&lt;/li&gt;
&lt;li&gt;Count points on a log scale. Display them as normal, but change the way karma is calculated. This would help address crazy outliers like &lt;a href="http://news.ycombinator.com/item?id=1048800"&gt;this one&lt;/a&gt;, and in general promote sustained quality over hot-button topics (for people actively trying for points).&lt;/li&gt;
&lt;/ol&gt;I know &lt;a href="http://www.paulgraham.com/"&gt;pg&lt;/a&gt; is always tweaking the system to make it work better; it will be interesting to see what, if anything, actually changes in the next while.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-2343174528989396499?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/xt2NtZNgcXE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/2343174528989396499/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2010/01/value-of-point.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/2343174528989396499?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/2343174528989396499?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/xt2NtZNgcXE/value-of-point.html" title="The value of a point" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2010/01/value-of-point.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEcDR3w8cCp7ImA9WxJXEko.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-6239963055035128102</id><published>2009-06-06T04:13:00.001-04:00</published><updated>2009-06-06T04:14:36.278-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-06T04:14:36.278-04:00</app:edited><title>Google Wave. Can you imagine?</title><content type="html">&lt;span xmlns=''&gt;&lt;p&gt;I'm sure you've all heard of &lt;a href='http://wave.google.com/'&gt;Google Wave&lt;/a&gt; by now, but for those who haven't, think of a hybrid email-chat-collaboration program all rolled into one. Participants can message back and forth on their own time like email, but it's also much more than that. As you type, your keystrokes are streamed in real-time to everyone else, letting them read what you write &lt;em&gt;as you write it&lt;/em&gt;. Couple that with the ability to comment and edit others' comments, and what do you get? I'm not sure, but we'll find out soon enough.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;I don't have any earth-shattering insights about how this product is going to revolutionize how we communicate (or fail completely), but I would like you to consider one scenario. Imagine a group of Digg (or 4chan or reddit or...) users, all getting onto a wave at once. They'd be able to fight for the top, change each others' comments to include &lt;em&gt;even more memes&lt;/em&gt;, spam, complain, and of course mock, and all in real-time. Pedobears would be flying, grammar would be mutilated in the name of speed, and still people would be replying in anger to posts not even finished. Would it be fun? Quite possibly. Carnage? Undoubtedly.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Welcome to the internet, Wave.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-6239963055035128102?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/ZGPg0WFIdQw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/6239963055035128102/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2009/06/google-wave-can-you-imagine.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/6239963055035128102?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/6239963055035128102?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/ZGPg0WFIdQw/google-wave-can-you-imagine.html" title="Google Wave. Can you imagine?" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2009/06/google-wave-can-you-imagine.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEANR307fSp7ImA9WxRSFkQ.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-2404775663018489669</id><published>2008-09-17T20:10:00.002-04:00</published><updated>2008-09-17T20:13:16.305-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-17T20:13:16.305-04:00</app:edited><title>How fast do you type?</title><content type="html">&lt;span xmlns=''&gt;&lt;p&gt;According to &lt;a href='http://www.typingtest.com'&gt;typingtest.com&lt;/a&gt;, I can type at 58 words per minute with 97% accuracy, which is probably about average among my roommates. But that is &lt;em&gt;words&lt;/em&gt; per minute; I am a programmer, I spend as much time typing code filled with hyphens and tildes and ampersands as typing "words". And I must admit, I never learned to touch-type type numbers or symbols, so typing code involves a lot of glancing down at the keyboard to find out exactly where a symbol is. Try it yourself – here is a string I find myself typing fairly often during a day at work (and no, it isn't confidential!):&lt;br /&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p style="font-family: sans-serif"&gt;dbisql.exe –c "UID=dba;PWD=123;ENG=myEngine;DBF=..\testdb.db"&lt;br /&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;How quick can you type that? And I will point out that this string doesn't even have any really difficult characters, just lots of punctuation. &lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;a href='http://steve-yegge.blogspot.com'&gt;Steve Yegge&lt;/a&gt; wrote a really good rant recently on the subject of typing (or as he calls it, &lt;a href='http://steve-yegge.blogspot.com/2008/09/programmings-dirtiest-little-secret.html'&gt;Programming's Dirtiest Little Secret&lt;/a&gt;), and it got me thinking. As good as I am at typing normal text, I fall apart when I run into anything else. Before reading his rant, I would never have thought of putting practicing typing on my professional development list, despite the fact that it will serve me better than any of the books I'd read or languages I'd learn. But right now I am going to put learning python on hold (again) and spend some quality time with my old friend Mavis Beacon.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-2404775663018489669?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/MYZKhzXkmc8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/2404775663018489669/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2008/09/how-fast-do-you-type.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/2404775663018489669?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/2404775663018489669?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/MYZKhzXkmc8/how-fast-do-you-type.html" title="How fast do you type?" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2008/09/how-fast-do-you-type.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEcHRng5fyp7ImA9WxdREko.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-1658695802214436730</id><published>2008-05-31T18:50:00.004-04:00</published><updated>2008-05-31T19:20:37.627-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-05-31T19:20:37.627-04:00</app:edited><title>What’s next for copyright?</title><content type="html">&lt;span xmlns=''&gt;&lt;p&gt;Everyone knows there is a copyright war going on right now. Big companies own the copyright to virtually all music, movies, and television shows, and they want to make large profits off these rights. People, on the other hand, want to be able to play their media when and where they want, and ideally they'd like to get it for free. This conflict shows up in a number of guises – battles over internet privacy, digital rights management, and bittorrent, to name a few – and shows no signs of being resolved any time soon. I do think there is hope for a solution, but the industry needs to accept that they can't control everything.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;To see it, let's take a look at one website that ignores copyright and gets away with it: YouTube. Users can upload and watch virtually anything, and due to the sheer number of uploads, copyright holders can't keep up. It survives because there is a big company protecting it from lawsuits, but that isn't why it's popular. The biggest feature here is ease of use – users go to YouTube because it is the best way to get the content they're looking for. YouTube already makes lots of money; if publishers were to learn from this, I think they'd have a winning strategy.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Of course, the challenge is to turn this example into a general solution. I would say that big publishers can do it, but they need to change their business model.  What they should do is try to control the brand and be the best for distribution, to make it as easy as possible for people to get the content they want. I'll give a couple examples of how I think this could go.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;I picture a website much like YouTube, but controlled by a publisher. Users can visit it to watch movies and TV shows for free, at their own leisure. To fund this they use the tried and true internet model: advertising. Most of it is general advertising, keyed to the content in the movie, and the rest is good for the publisher – collector's edition DVDs, upcoming movies with the same director, etcetera. Sure, people still pass movies around on bittorrent, but this is quick and convenient. Then persecuting people who share files isn't really necessary, since many of them will make it to the publisher's site on their own. Similarly, people won't fight as hard to protect other sites hosting the content, since there is an easy, legal, and free channel to get the same. Naturally this wouldn't be a replacement for physical media, but the shows will be on the internet regardless of what publishers want, so they may as well get a slice out of it.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;For music, this could go even further. A music company could run their own download site or tracker, putting up the content themselves. It would be high quality &lt;em&gt;official&lt;/em&gt; content, for free. They could try to get users to pay if they like the music, buy physical copies, etcetera. Remind you of anything? &lt;a href='http://www.techcrunch.com/2007/10/04/the-inevitable-march-of-recorded-music-towards-free/'&gt;Radiohead has done this&lt;/a&gt;. &lt;em&gt;After&lt;/em&gt; this is in place, they could go after other distributors, to keep people coming to the 'official' site. Again, people will share the music. But who cares? It's free unless they choose to pay, and let's face it, they are going to share anyways.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;In that case, why would this kind of scheme work? A number of reasons:&lt;br /&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;It is easy to do. Laws don't need to be changed, and people won't fight it.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Profits are still good. Maybe not as high as they once were, but that industry is dying anyways. And compared to physical media, costs are virtually non-existent, so people wouldn't need to pay nearly as much piece of media for this to work.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Publishers have already lost control; they just don't know it yet. If you can't control your users, make them &lt;em&gt;want&lt;/em&gt; to come to you.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;There are lots of signs that this kind of model is coming. The biggest problem is that it isn't being done by the publishers. Either artists are breaking off and trying it themselves, or companies are springing up to sign deals and try it for themselves. Somehow we need to get the publishers to try it, and I think the whole problem of draconian copyright enforcement will slowly disappear. Or at least, it won't be the users themselves getting slapped with lawsuits.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-1658695802214436730?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/htJrufblR6s" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/1658695802214436730/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2008/05/whats-next-for-copyright.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/1658695802214436730?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/1658695802214436730?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/htJrufblR6s/whats-next-for-copyright.html" title="What’s next for copyright?" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2008/05/whats-next-for-copyright.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEUMSHY5fSp7ImA9WxdREUw.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-4189459878832255218</id><published>2008-05-29T22:16:00.005-04:00</published><updated>2008-05-29T22:58:09.825-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-05-29T22:58:09.825-04:00</app:edited><title>NOT Puzzle Solution</title><content type="html">&lt;span xmlns=""&gt;&lt;p&gt;Last week, I closed with a puzzle: "Invert three inputs using only two NOT gates." I should start off by pointing out that I cannot take credit for this problem; I believe it was first penned by Clive Maxfield just over two years ago. Check out &lt;a href="http://www.pldesignline.com/187202855"&gt;his page on it&lt;/a&gt; for a full discussion. That said, I &lt;em&gt;will&lt;/em&gt; take credit for extending it to the &lt;strong&gt;n&lt;/strong&gt; case, if you really insist.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;The solutions that follow are just two of many possible ways of looking at this. Structurally, they are by no means the simplest solutions, but I find them to be &lt;em&gt;conceptually&lt;/em&gt; the simplest. If you haven't tried to solve the puzzle yet I heartily recommend it; it makes knowing the solution so much more rewarding if you have.&lt;br /&gt;&lt;/p&gt;&lt;h2&gt;Solution for 3 inputs&lt;br /&gt;&lt;/h2&gt;&lt;p&gt;My solution to this puzzle involves reasoning on the number of inputs with value '1'. If none of the inputs are 1s, then all the outputs must be, and vice versa. To do this, we create a set of temporary variables with the goal of knowing exactly how many inputs were 1.&lt;br /&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p style="font-family: sans-serif"&gt;3 := A &amp;and; B &amp;and; C&lt;br /&gt;2or3 := (A &amp;and; B) &amp;or; (B &amp;and; C) &amp;or; (A &amp;and; C)&lt;br /&gt;0or1 := ¬ 2or3&lt;br /&gt;1 := 0or1 &amp;and; (A &amp;or; B &amp;or; C)&lt;br /&gt;0or2 := ¬ (1 &amp;or; 3)&lt;br /&gt;0 := 0or1 &amp;and; 0or2&lt;br /&gt;2 := 0or2 &amp;and; 2or3&lt;br /&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;From here we can just build our outputs directly:&lt;br /&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p style="font-family: sans-serif"&gt;X = 0 &amp;or; (1 &amp;and; (B &amp;or; C)) &amp;or; (2 &amp;and; (B &amp;and; C))&lt;br /&gt;Y = 0 &amp;or; (1 &amp;and; (A &amp;or; C)) &amp;or; (2 &amp;and; (A &amp;and; C))&lt;br /&gt;Z = 0 &amp;or; (1 &amp;and; (A &amp;or; B)) &amp;or; (2 &amp;and; (A &amp;and; B))&lt;br /&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;Solution for &lt;strong&gt;n&lt;/strong&gt; inputs&lt;br /&gt;&lt;/h2&gt;&lt;p&gt;This proof sketch is for odd &lt;strong&gt;n. &lt;/strong&gt;If &lt;strong&gt;n&lt;/strong&gt; is even, just invert the last input directly with a NOT, and use this pattern for the first &lt;strong&gt;n&lt;/strong&gt;-1.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;As before, we are going to reason on how many inputs (I&lt;sub&gt;1&lt;/sub&gt; to I&lt;sub&gt;n&lt;/sub&gt;) were 1. For this we will use a bit more syntax: {a-b} means anywhere from a to b inputs (inclusive) were 1, and {a, b} means either a &lt;em&gt;or&lt;/em&gt; b inputs were 1, with no other possibilities allowed. First, we can generate all the ranges ending in &lt;strong&gt;n&lt;/strong&gt; directly:&lt;br /&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p style="font-family: sans-serif"&gt;{1-&lt;strong&gt;n&lt;/strong&gt;} := (I&lt;sub&gt;1&lt;/sub&gt; &amp;or; I&lt;sub&gt;2&lt;/sub&gt; &amp;or; … &amp;or; I&lt;sub&gt;n&lt;/sub&gt;)&lt;br /&gt;{2-&lt;strong&gt;n&lt;/strong&gt;} := ((I&lt;sub&gt;1&lt;/sub&gt; &amp;and; I&lt;sub&gt;2&lt;/sub&gt;) &amp;or; (I&lt;sub&gt;1&lt;/sub&gt; &amp;and; I&lt;sub&gt;3&lt;/sub&gt;) &amp;or; … &amp;or; (I&lt;sub&gt;n-1&lt;/sub&gt; &amp;and; I&lt;sub&gt;n&lt;/sub&gt;))&lt;br /&gt;…&lt;br /&gt;{&lt;strong&gt;n&lt;/strong&gt;-&lt;strong&gt;n&lt;/strong&gt;} := (I&lt;sub&gt;1&lt;/sub&gt; &amp;and; I&lt;sub&gt;2&lt;/sub&gt; &amp;and; … &amp;and; I&lt;sub&gt;n&lt;/sub&gt;)&lt;br /&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Clearly, this doesn't need any NOTs. Next, we are going to make all the ranges starting in 0 and ending in an odd number:&lt;br /&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p style="font-family: sans-serif"&gt;{0-1} := ¬ {2-&lt;strong&gt;n&lt;/strong&gt;}&lt;br /&gt;{0-3} := ¬ {4-&lt;strong&gt;n&lt;/strong&gt;}&lt;br /&gt;…&lt;br /&gt;{0-(&lt;strong&gt;n&lt;/strong&gt;-2)} := ¬{(&lt;strong&gt;n&lt;/strong&gt;-1)-&lt;strong&gt;n&lt;/strong&gt;}&lt;br /&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;This requires (&lt;strong&gt;n&lt;/strong&gt;-1)/2 NOT gates. Using these together, we can have variables for all the odd numbers of inputs directly:&lt;br /&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p style="font-family: sans-serif"&gt;1 := {0-1} &amp;and; {1-&lt;strong&gt;n&lt;/strong&gt;}&lt;br /&gt;3 := {0-3} &amp;and; {3-&lt;strong&gt;n&lt;/strong&gt;}&lt;br /&gt;…&lt;br /&gt;&lt;strong&gt;n&lt;/strong&gt;-2 := {0-(&lt;strong&gt;n&lt;/strong&gt;-2)} &amp;and; {(&lt;strong&gt;n&lt;/strong&gt;-2)-&lt;strong&gt;n&lt;/strong&gt;}&lt;br /&gt;&lt;strong&gt;n&lt;/strong&gt; := {&lt;strong&gt;n&lt;/strong&gt;-&lt;strong&gt;n&lt;/strong&gt;}&lt;br /&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Again, this doesn't require any NOTs. Lastly, we use one more NOT to let us get all the even values:&lt;br /&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p style="font-family: sans-serif"&gt;{0,2,4,…,(&lt;strong&gt;n&lt;/strong&gt;-1)} := ¬ (1 &amp;or; 3 &amp;or; … &amp;or; &lt;strong&gt;n&lt;/strong&gt;)&lt;br /&gt;&lt;br /&gt;0 := {0-1} &amp;and; {0,2,4,…,(&lt;strong&gt;n&lt;/strong&gt;-1)}&lt;br /&gt;2 := {0-3} &amp;and; {0,2,4,…,(&lt;strong&gt;n&lt;/strong&gt;-1)}  &amp;and; {2-&lt;strong&gt;n&lt;/strong&gt;}&lt;br /&gt;4 := {0-5} &amp;and; {0,2,4,…,(&lt;strong&gt;n&lt;/strong&gt;-1)} &amp;and; {4-&lt;strong&gt;n&lt;/strong&gt;}&lt;br /&gt;…&lt;br /&gt;&lt;strong&gt;n&lt;/strong&gt;-1 := {0,2,4,…,(&lt;strong&gt;n&lt;/strong&gt;-1)}  &amp;and; {(&lt;strong&gt;n&lt;/strong&gt;-1)-&lt;strong&gt;n&lt;/strong&gt;}&lt;br /&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt; There! Now we have a variable for each possible number of inputs with value 1, so we can just plug in all possibilities to get our outputs:&lt;br /&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p style="font-family: sans-serif"&gt;O&lt;sub&gt;1&lt;/sub&gt; = 0 &amp;or; (1 &amp;and; (I&lt;sub&gt;2&lt;/sub&gt; &amp;or; I&lt;sub&gt;3&lt;/sub&gt; &amp;or; … &amp;or; I&lt;sub&gt;n&lt;/sub&gt;)) &amp;or; … &amp;or; (&lt;strong&gt;n&lt;/strong&gt;-1 &amp;and; (I&lt;sub&gt;2&lt;/sub&gt; &amp;and; I&lt;sub&gt;3&lt;/sub&gt; &amp;and; … &amp;and; I&lt;sub&gt;n&lt;/sub&gt;))&lt;br /&gt;O&lt;sub&gt;2&lt;/sub&gt; = 0 &amp;or; (1 &amp;and; (I&lt;sub&gt;1&lt;/sub&gt; &amp;or; I&lt;sub&gt;3&lt;/sub&gt; &amp;or; … &amp;or; I&lt;sub&gt;n&lt;/sub&gt;)) &amp;or; … &amp;or; (&lt;strong&gt;n&lt;/strong&gt;-1 &amp;and; (I&lt;sub&gt;1&lt;/sub&gt; &amp;and; I&lt;sub&gt;3&lt;/sub&gt; &amp;and; … &amp;and; I&lt;sub&gt;n&lt;/sub&gt;))&lt;br /&gt;…&lt;br /&gt;O&lt;sub&gt;n&lt;/sub&gt; = 0 &amp;or; (1 &amp;and; (I&lt;sub&gt;1&lt;/sub&gt; &amp;or; I&lt;sub&gt;2&lt;/sub&gt; &amp;or; … &amp;or; I&lt;sub&gt;n-1&lt;/sub&gt;)) &amp;or; … &amp;or; (&lt;strong&gt;n&lt;/strong&gt;-1 &amp;and; (I&lt;sub&gt;1&lt;/sub&gt; &amp;and; I&lt;sub&gt;1&lt;/sub&gt; &amp;and; … &amp;and; I&lt;sub&gt;n-1&lt;/sub&gt;))&lt;br /&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;As you have seen, this took a total of (&lt;strong&gt;n&lt;/strong&gt;-1)/2 + 1 NOT gates. Since &lt;strong&gt;n&lt;/strong&gt; is odd, (&lt;strong&gt;n&lt;/strong&gt;-1)/2 + 1 = &amp;lfloor;&lt;strong&gt;n&lt;/strong&gt;/2&amp;rfloor; + 1, and we're done! Whew, I wouldn't want to implement that.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-4189459878832255218?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/ZqCoHWCHMmk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/4189459878832255218/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2008/05/not-puzzle-solution.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/4189459878832255218?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/4189459878832255218?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/ZqCoHWCHMmk/not-puzzle-solution.html" title="NOT Puzzle Solution" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2008/05/not-puzzle-solution.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0YBRH0yfSp7ImA9WxdSFkg.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-7869519820726152618</id><published>2008-05-24T14:54:00.004-04:00</published><updated>2008-05-24T15:59:15.395-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-05-24T15:59:15.395-04:00</app:edited><title>Boolean Logic</title><content type="html">&lt;span xmlns=""&gt;&lt;p&gt;Today we discuss Boolean Logic. It may be the first in a series on different kinds of logic, or it may not. We'll see.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;A concept that should be intimately familiar to any programmer is &lt;em&gt;Boolean logic.&lt;/em&gt; True or False, 1 or 0, Yes or No, these are all different representations of a Boolean variable. Invented by George Boole in the 1800s, Boolean logic forms the basis of modern computing, used in everything from the base transistor to bitmasks and search queries. &lt;a href="http://en.wikipedia.org/wiki/Boolean_algebra"&gt;Wikipedia&lt;/a&gt; has a good introduction to the theory for anyone who needs a refresher. Rather than re-teach it, here I am going to draw your attention to a few of the more interesting features of Boolean logic.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;First off, everything can be defined from only one basic operation, &lt;em&gt;alternative denial&lt;/em&gt; (aka NAND, ↑). Alternatively, the same can be done with NOR, albeit in different patterns. For example, NOT can be created by splitting the input into both sides of a NAND gate. For this reason – and many others – NAND is often used as a building block of transistor circuits. It is a bit awkward for general use however, so for higher level Boolean logic we usually deal in three main operations: &lt;em&gt;negation&lt;/em&gt; (NOT, or ¬), &lt;em&gt;conjunction&lt;/em&gt; (AND, or ∧), and &lt;em&gt;disjunction&lt;/em&gt; (OR, or ∨). There are others, but these are good enough for now. Take a look at a few examples:&lt;br /&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;A ∨ B  =  ¬(¬A ∧ ¬B)      (De Morgan's Law)&lt;br /&gt;0  =  A ∧ ¬A&lt;br /&gt;1  =  ¬(A ∧ ¬A)&lt;br /&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Even just using these three operators, there are multiple ways to express any given function. For example, which is the 'best' way to represent XOR (the ⊕ operator)?&lt;br /&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;A ⊕ B  =  (¬A ∧ B) ∨ (A ∧ ¬B)&lt;br /&gt;A ⊕ B  =  (A ∨ B) ∧ ¬(A ∧ B)&lt;br /&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The first is easier for most people to understand ("A is true and B is false, or the other way around"), while the second has fewer logical operations. If we wanted to implement XOR in software that might matter, although in hardware it would usually be implemented as a custom unit such as the CMOS 4070, or failing that, four NAND gates:&lt;br /&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;N  :=  A ↑ B&lt;br /&gt;A ⊕ B  =  (A ↑ N) ↑ (N ↑ B)&lt;br /&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;span xmlns=""&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://en.wikipedia.org/wiki/Image:XOR_Using_NAND.jpg"&gt;&lt;img style="cursor: pointer; display: block; margin-left: auto; margin-right: auto;" src="http://upload.wikimedia.org/wikipedia/commons/f/f1/XOR_Using_NAND.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Another interesting feature of hardware level Boolean logic is &lt;em&gt;propagation delay&lt;/em&gt;.  Essentially, we think of logical operations as instantaneous, however in hardware they take a measured time to switch. The propagation delay is the total time it takes for a change in the inputs to be reflected as a change of the outputs of a function. This gives us a tradeoff between time and transistor count – basically, it might be better to use more transistors to implement a function, but structured in parallel. This tradeoff is sometimes reflected in software: if your computer can run multiple instructions in parallel, it is sometimes possible to use more instructions perform a function in fewer clock cycles. The compiler usually handles this kind of micro-optimization, however.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;One other interesting tidbit: take a look at &lt;a href="http://en.wikipedia.org/wiki/Karnaugh_map"&gt;Karnaugh maps&lt;/a&gt; for simplifying expressions and finding &lt;a href="http://en.wikipedia.org/wiki/Race_condition"&gt;race conditions&lt;/a&gt;. Essentially, you draw out the truth table for an expression and then cover the '1's with as few overlapping rectangles as possible, optionally adding extras to prevent race conditions.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span xmlns=""&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://commons.wikimedia.org/wiki/Image:K-map_6%2C8%2C9%2C10%2C11%2C12%2C13%2C14_anti-race.svg"&gt;&lt;img style="cursor: pointer; display: block; margin-left: auto; margin-right: auto;" src="http://1.bp.blogspot.com/_4uUxYbT6E0Q/SDhxGovEwAI/AAAAAAAAB2c/iOwONr2VbLM/s400/K-map_6,8,9,10,11,12,13,14_anti-race.png" alt="" id="BLOGGER_PHOTO_ID_5204033728254623746" border="0" /&gt;&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Lastly, I'll leave you with a puzzle: invert three inputs using only two NOT gates. To be specific, you have a function with 3 inputs, A B C, and you want three outputs, X Y Z, such that&lt;br /&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;X  =  ¬A&lt;br /&gt;Y  =  ¬B&lt;br /&gt;Z  =  ¬C&lt;br /&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;However, you only have two NOT gates (although you can use as many AND or OR gates as you'd like). Note: to solve this in written form (rather than by drawing a circuit diagram), you may find that you need temporary variables, such as 'N := ¬A ∧ B'.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Bonus points if you can generalize it to an algorithm for inverting &lt;strong&gt;n&lt;/strong&gt; inputs using ⌊&lt;strong&gt;n&lt;/strong&gt;/2⌋ + 1 NOT gates.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-7869519820726152618?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/bXLVFrpNq7c" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/7869519820726152618/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2008/05/boolean-logic.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/7869519820726152618?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/7869519820726152618?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/bXLVFrpNq7c/boolean-logic.html" title="Boolean Logic" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_4uUxYbT6E0Q/SDhxGovEwAI/AAAAAAAAB2c/iOwONr2VbLM/s72-c/K-map_6,8,9,10,11,12,13,14_anti-race.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2008/05/boolean-logic.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0UAQHg9eCp7ImA9WxdSEEg.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-3618158124202104876</id><published>2008-05-17T12:55:00.008-04:00</published><updated>2008-05-17T15:07:21.660-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-05-17T15:07:21.660-04:00</app:edited><title>Risk</title><content type="html">&lt;span xmlns=""&gt;&lt;p&gt;I, like many people these days, keep my important information on the internet. In this day and age, the big companies I entrust my privacy to are all as secure as I could ask for, and keep getting better. For example, Google (who is going to be my example for the brunt of this article) has never been hacked or lost &lt;em&gt;any&lt;/em&gt; user information (as far as I know). But security doesn't lie entirely in the cloud; a key challenge is getting information in and out, and the security on &lt;em&gt;that&lt;/em&gt; really hasn't changed.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;So how do we protect our online identities? With passwords, of course. Now, there have been a few advances in password security – such as guidelines on what kind of passwords to use – but for 40 years of innovation, that really doesn't feel like much. Of course, that could mean that passwords are Good Enough™, but considering that someone could just watch you type one in, I don't think that's true. It's like building a walled garden with a moat, barbed wire, and lasers: if someone steals the key, they can just walk in the front gate.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;To be fair, we've had this same problem for years. But recently I've noticed a trend: more and more information is ending up on the internet, and quite a lot of it is going to the &lt;em&gt;same&lt;/em&gt; places. To see why this is a problem, let's take a look at the engineering definition of &lt;a href="http://en.wikipedia.org/wiki/Risk"&gt;&lt;strong&gt;Risk&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;:&lt;br /&gt;&lt;/strong&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Risk = (probability of an accident) x (losses per accident)&lt;br /&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_4uUxYbT6E0Q/SC8Oa6FSV_I/AAAAAAAABBM/Se6BO53iud0/s1600-h/Google+services.png"&gt;&lt;img style="margin: 0pt 0pt 5px 5px; float: right; cursor: pointer;" src="http://3.bp.blogspot.com/_4uUxYbT6E0Q/SC8Oa6FSV_I/AAAAAAAABBM/Se6BO53iud0/s400/Google+services.png" alt="" id="BLOGGER_PHOTO_ID_5201391950067030002" border="0" /&gt;&lt;/a&gt;The probability of an accident is equivalent to someone getting my password, and doesn't really change. But the losses per accident are going up and up and up. Take a look at my Google account right now: I use a fair number of services – probably more than most – but the trend is the same for everyone. Hypothetically, someone could get access to&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Every email I have sent or received (Gmail)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Where and when I will be (Calendar)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;My address and credit information (Google Checkout)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Pictures of me and my friends (Picasa)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Everything I have searched for or clicked on on Google (Web History)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;My health information&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Wait, health information? Yep! Coming soon to a Google near you, &lt;a href="http://googleblog.blogspot.com/2008/02/pilot-with-cleveland-clinic-for-health.html"&gt;personal health information&lt;/a&gt;&lt;a href="#HealthVault"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt;. All the time we are moving more information into the digital world, and for the most part, that's a good thing. But the risk to us is increasing at the same rate, and that makes me a tad nervous. I have to ask, what would it take for stealing passwords to become a lucrative business? I'm not the only one who has noticed the problem; some research is going into building a better lock, using technologies like fingerprint or iris scanning, and there are other approaches too. One possibility being thrown around is using usage patterns to detect when someone malicious is in the account, and cut them off from any information until they confirm their identity&lt;a href="#CreditCard"&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;. Another idea I had was partitioning the information &lt;em&gt;within&lt;/em&gt; the account, to mitigate the risk of someone getting access. For example, I'd use a different password for Gmail and Google Reader than the rest of my services, since those are the most likely to be compromised. But regardless of what, if anything, gets implemented, a word of advice: keep your passwords safe!&lt;br /&gt;&lt;/p&gt;&lt;h2&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;Footnotes&lt;/span&gt;&lt;br /&gt;&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;&lt;a name="HealthVault"&gt;&lt;/a&gt;It's not just Google – Microsoft has a &lt;a href="http://www.healthvault.com/"&gt;product of its own&lt;/a&gt; in development for storing health information.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a name="CreditCard"&gt;&lt;/a&gt;The parallel is credit card companies. If you start to purchase more than usual, or in different locations than usual, you may find your credit card cut off until you make a phone call.&lt;/li&gt;&lt;/ol&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-3618158124202104876?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/euxSZRSCi9M" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/3618158124202104876/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2008/05/risk.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/3618158124202104876?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/3618158124202104876?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/euxSZRSCi9M/risk.html" title="Risk" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_4uUxYbT6E0Q/SC8Oa6FSV_I/AAAAAAAABBM/Se6BO53iud0/s72-c/Google+services.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2008/05/risk.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck8CQX87fCp7ImA9WxdTFEk.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-4990477148204297671</id><published>2008-05-10T12:28:00.007-04:00</published><updated>2008-05-10T13:34:20.104-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-05-10T13:34:20.104-04:00</app:edited><title>Scaling User Content</title><content type="html">&lt;span xmlns=""&gt;&lt;p&gt;Scalability.  One little word, and yet it continues to be a major problem plaguing programmers to this day.  From &lt;a href="http://en.wiktionary.org/wiki/scalability"&gt;Wikitionary&lt;/a&gt;:&lt;br /&gt;&lt;/p&gt;&lt;blockquote&gt;2. (computing) The ability to support the required quality of service as the system load increases without changing the system.&lt;br /&gt;&lt;/blockquote&gt;&lt;p&gt;A desirable property in any system to be sure, and to some extent it's easy enough to do. But there is always a limit, and once hit, the system needs to change. A lot of research has been done on how to scale just about anything – a party, a business, a network – and I don't really want to talk about these. But there is one issue I will talk about: user content.&lt;/p&gt;&lt;p&gt;Let's take a look at one of the earliest examples: the book. At first there were only a couple books to be found, laboriously copied out by hand and hugely expensive. This in itself wasn't too much of a problem, as very few knew how to read anyways. But the effort involved meant that only the best content would do, and only the elite would ever see it. Reading and writing grew slowly, and over time libraries were built. But as the price of admission fell and the literacy rate rose, more people started reading and writing. And then in 1439, Johann Gutenberg invented the printing press. Within a very short period of time the problem had changed – rather than getting access to books, people now had to choose &lt;em&gt;between &lt;/em&gt;them, and the quality bar had been lowered. People have been solving this problem ever since; Oprah's Book Club, the New York Times' best sellers, and the Hugo Awards are all ways to separate the wheat from the chaff.&lt;/p&gt;&lt;p&gt;In the internet age, it is &lt;em&gt;so easy&lt;/em&gt; to get your words out there that websites need to have a strategy for scaling content beyond the purely technical problems of storage and retrieval, to keep from being drowned in the chaff. Let's take a look at a few popular websites and see how they do it.&lt;span&gt;&lt;span xmlns=""&gt;&lt;h2&gt;&lt;span&gt;&lt;span xmlns=""&gt;&lt;span&gt;&lt;span xmlns=""&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.google.com"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://4.bp.blogspot.com/_4uUxYbT6E0Q/SCXRhP6kgxI/AAAAAAAABAs/5Uzg_oXSl90/s320/google.png" alt="" id="BLOGGER_PHOTO_ID_5198791714007188242" border="0" /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;h2&gt;Google&lt;/h2&gt;&lt;p&gt;Born in an era when most companies were only interested in creating portal sites and search engines were 'good enough', Google was a very different kind of company. Focused on providing good quality search results, and unwilling to allow ad companies to 'buy' position in the index, the Google search engine was generations better than its competitors. It used innovative algorithms (notably the PageRank algorithm) and a team of high class programmers to stay ahead of spammers and keep the search results fresh and relevant. Even when branching out into other tools, Google has always used specialized algorithms to solve its problems and provide quality content for users.&lt;br /&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Approach:&lt;/strong&gt; Custom algorithms for filtering content&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Drawback:&lt;/strong&gt; Spammers can find flaws in the algorithms, and exploit them for gain&lt;/li&gt;&lt;/ul&gt;&lt;span&gt;&lt;span xmlns=""&gt;&lt;h2&gt;&lt;span&gt;&lt;span xmlns=""&gt;&lt;span&gt;&lt;span xmlns=""&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://en.wikipedia.org"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://1.bp.blogspot.com/_4uUxYbT6E0Q/SCXSOf6kgyI/AAAAAAAABA0/RZI23XW7o9o/s320/wikipedia.png" alt="" id="BLOGGER_PHOTO_ID_5198792491396268834" border="0" /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;&lt;/span&gt;&lt;/span&gt;&lt;h2&gt;Wikipedia&lt;/h2&gt;&lt;p&gt;Originally conceived as a feeder project for Nupedia, an expert-written online encyclopedia (complete with full-time editor), Wikipedia was born in early 2001. It was to be a more 'open' compliment to Nupedia where anyone could edit articles, except it was not well received by the editors and reviewers. Only five days after creation it was given its own name, &lt;em&gt;Wikipedia,&lt;/em&gt; and moved to its own site. A popular idea from the very beginning, Wikipedia had over 20,000 articles in its very first year. To keep the content clean of spam a whole set of tools were created, and a hierarchy of moderators to use them.  The goal was to make it easier to undo vandalism than to create it in the first place, and looking at the current state of Wikipedia, it was highly successful. There is also a series of guidelines for acceptable content, and a court style system for resolving complaints.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Approach: &lt;/strong&gt;Content moderated by the community itself, with sophisticated tools and guidelines to assist this&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Drawback:&lt;/strong&gt; A lot of time and effort goes into keeping the content clean, usually done by a small subset of the site's users&lt;/li&gt;&lt;/ul&gt;&lt;span&gt;&lt;span xmlns=""&gt;&lt;h2&gt;&lt;span xmlns=""&gt;&lt;span xmlns=""&gt;&lt;span&gt;&lt;span xmlns=""&gt;&lt;span&gt;&lt;span xmlns=""&gt;&lt;span&gt;&lt;span xmlns=""&gt;&lt;span&gt;&lt;span xmlns=""&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.facebook.com"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://1.bp.blogspot.com/_4uUxYbT6E0Q/SCXTtf6kgzI/AAAAAAAABA8/DxhFUcfxyT4/s320/Facebook.png" alt="" id="BLOGGER_PHOTO_ID_5198794123483841330" border="0" /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;&lt;/span&gt;&lt;/span&gt;&lt;h2&gt;Facebook&lt;/h2&gt;&lt;p&gt;Created by Mark Zuckerberg as a way to connect with friends in Harvard University, Facebook rapidly expanded to other universities, high schools, and eventually, anyone who wanted to join. It works on the principle that people are grouped by the networks they are part of, and these can be used to find people you know. Users 'friend' each other, identifying that they do indeed know one another and giving permission to access each other's profile. This keeps the content remarkably relevant and spam free, since users only sees the actions of people they personally know.&lt;br /&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Approach: &lt;/strong&gt;Content associated with its author, and users manually select which authors they want to see content by&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Drawback:&lt;/strong&gt; Not well suited to finding new associations, instead good for maintaining ones created externally&lt;/li&gt;&lt;/ul&gt;&lt;span&gt;&lt;span xmlns=""&gt;&lt;h2&gt;&lt;span xmlns=""&gt;&lt;span&gt;&lt;span xmlns=""&gt;&lt;span&gt;&lt;span xmlns=""&gt;&lt;span&gt;&lt;span xmlns=""&gt;&lt;span xmlns=""&gt;&lt;span&gt;&lt;span xmlns=""&gt;&lt;span&gt;&lt;span xmlns=""&gt;&lt;span&gt;&lt;span xmlns=""&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.digg.com"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://1.bp.blogspot.com/_4uUxYbT6E0Q/SCXVlf6kg0I/AAAAAAAABBE/IBSAK7oc4PE/s320/digg.png" alt="" id="BLOGGER_PHOTO_ID_5198796185068143426" border="0" /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;&lt;/span&gt;&lt;/span&gt;&lt;h2&gt;Digg&lt;/h2&gt;&lt;p&gt;Digg was one of the first widely used tools for people to share interesting content they found on the internet. Users can 'dig' an article, picture, or video they find, and subsequent users can 'dig' or 'bury' it depending on whether they like it or not. This kind of social voting brings the most interesting articles to the top and leaves the rest behind. The same kind of voting system is used on the submission comments, highlighting interesting ones and hiding the rest.&lt;br /&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Approach: &lt;/strong&gt;Submitted content is voted up or down by the rest of the community, filtering out what's interesting&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Drawback:&lt;/strong&gt; Using community opinion leads to a &lt;a href="http://en.wikipedia.org/wiki/Groupthink"&gt;groupthink&lt;/a&gt; mentality, promoting content that reinforces the community's opinions&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This is by no means all of the approaches out there, just a few that I think are worth noting. For example, Slashdot uses a more sophisticated version of comment voting that takes into account how 'correct' a user's opinions usually are. Each one has its own set of drawbacks, however they are &lt;em&gt;all&lt;/em&gt; better than just doing nothing. I am excited to see what innovative solutions people will come up with in the coming years. I mentioned in my last post that I thought blog comments had a systemic flaw; they just don't scale with one moderator and &lt;strong&gt;n&lt;/strong&gt; users. I'm sure someone will find a good solution for this problem soon, whether it is comment voting à la Digg or something entirely new. But until then, manual moderation it is.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-4990477148204297671?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/L0Vg4QPnvLs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/4990477148204297671/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2008/05/scaling-user-content.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/4990477148204297671?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/4990477148204297671?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/L0Vg4QPnvLs/scaling-user-content.html" title="Scaling User Content" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_4uUxYbT6E0Q/SCXRhP6kgxI/AAAAAAAABAs/5Uzg_oXSl90/s72-c/google.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2008/05/scaling-user-content.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0MFQng9fyp7ImA9WhRbE0w.&quot;"><id>tag:blogger.com,1999:blog-1806360094658697411.post-6020084089315354346</id><published>2008-05-03T13:31:00.008-04:00</published><updated>2012-02-03T20:36:53.667-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-03T20:36:53.667-05:00</app:edited><title>About Me</title><content type="html">&lt;span xmlns=""&gt; &lt;p&gt;&lt;b&gt;Details updated June 7, 2010&lt;/b&gt;&lt;/p&gt;&lt;p&gt;I've always hated writing 'About Me' sections. In my view writing should be about the &lt;em&gt;content&lt;/em&gt;, not the author, but since when did the world work the way I want it to? And I have to admit, it does make sense for a first post.&lt;br /&gt;
&lt;/p&gt;&lt;h2&gt;Who are you?&lt;br /&gt;
&lt;/h2&gt;&lt;p&gt;I'm Eric Burnett. Currently I am a software engineer at Google, working on &lt;a href="http://www.doubleclick.com/products/advertisingexchange/index.aspx" alt="Ad Exchange"&gt;Ad Exchange&lt;/a&gt;. This blog is not about my work, however, so don't expect much in that direction. Prior to this I was at the University of Waterloo earning a bachelor's in Computer Science&lt;sup&gt;&lt;a href="#degree"&gt;1&lt;/a&gt;&lt;/sup&gt;, a topic on which I would be happy to field questions :).&lt;/p&gt;&lt;p&gt;I have always been fascinated by computers, and since I was in elementary school I knew I would end up in a career working with them. My first exposure to programming came actually quite late, when I was 15 or 16. I took a summer school course that had us making games in Macromedia Director (now &lt;a href="http://en.wikipedia.org/wiki/Adobe_Director"&gt;Adobe Director&lt;/a&gt;) using its custom scripting language. That was also my first exposure to the realities of our industry: the game I was making didn't get completed on time, even with features cut. Despite that, I immediately found myself fascinated by programming and understanding the magical world inside the computer, and so during the next year I taught myself C++. The rest, as they say, is history.&lt;br /&gt;
&lt;/p&gt;&lt;p&gt;Since then I have learned that it doesn't matter what language or tools you are using; programming is just a realization of the thought process of the programmer. From my point of view it doesn't matter what you &lt;em&gt;know&lt;/em&gt;, any programmer worth their salt can learn what they need to know in short order. What matters more is how you&lt;em&gt; think&lt;/em&gt;, and that's the part that fascinates me. This blog is part of that: in part, it's the story of my quest to know what makes us tick.&lt;br /&gt;
&lt;/p&gt;&lt;h2&gt;Why start a blog?&lt;br /&gt;
&lt;/h2&gt;&lt;p&gt;That's the real question, isn't it? I've been reading blogs for a couple years now, but recently I've found myself with ideas that I want to share, and nobody to share them with. I don't know if anyone will ever read this, but if nothing else the act of writing these ideas down forces me to think them through in a way that pure contemplation never quite does (more on that later). Posting this is both a challenge and a commitment to myself to keep thinking, questioning, and finding new insights that are worth sharing.&lt;br /&gt;
&lt;/p&gt;&lt;p&gt;The strongest inspiration for this blog has come from reading &lt;a href="http://www.codinghorror.com/blog/"&gt;Coding Horror&lt;/a&gt;, one of the most widely read programming blogs out there. If you haven't heard of it, I suggest you go take a look. I have a long list of other blogs that I read, but it was this one that showed me the power of a blog – Jeff Atwood consistently made me think. It also happens to be well written and humorous – hell, even the format of this post was borrowed from &lt;a href="http://www.codinghorror.com/blog/archives/000021.html"&gt;Jeff's About Me page&lt;/a&gt;.&lt;br /&gt;
&lt;/p&gt;&lt;h2&gt;What's with the name?&lt;br /&gt;
&lt;/h2&gt;&lt;p&gt;&lt;b&gt;[This section relates to the old name, "Intelligent Conversation"]&lt;/b&gt;&lt;/p&gt;&lt;p&gt;There is a whole story behind the name of this blog. "Intelligent Conversation"…sounds a bit conceited, doesn't it? A couple months ago, I did a google search on those two words, and very little of merit showed up. There were a couple links about finding a good date, and two links on finding intelligent conversation on the internet (written in 1998 and 2002). Their view? It could be found if you looked hard enough, but it wasn't easy. Now, those were written six to ten years ago, and the internet has changed a lot in that time. I haven't looked at any forums or chatrooms recently, but I do know one place it can be found – blogs. As the internet matured we have found a way to take advantage of the one-to-many nature of the internet, with a few people writing and many people reading. In this new form, it's a lot easier to find people saying things that you want to hear, and you don't have to worry about sifting through spam&lt;a href="#Footnote2"&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;. With blogs there is history, reputation, and people own their words and think before they speak – what progress we have made!&lt;br /&gt;
&lt;/p&gt;&lt;p&gt;There is a second reason for the name. As I mentioned earlier, the act of writing ideas down forces me to think them through. I find this holds for any other kind of conversation as well, be it giving a presentation or chatting with a friend. It's easy to have an idea and see the merit in it, even when it's half formed. But before you hold it up for scrutiny, you'd be well advised to finish it off and polish it up nicely, because people will be examining it closely. If they like what they see then you've taught them something, and if not, well, then they get to teach you something! And hey, even if nobody is around to see, you still get to walk away with a nice shiny idea. Intelligence drives conversation, but conversation drives intelligence too, and I want a piece of the action.&lt;br /&gt;
&lt;/p&gt;&lt;h2&gt;What's with the &lt;i&gt;new&lt;/i&gt; name?&lt;br /&gt;
&lt;/h2&gt;&lt;p&gt;&lt;b&gt;[Section added January 27, 2010]&lt;/b&gt;&lt;/p&gt;&lt;p&gt;All the reasons listed for the original name still apply. But I missed one key insight the first time around.&lt;br /&gt;
&lt;blockquote&gt;"Intelligent Conversation"…sounds a bit conceited, doesn't it?&lt;br /&gt;
&lt;/blockquote&gt;Unfortunately yes, it does. And with the way content is consumed on the internet, &lt;i&gt;the first impression is all I'll get&lt;/i&gt;. Very few people will ever read the rationalization behind any name I choose, so it is important that I choose one that gives off the right impression. Considering I was getting embarrassed to give out links, it was time for a change.&lt;br /&gt;
&lt;/p&gt;&lt;h2&gt;Where can I find you?&lt;br /&gt;
&lt;/h2&gt;&lt;p&gt;Feel free to drop me an email at &lt;a href="mailto:ericburnett@gmail.com"&gt;ericburnett@gmail.com&lt;/a&gt;. I can't promise I'll respond, but it will always get read.&lt;br /&gt;
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;&lt;span style="color: rgb(102, 102, 102);" &gt;Footnotes&lt;/span&gt;&lt;br /&gt;
&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;&lt;a name="degree"&gt;&lt;/a&gt;Technically, I earned an Honours Bachelor's of Mathematics in Computer Science Co-op degree, with a minor in Combinatorics and Optimization. But don't hold that against me.&lt;/li&gt;
&lt;li&gt;&lt;a name="Footnote2"&gt;&lt;/a&gt;This doesn't hold for blog comments. On small blogs comments can work fine, but the more popular a blog gets, the harder to manage comments get. Part of this is from real spam, but technology is helping mitigate that. A larger part is just the nature of blogs: blogs are an open discourse, person to world. But their comments are a two way conversation, and relatively quickly there are just too many participants for any one person to follow. It doesn't help that the signal to noise ratio is usually quite low for blog comments, but I think the problem is systemic.&lt;br /&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1806360094658697411-6020084089315354346?l=www.thelowlyprogrammer.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TheLowlyProgrammer/~4/EuJQ7RpNcjQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.thelowlyprogrammer.com/feeds/6020084089315354346/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.thelowlyprogrammer.com/2008/05/about-me.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/6020084089315354346?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1806360094658697411/posts/default/6020084089315354346?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TheLowlyProgrammer/~3/EuJQ7RpNcjQ/about-me.html" title="About Me" /><author><name>Eric Burnett</name><uri>https://profiles.google.com/103760168651162106315</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-zwHDmf94eg4/AAAAAAAAAAI/AAAAAAAAENc/vQsM5DAF7E4/s512-c/photo.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.thelowlyprogrammer.com/2008/05/about-me.html</feedburner:origLink></entry></feed>

