<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Martin Ankerl &#187; ruby</title>
	<atom:link href="http://martin.ankerl.com/category/ruby/feed/" rel="self" type="application/rss+xml" />
	<link>http://martin.ankerl.com</link>
	<description>No movement is faster than no movement</description>
	<lastBuildDate>Tue, 13 Jul 2010 05:31:29 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=abc</generator>
		<item>
		<title>svn-shortlog &#8212; Compact &amp; Beautiful Subversion Changelog</title>
		<link>http://martin.ankerl.com/2009/12/23/svn-shortlog-compact-beautiful-subversion-changelog/</link>
		<comments>http://martin.ankerl.com/2009/12/23/svn-shortlog-compact-beautiful-subversion-changelog/#comments</comments>
		<pubDate>Wed, 23 Dec 2009 16:58:17 +0000</pubDate>
		<dc:creator>Martin Ankerl</dc:creator>
				<category><![CDATA[coding]]></category>
		<category><![CDATA[news]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://martin.ankerl.com/?p=303</guid>
		<description><![CDATA[At work we periodically have short developer meetings to discuss what has happened in the last month. To do this, we go through the bugs in our issue tracking system, and the subversion commits in our repository. Unfortunately, getting an overview of the subversion commits was rather cumbersome, and we could not find any efficient [...]]]></description>
			<content:encoded><![CDATA[<p>At work we periodically have short developer meetings to discuss what has happened in the last month. To do this, we go through the bugs in our issue tracking system, and the subversion commits in our repository. Unfortunately, getting an overview of the subversion commits was rather cumbersome, and we could not find any efficient tool to do this. Hence, <strong>svn-shortlog</strong> was born.</p>
<p>This is an attempt to format the subversion log of a one-month period in the following way:</p>
<ul>
<li>Beautiful HTML output.</li>
<li>Compact representation of lots of information</li>
<li>Usable with a not-so color rich beamer.</li>
<li>Fully automatic.</li>
</ul>
<h2>Usage</h2>
<ol>
<li>Install <a href="http://www.ruby-lang.org/de/">Ruby</a> (both 1.8 or 1.9 should work).</li>
<li>Download <a href="http://svn-shortlog.googlecode.com/svn/trunk/svn-shortlog.rb">svn-shortlog.rb</a>.</li>
<li>Open <tt>svn-shortlog.rb</tt> with your favourite text editor, and configure the config section according to your needs.</li>
<li>Doubleclick <tt>svn-shortlog.rb</tt></li>
<li>Open the generated <tt>changelog_....html</tt> file with your favourite browser.</li>
</ol>
<h2>Sample Output</h2>
<p>Here is a <a target="_blank" href="http://martin.ankerl.com/wp-content/uploads/2009/12/changes_2009-12-01_to_2009-12-31.html">sample output of one month of boost commits</a> into trunk, taken from the <a href="http://www.boost.org/users/download/#repository">public repository</a>. The output is quite information dense, a quick description is in the screenshot: <center><img src="http://martin.ankerl.com/wp-content/uploads/2009/12/documentation.png" alt="" title="documentation" width="690" height="408" /></center> All commits are structured by user, then by date. Each commit is on one line. You can click each line to see the full information related to a commit.</p>
<h2>Issues</h2>
<p>Ideas, suggestions, problems? Please post them as a comment here, at the <a href="https://code.google.com/p/svn-shortlog/issues/list">bug tracker</a>.</p>
<h2>Credits</h2>
<p>This tool is based on the idea from my colleague <a href="http://cheind.wordpress.com/">Christoph Heindl</a> and inspired by <a href="http://groups.google.com/group/linux.kernel/msg/d43224c9ba53f0cc?">Linus&#8217; Kernel shortlog</a> and <a href="http://mail.google.com/">Gmail</a>.</p>
<div style='clear:both'></div><img src="http://martin.ankerl.com/?ak_action=api_record_view&id=303&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://martin.ankerl.com/2009/12/23/svn-shortlog-compact-beautiful-subversion-changelog/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>How to Generate Random Colors Programmatically</title>
		<link>http://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/</link>
		<comments>http://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/#comments</comments>
		<pubDate>Wed, 09 Dec 2009 19:09:17 +0000</pubDate>
		<dc:creator>Martin Ankerl</dc:creator>
				<category><![CDATA[coding]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://martin.ankerl.com/?p=254</guid>
		<description><![CDATA[Creating random colors is actually more difficult than it seems. The randomness itself is easy, but aesthetically pleasing randomness is more difficult. For a little project at work I needed to automatically generate multiple background colors with the following properties: Text over the colored background should be easily readable Colors should be very distinct The [...]]]></description>
			<content:encoded><![CDATA[<p>Creating random colors is actually more difficult than it seems. The randomness itself is easy, but aesthetically pleasing randomness is more difficult. For a little project at work I needed to automatically generate multiple background colors with the following properties:</p>
<ul>
<li>Text over the colored background should be easily readable</li>
<li>Colors should be very distinct</li>
<li>The number of required colors is not initially known</li>
</ul>
<h1>Naïve Approach</h1>
<p>The first and simplest approach is to create random colors by simply using a random number between <tt>[0, 256[</tt> for the R, G, B values. I have created a little Ruby script to generate sample HTML code:
<pre class="brush: ruby;"># generates HTML code for 26 background colors given R, G, B values.
def gen_html
  ('A'..'Z').each do |c|
    r, g, b = yield
    printf &quot;&lt;span style=\&quot;background-color:#%02x%02x%02x; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;\&quot;&gt;#{c}&lt;/span&gt; &quot;, r, g, b
  end
end

# naive approach: generate purely random colors
gen_html { [rand(256), rand(256), rand(256)] }</pre>
<p> The generated output looks like this:
<p style="text-align:center;"><span style="background-color:#a69dd8; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">A</span> <span style="background-color:#0c35b0; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">B</span> <span style="background-color:#f82750; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">C</span> <span style="background-color:#0ebd31; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">D</span> <span style="background-color:#5fab4f; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">E</span> <span style="background-color:#c538cf; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">F</span> <span style="background-color:#014a59; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">G</span> <span style="background-color:#e14af8; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">H</span> <span style="background-color:#9fb730; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">I</span> <span style="background-color:#4bec60; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">J</span> <span style="background-color:#ef9345; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">K</span> <span style="background-color:#d2ece0; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">L</span> <span style="background-color:#9cda80; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">M</span> <span style="background-color:#dbc07c; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">N</span> <span style="background-color:#7328dd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">O</span> <span style="background-color:#1e9942; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">P</span> <span style="background-color:#621b7b; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">Q</span> <span style="background-color:#c830b2; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">R</span> <span style="background-color:#362332; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">S</span> <span style="background-color:#e8c55d; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">T</span> <span style="background-color:#bd8787; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">U</span> <span style="background-color:#66c6a4; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">V</span> <span style="background-color:#21ec4b; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">W</span> <span style="background-color:#782364; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">X</span> <span style="background-color:#c3bf15; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">Y</span> <span style="background-color:#3db35a; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">Z</span>
</p>
<p>As you can see this is quite suboptimal. Some letters are hard to read because the background is too dark (B, Q, S), other colors look very similar (F, R).</p>
<h1>Using HSV Color Space</h1>
<p><a href="http://en.wikipedia.org/wiki/File:HSV_cylinder.png"><img src="http://martin.ankerl.com/wp-content/uploads/2009/12/HSV_cylinder_small.png" alt="HSV_cylinder_small" title="HSV_cylinder_small" width="250" height="200" style="float:right;margin-left:10px; margin-bottom:10px;" /></a>Let's fix the too dark / too bright problem first. A convenient way to do this is to not use the RGB color space, but <a href="http://en.wikipedia.org/wiki/HSL_and_HSV">HSV</a> (Hue, Saturation, Value). Here you get equally bright and colorful colors by using a fixed value for saturation and value, and just modifying the hue.</p>
<p>Based on the description provided by the wikipedia article on <a href="http://en.wikipedia.org/wiki/HSL_and_HSV#Converting_to_RGB">conversion from HSV to RGB</a> I have implemented a converter:
<pre class="brush: ruby;"># HSV values in [0..1[
# returns [r, g, b] values from 0 to 255
def hsv_to_rgb(h, s, v)
  h_i = (h*6).to_i
  f = h*6 - h_i
  p = v * (1 - s)
  q = v * (1 - f*s)
  t = v * (1 - (1 - f) * s)
  r, g, b = v, t, p if h_i==0
  r, g, b = q, v, p if h_i==1
  r, g, b = p, v, t if h_i==2
  r, g, b = p, q, v if h_i==3
  r, g, b = t, p, v if h_i==4
  r, g, b = v, p, q if h_i==5
  [(r*256).to_i, (g*256).to_i, (b*256).to_i]
end</pre>
<p>Using the generator and fixed values for saturation and value:
<pre class="brush: ruby;"># using HSV with variable hue
gen_html { hsv_to_rgb(rand, 0.5, 0.95) }</pre>
<p>returns something like this:<center><span style="background-color:#f379ad; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">A</span> <span style="background-color:#7979f3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">B</span> <span style="background-color:#9079f3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">C</span> <span style="background-color:#79e5f3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">D</span> <span style="background-color:#8979f3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">E</span> <span style="background-color:#79f396; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">F</span> <span style="background-color:#79cff3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">G</span> <span style="background-color:#79b1f3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">H</span> <span style="background-color:#7979f3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">I</span> <span style="background-color:#799ef3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">J</span> <span style="background-color:#ecf379; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">K</span> <span style="background-color:#80f379; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">L</span> <span style="background-color:#797cf3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">M</span> <span style="background-color:#79f3f0; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">N</span> <span style="background-color:#9af379; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">O</span> <span style="background-color:#79f37a; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">P</span> <span style="background-color:#f3ad79; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">Q</span> <span style="background-color:#f3e179; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">R</span> <span style="background-color:#79b9f3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">S</span> <span style="background-color:#e8f379; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">T</span> <span style="background-color:#f3b379; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">U</span> <span style="background-color:#f379c9; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">V</span> <span style="background-color:#79b8f3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">W</span> <span style="background-color:#f379dc; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">X</span> <span style="background-color:#79f37b; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">Y</span> <span style="background-color:#8e79f3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">Z</span><br />
</center><br />
Much better. The text is easily readable, and all colors have a similar brightness. Unfortunately, since we have limited us to less colors now, the difference between the randomly generated colors is even less than in the first approach.</p>
<h1>Golden Ratio</h1>
<p>Using just <tt>rand()</tt> to choose different values for hue does not lead to a good use of the whole color spectrum, it simply is too random.<center><img src="http://martin.ankerl.com/wp-content/uploads/2009/12/distribution-random.png" alt="distribution-random" title="distribution-random" width="483" height="291" class="alignright size-full wp-image-273" /></center></p>
<p>Here I have generated 2, 4, 8, 16, and 32 random values and printed them all on a scale. Its easy to see that some values are very tightly packed together, which we do not want.</p>
<p>Lo and behold, some mathematician has discovered the <a href="http://en.wikipedia.org/wiki/Golden_ratio">Golden Ratio</a> more than 2400 years ago. It has lots of interesting properties, but for us only one is interesting:</p>
<blockquote><p>[...] Furthermore, it is a property of the golden ratio, <em>&Phi;</em>, that each subsequent hash value divides the interval into which it falls according to the golden ratio!<br />
-- <a href="http://brpreiss.com/books/opus4/html/page214.html">Bruno R. Preiss, P.Eng.</a></p></blockquote>
<p>Using the golden ratio as the spacing, the generated values look like this:<br />
<center><img src="http://martin.ankerl.com/wp-content/uploads/2009/12/distribution-goldenratio.png" alt="distribution-goldenratio" title="distribution-goldenratio" width="483" height="291" class="alignright size-full wp-image-274" /></center></p>
<p>Much better! The values are very evenly distributed, regardless how many values are used. Also, the algorithm for this is extremly simple. Just add 1/&Phi; and modulo 1 for each subsequent color.</p>
<pre class="brush: ruby;"># use golden ratio
golden_ratio_conjugate = 0.618033988749895
h = rand # use random start value
gen_html {
  h += golden_ratio_conjugate
  h %= 1
  hsv_to_rgb(h, 0.5, 0.95)
}</pre>
<p>The final result:<br />
<center><span style="background-color:#f37e79; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">A</span> <span style="background-color:#7998f3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">B</span> <span style="background-color:#bbf379; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">C</span> <span style="background-color:#f379df; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">D</span> <span style="background-color:#79f3e3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">E</span> <span style="background-color:#f3bf79; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">F</span> <span style="background-color:#9c79f3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">G</span> <span style="background-color:#7af379; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">H</span> <span style="background-color:#f3799d; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">I</span> <span style="background-color:#79c1f3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">J</span> <span style="background-color:#e4f379; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">K</span> <span style="background-color:#de79f3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">L</span> <span style="background-color:#79f3ba; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">M</span> <span style="background-color:#f39779; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">N</span> <span style="background-color:#797ff3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">O</span> <span style="background-color:#a2f379; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">P</span> <span style="background-color:#f379c6; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">Q</span> <span style="background-color:#79e9f3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">R</span> <span style="background-color:#f3d979; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">S</span> <span style="background-color:#b579f3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">T</span> <span style="background-color:#79f392; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">U</span> <span style="background-color:#f37984; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">V</span> <span style="background-color:#79a8f3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">W</span> <span style="background-color:#cbf379; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">X</span> <span style="background-color:#f379ee; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">Y</span> <span style="background-color:#79f3d3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">Z</span></center></p>
<p>You can see that the first few values are very different, and the difference decreases as more colors are added (Z and E are already quite similar). Anyways, this is good enough for me.</p>
<p>And because it is so beautiful, here are some more colors <img src='http://martin.ankerl.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /><br />
<tt>s=0.99, v=0.99</tt>, <tt>s=0.25, h=0.8</tt>, and <tt>s=0.3, v=0.99</tt> <center><span style="background-color:#024bfd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">A</span> <span style="background-color:#94fd02; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">B</span> <span style="background-color:#fd02de; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">C</span> <span style="background-color:#02fdd3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">D</span> <span style="background-color:#fd8a02; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">E</span> <span style="background-color:#4102fd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">F</span> <span style="background-color:#0dfd02; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">G</span> <span style="background-color:#fd0256; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">H</span> <span style="background-color:#029ffd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">I</span> <span style="background-color:#e8fd02; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">J</span> <span style="background-color:#c802fd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">K</span> <span style="background-color:#02fd7f; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">L</span> <span style="background-color:#fd3602; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">M</span> <span style="background-color:#0217fd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">N</span> <span style="background-color:#61fd02; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">O</span> <span style="background-color:#fd02aa; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">P</span> <span style="background-color:#02f3fd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">Q</span> <span style="background-color:#fdbe02; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">R</span> <span style="background-color:#7402fd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">S</span> <span style="background-color:#02fd2b; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">T</span> <span style="background-color:#fd0222; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">U</span> <span style="background-color:#026bfd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">V</span> <span style="background-color:#b5fd02; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">W</span> <span style="background-color:#fc02fd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">X</span> <span style="background-color:#02fdb3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">Y</span> <span style="background-color:#fd6a02; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">Z</span><br />
</center></p>
<p><center><span style="background-color:#99a8cc; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">A</span> <span style="background-color:#b7cc99; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">B</span> <span style="background-color:#cc99c6; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">C</span> <span style="background-color:#99ccc4; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">D</span> <span style="background-color:#ccb599; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">E</span> <span style="background-color:#a699cc; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">F</span> <span style="background-color:#9bcc99; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">G</span> <span style="background-color:#cc99aa; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">H</span> <span style="background-color:#99b9cc; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">I</span> <span style="background-color:#c8cc99; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">J</span> <span style="background-color:#c299cc; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">K</span> <span style="background-color:#99ccb3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">L</span> <span style="background-color:#cca499; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">M</span> <span style="background-color:#999dcc; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">N</span> <span style="background-color:#accc99; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">O</span> <span style="background-color:#cc99bb; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">P</span> <span style="background-color:#99cacc; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">Q</span> <span style="background-color:#ccbf99; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">R</span> <span style="background-color:#b099cc; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">S</span> <span style="background-color:#99cca2; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">T</span> <span style="background-color:#cc99a0; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">U</span> <span style="background-color:#99afcc; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">V</span> <span style="background-color:#becc99; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">W</span> <span style="background-color:#cc99cc; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">X</span> <span style="background-color:#99ccbd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">Y</span> <span style="background-color:#ccae99; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">Z</span></center></p>
<p><center><span style="background-color:#b1c7fd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">A</span> <span style="background-color:#ddfdb1; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">B</span> <span style="background-color:#fdb1f3; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">C</span> <span style="background-color:#b1fdf0; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">D</span> <span style="background-color:#fddab1; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">E</span> <span style="background-color:#c4b1fd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">F</span> <span style="background-color:#b4fdb1; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">G</span> <span style="background-color:#fdb1ca; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">H</span> <span style="background-color:#b1e1fd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">I</span> <span style="background-color:#f7fdb1; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">J</span> <span style="background-color:#edb1fd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">K</span> <span style="background-color:#b1fdd7; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">L</span> <span style="background-color:#fdc1b1; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">M</span> <span style="background-color:#b1b7fd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">N</span> <span style="background-color:#cefdb1; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">O</span> <span style="background-color:#fdb1e4; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">P</span> <span style="background-color:#b1fafd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">Q</span> <span style="background-color:#fdeab1; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">R</span> <span style="background-color:#d4b1fd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">S</span> <span style="background-color:#b1fdbd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">T</span> <span style="background-color:#fdb1bb; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">U</span> <span style="background-color:#b1d1fd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">V</span> <span style="background-color:#e7fdb1; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">W</span> <span style="background-color:#fdb1fd; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">X</span> <span style="background-color:#b1fde7; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">Y</span> <span style="background-color:#fdd0b1; padding:5px; -moz-border-radius:3px; -webkit-border-radius:3px;">Z</span></center></p>
<p>Have fun!<br />
Martin</p>
<div style='clear:both'></div><img src="http://martin.ankerl.com/?ak_action=api_record_view&id=254&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Two Word Anagram Finder Algorithm (in Ruby)</title>
		<link>http://martin.ankerl.com/2008/08/09/two-word-anagram-finder-algorithm/</link>
		<comments>http://martin.ankerl.com/2008/08/09/two-word-anagram-finder-algorithm/#comments</comments>
		<pubDate>Sat, 09 Aug 2008 19:32:30 +0000</pubDate>
		<dc:creator>Martin Ankerl</dc:creator>
				<category><![CDATA[benchmark]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[tricks]]></category>

		<guid isPermaLink="false">http://martin.ankerl.com/?p=156</guid>
		<description><![CDATA[Today I have got some sourcecode for you. There is a little programming challenge named The Self-Documenting Code Contest that is quite fun, they try to find the cleanest and easiest to read code for this task: Write a program that generates all two-word anagrams of the string &#8220;documenting&#8221;. Here&#8217;s a word list you might [...]]]></description>
			<content:encoded><![CDATA[<p>Today I have got some sourcecode for you. There is a little programming challenge named <a href="http://selfexplanatorycode.blogspot.com/">The Self-Documenting Code Contest</a> that is quite fun, they try to find the cleanest and easiest to read code for this task:</p>
<blockquote><p>
Write a program that generates all two-word anagrams of the string &#8220;documenting&#8221;. Here&#8217;s a word list you might want to use: <a href='http://martin.ankerl.com/wp-content/uploads/2008/08/wordlist.zip'>wordlist.zip</a>.</p>
<p>When you&#8217;re done, send the results to <a href="mailto:selfdocumenting@hotmail.com">selfdocumenting@hotmail.com</a>.</p>
<p>Good luck!
</p></blockquote>
<p>So this caught my interest and i wrote a little entry in Ruby that is 23 lines long with whitespace and very nice to read. But I won&#8217;t show you this code until the contest is over, and this is not the reason for this post. The reason is, that the nice version takes about 2 seconds, and somebody else has coded a Python solution that takes only 1 second (I have no idea what his code looks like). This post is about a fast anagram finding algorithm, and how I developed this algorithm. The final result takes about 0.11 seconds.</p>
<h1>Algorithm</h1>
<p>The most basic algorithm has two phases:</p>
<ol>
<li>Read in the file
<li>Build all combinations of two words and compare the letter count with the query.
</ol>
<p>Building the combinations is usually done with two nested loops and takes O(n^2) runtime. This is slow, so I have added another step in between:</p>
<h2>Idea #1: Filter out Candidate Words</h2>
<p>The second step is really slow, but it would be a lot faster if it has to handle less words. So I wrote a little filtering step that lets only words through which are made out of the same letters as the query word.</p>
<p>For example when the query is <tt>documenting</tt>, the word <tt>men</tt> or <tt>go</tt> and even <tt>too</tt> are extracted, even if the number of letters might not match. But that&#8217;s not important, what is important is that the number of possible words are reduced a lot, and so the next phase is faster.</p>
<h2>Idea #2: Use a Commutative Hashing Function</h2>
<p>String comparisons are slow. To common way to find out if the strings <tt>coming</tt> with <tt>tuned</tt> is an anagram of the word <tt>documenting</tt> is to sort the letters and make a comparison, like this:</p>
<pre class="brush: ruby;">
irb(main):003:0&gt; &quot;documenting&quot;.unpack(&quot;c*&quot;).sort.pack(&quot;c*&quot;)
=&gt; &quot;cdegimnnotu&quot;
irb(main):004:0&gt; (&quot;coming&quot; + &quot;tuned&quot;).unpack(&quot;c*&quot;).sort.pack(&quot;c*&quot;)
=&gt; &quot;cdegimnnotu&quot;
</pre>
<p>The strings are equal, so we have a match. But this comparison is terribly slow! What&#8217;s worse, the computations have to be redone for each match. It would be much better to just compare hash values, and find a hash function to quickly check if we might have a match, and only do the string comparison when the hash check matches. The hash has to be good enough that we don&#8217;t have too much false positives (hashes are equal but the real comparisons not) to get a speed advantage. So why not just sum up all the letters bytes? </p>
<pre class="brush: ruby;">
irb(main):005:0&gt; &quot;documenting&quot;.sum
=&gt; 1181
irb(main):006:0&gt; &quot;coming&quot;.sum + &quot;tuned&quot;.sum
=&gt; 1181
</pre>
<p>Ruby&#8217;s <a href="http://www.ruby-doc.org/core/classes/String.html#M000857">String#sum</a> does exactly this. we can now precalculate the sum for each word, and to find a match we just add the two hashes and compare the result to the query&#8217;s hash:</p>
<pre class="brush: ruby;">
irb(main):007:0&gt; query=&quot;documenting&quot;; first=&quot;coming&quot;; second=&quot;tuned&quot;
=&gt; &quot;tuned&quot;
irb(main):008:0&gt; first.sum + second.sum == query.sum
=&gt; true
</pre>
<p>When this very quick check returns true, we have to do the string comparison to be absolutely sure it is a match. This considerably speeds up the whole program, but it is still O(n^2).</p>
<h2>Idea #3: Reformulate Problem</h2>
<p>Now here comes the trickiest and coolest part. Since Idea #2 the slowest part is matching the numbers, with still quadratic complexity. But the hard task is not anagram finding any more, we have reduced it to finding two hashes that combined have the same hash as the query. We can reformulate this problem into something completely detached from the anagram problem:</p>
<blockquote><p>
Given a list of numbers, find all combination of two numbers that add up to a given number
</p></blockquote>
<p>When we concentrate on just this problem and ignore the rest, we might come up with a better way of doing things.</p>
<p>I came up with a fast solution, described below. Somebody posted a better solution that is both faster and simpler, if you want just this final solution <a href="#idea4">skip ahead to Idea #4</a> as the following description is outdated.</p>
<p>It clearly looks stupid to just try all combinations to add the numbers.<br />
So lets sort them first. Quicksort is fast, especially with numbers, so no worries here. Now consider a list of numbers like this example:</p>
<pre>1   3   7   10   10   12   17   20   22   23   24   24   25   26   30</pre>
<p>Find all the combinations of two number that add up to 27. They are</p>
<ul>
<li>1 + 26 = 27
<li>3 + 24 = 27
<li>7 + 20 = 27
<li>10 + 17 = 27
<li>10 + 17 = 27 (a second time)
</ul>
<p>You can detect a pattern here: the first number always increases, the second number always decreases! We can now formulate an algorithm for this:</p>
<p>We can have two pointers to the array, one starting from the left side, the other starting from the right side. When the numbers behind the pointers add up to a bigger result than the query (e.g. 1 + 30 = 31), we decrease the right pointer to find a smaller combination (1 + 26 = 27). When the sums are too small (1 + 25 = 26), we move the left pointer to the right (3 + 25 = 28).</p>
<p>This way we walk through the whole array in O(n) time and the sum of the pointers is always kept as close the the desired result as possible. When the pointers meet each other, we can stop the whole process or otherwise we would just reverse the words. </p>
<p>This algorithm gets a bit more complicated when you consider that we might have lots of numbers in it that are equal, whenever this happens you have to fall back into an O(n^2) matching algorithm for just this section.</p>
<h2><a name="idea4"></a>Idea #4: Use Hash directly</h2>
<p><b>UPDATE</b> Scrap the implementation in idea #3. A blog post here from a reader of this article posted a way to do this really in O(n), without any sorting which is O(n*log(n)). The idea is to use a hashmap that maps from the hash key of the word to its matches:</p>
<pre class="brush: ruby;">
M = {}
S = the target sum
for each element e in the list
      if M[S-e] exists? (e,S-e) is a pair
      add e to the M
</pre>
<p>Just use a Hashmap that maps from the cummulative hash of a word to a list of words that have the same hash. Whenever a new word is added, get the list of words that is stored under <tt>query.sum - current_word.sum</tt>. When the hashes are the same we just have to create a list of all the matches under this key, and check each of the matches sequentially for equality. This is just normal hash collision handling through a linked list. That&#8217;s very simple and works like a charm.</p>
<p>I have revised the code, it got both simpler and faster. That&#8217;s a win-win situation, wohoo! </p>
<h1>The Sourcecode</h1>
<p>I hope the code is understandable now with the above explanation. If you have any questions or ideas, please share them here!</p>
<pre class="brush: ruby;">
#!/usr/bin/ruby

# created by Martin Ankerl http://martin.ankerl.com/

class String
	# creates an array of characters
	def letters
		unpack(&quot;c*&quot;)
	end
end

class Array
	# converts an array of letters back into a String
	def word
		pack(&quot;c*&quot;)
	end
end

query = &quot;documenting&quot;
query_letters_sorted = query.letters.sort
txt = File.read('wordlist.txt').downcase

# to quickly check if a letter is part of the query word
used_letters = Array.new(256, nil)
query_letters_sorted.each do |letter|
	used_letters[letter] = true
end

# Maps from cummulative hash of a word to a list of words that have this hash code.
hashToWords = Hash.new do |hash, key|
	hash[key] = Array.new
end

query_hash = query.sum

prev = 0
txt_size = txt.size
separator = 10
idx = txt.index(separator, prev)
while prev &lt; txt_size

	letter_idx = prev

	# no need to check end of word because it is \n
	# which is not part of the word anyways
	while used_letters[txt[letter_idx]]
		letter_idx += 1
	end

	# ignore word if the above quick check fails
	if letter_idx == idx
		word = txt[prev, idx-prev]

		# check all key matches
		key = word.sum
		hashToWords[query_hash - key].each do |other_word|
			if (word.letters + other_word.letters).sort == query_letters_sorted
				puts &quot;#{word} #{other_word}&quot;
				puts &quot;#{other_word} #{word}&quot;
			end
		end

		# insert word
		hashToWords[key] &lt;&lt; word
	end

	prev = idx + 1

	# no need to check end of file because we have to end with new line
	idx = txt.index(separator, prev)
end
</pre>
<p>When you rewrite the algorithm in C++ or Java or Python I am sure it will be faster than this one. But this is not the point of this post. The point is, &#8220;The Best Optimizer is between Your Ears&#8221; (Michael Abrash, <a href="http://www.byte.com/abrash/">Graphics Programming Black Book</a>).</p>
<p>Have fun!</p>
<div style='clear:both'></div><img src="http://martin.ankerl.com/?ak_action=api_record_view&id=156&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://martin.ankerl.com/2008/08/09/two-word-anagram-finder-algorithm/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>New Release of XDCC-Fetch</title>
		<link>http://martin.ankerl.com/2007/11/04/new-release-of-xdcc-fetch/</link>
		<comments>http://martin.ankerl.com/2007/11/04/new-release-of-xdcc-fetch/#comments</comments>
		<pubDate>Sun, 04 Nov 2007 14:41:09 +0000</pubDate>
		<dc:creator>Martin Ankerl</dc:creator>
				<category><![CDATA[coding]]></category>
		<category><![CDATA[freeware]]></category>
		<category><![CDATA[news]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[release]]></category>
		<category><![CDATA[xdcc]]></category>
		<category><![CDATA[xdcc-fetch]]></category>

		<guid isPermaLink="false">http://martin.ankerl.com/?p=99</guid>
		<description><![CDATA[XDCC-Fetch is a nice little application written in Ruby that is able to download from XDCC bots on IRC. I have updated it to work with fox 1.6, so this should work with the recent Ruby version. Screenshot Unfortunately I don&#8217;t really have the time nor the interest to continue development for XDCC-Fetch. Please contact [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://xdccfetch.sourceforge.net/">XDCC-Fetch</a> is a nice little application written in Ruby that is able to download from <a href="http://en.wikipedia.org/wiki/XDCC">XDCC</a> bots on IRC. I have updated it to work with fox 1.6, so this should work with the recent Ruby version.</p>
<h1>Screenshot</h1>
<p><center><br />
<img src="/files/xdcc-fetch.png" width="527" height="488"><br />
</center></p>
<p>Unfortunately I don&#8217;t really have the time nor the interest to continue development for XDCC-Fetch. Please <a href="mailto:martin.ankerl@gmail.com">contact me</a> if you are interested to continue development.</p>
<div style='clear:both'></div><img src="http://martin.ankerl.com/?ak_action=api_record_view&id=99&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://martin.ankerl.com/2007/11/04/new-release-of-xdcc-fetch/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>New Release of Dice-RPG</title>
		<link>http://martin.ankerl.com/2007/11/04/new-release-of-dice-rpg/</link>
		<comments>http://martin.ankerl.com/2007/11/04/new-release-of-dice-rpg/#comments</comments>
		<pubDate>Sun, 04 Nov 2007 14:19:00 +0000</pubDate>
		<dc:creator>Martin Ankerl</dc:creator>
				<category><![CDATA[freeware]]></category>
		<category><![CDATA[fun]]></category>
		<category><![CDATA[news]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[Dice-RPG]]></category>
		<category><![CDATA[release]]></category>
		<category><![CDATA[RPG]]></category>

		<guid isPermaLink="false">http://martin.ankerl.com/?p=98</guid>
		<description><![CDATA[I was bored today so I have updated my little program Dice-RPG to work with fox 1.6. What? Dice-RPG is a free dice throwing program that can be used for role playing games. Although I have never played a RPG in my entire life, my brother forced me to write this tool for the good [...]]]></description>
			<content:encoded><![CDATA[<p>I was bored today so I have updated my little program Dice-RPG to work with fox 1.6.</p>
<h1>What?</h1>
<p>Dice-RPG is a free dice throwing program that can be used for role playing games. Although I have never played a RPG in my entire life, my <a href="http://wyrm-chris.livejournal.com/">brother</a> forced me to write this tool for the good of mankind (actually, I wrote it because I did not have anything better to do, but don&#8217;t tell him).</p>
<p>Here is a screenshot:<br />
<center><br />
<img src="/files/dice-rpg.png" width="358" height="325"><br />
</center></p>
<h1>How?</h1>
<p>To use Dice-RPG,</p>
<ol>
<li>Install the Ruby one-click installer from <a href="http://www.ruby-lang.org/">here</a> if you don&#8217;t have it already. This is a runtime, like Java or C#.
<li>Download <a href="/files/dice-rpg.rbw">Dice-RPG.rbw</a> (right click and save the link) and doubleclick it.
</ol>
<p>Have fun!</p>
<div style='clear:both'></div><img src="http://martin.ankerl.com/?ak_action=api_record_view&id=98&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://martin.ankerl.com/2007/11/04/new-release-of-dice-rpg/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>RFind &#8211; Quickly Find Files</title>
		<link>http://martin.ankerl.com/2007/04/01/rfind-quickly-find-files/</link>
		<comments>http://martin.ankerl.com/2007/04/01/rfind-quickly-find-files/#comments</comments>
		<pubDate>Sun, 01 Apr 2007 19:35:05 +0000</pubDate>
		<dc:creator>Martin Ankerl</dc:creator>
				<category><![CDATA[freeware]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[release]]></category>
		<category><![CDATA[RFind]]></category>

		<guid isPermaLink="false">http://martin.ankerl.com/?p=86</guid>
		<description><![CDATA[RFind is a little application that indexes the filenames of a given directory, and allows to quickly search this index with regular expressions. The motivation behind this app was that someone thought this had to be in C++ to be fast, so I proved him wrong: search-on-typing with more than 500,000 indexed filenames is easily [...]]]></description>
			<content:encoded><![CDATA[<p>RFind is a little application that indexes the filenames of a given directory, and allows to quickly search this index with regular expressions.</p>
<p>The motivation behind this app was that someone thought this had to be in C++ to be fast, so I proved him wrong: search-on-typing with more than 500,000 indexed filenames is easily possible <img src='http://martin.ankerl.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>I have tried to make this little tool very configurable so that is can be useful to everyone. Some of the features are:</p>
<ul>
<li>Hierarchical presented search results
<li>Search-on-typing
<li>Define rules to execute on mouseclick
</ul>
<p><strong>Download:</strong></p>
<ul>
<li><a href="/files/rfind-1.0.zip">rfind-1.0.zip</a></li>
</ul>
<p>This tool is written in Ruby and requires fxruby 1.0, which is a bit out of date. I will try to update it to a recent version of fxruby soon.</p>
<div style='clear:both'></div><img src="http://martin.ankerl.com/?ak_action=api_record_view&id=86&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://martin.ankerl.com/2007/04/01/rfind-quickly-find-files/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TextAnalyzer in Python</title>
		<link>http://martin.ankerl.com/2007/03/29/textanalyzer-in-python/</link>
		<comments>http://martin.ankerl.com/2007/03/29/textanalyzer-in-python/#comments</comments>
		<pubDate>Thu, 29 Mar 2007 19:05:56 +0000</pubDate>
		<dc:creator>Martin Ankerl</dc:creator>
				<category><![CDATA[coding]]></category>
		<category><![CDATA[freeware]]></category>
		<category><![CDATA[news]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://martin.ankerl.com/?p=85</guid>
		<description><![CDATA[I have just found out that somebody has translated my textanalyzer from Ruby into Python. It also contains some improvements like stopwords. The core algorithm is still the same. Get it at kelpheavyweaponry.com.]]></description>
			<content:encoded><![CDATA[<p>I have just found out that somebody has translated my <a href="http://martin.ankerl.com/2007/01/09/textanalyzer-automatically-extract-characteristic-words/">textanalyzer</a> from Ruby into Python. It also contains some improvements like stopwords. The core algorithm is still the same. Get it at <a href="http://www.kelpheavyweaponry.com/cgi-bin/viewcvs.cgi/trunk/libraries/textanalyze.py">kelpheavyweaponry.com</a>.</p>
<div style='clear:both'></div><img src="http://martin.ankerl.com/?ak_action=api_record_view&id=85&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://martin.ankerl.com/2007/03/29/textanalyzer-in-python/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>e &#8212; New Release of Extract Any Archive</title>
		<link>http://martin.ankerl.com/2007/02/25/e-new-release-of-extract-any-archive/</link>
		<comments>http://martin.ankerl.com/2007/02/25/e-new-release-of-extract-any-archive/#comments</comments>
		<pubDate>Sun, 25 Feb 2007 10:34:04 +0000</pubDate>
		<dc:creator>Martin Ankerl</dc:creator>
				<category><![CDATA[freeware]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[news]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://martin.ankerl.com/?p=84</guid>
		<description><![CDATA[Extract Any Archive just got better: When you extract multiple archives at once, e.g. with e *.rar and some files are not extractable, e continuous to extract the other files and prints an error message with all the failed files when it has finished. More info and download of e is here.]]></description>
			<content:encoded><![CDATA[<p>Extract Any Archive just got better: When you extract multiple archives at once, e.g. with</p>
<pre>e *.rar</pre>
<p>and some files are not extractable, <tt>e</tt> continuous to extract the other files and prints an error message with all the failed files when it has finished. More info and download of <tt>e</tt> is <a href="http://martin.ankerl.com/2006/08/11/program-e-extract-any-archive/">here</a>.</p>
<div style='clear:both'></div><img src="http://martin.ankerl.com/?ak_action=api_record_view&id=84&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://martin.ankerl.com/2007/02/25/e-new-release-of-extract-any-archive/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Open Source Search Engine</title>
		<link>http://martin.ankerl.com/2007/02/08/open-source-search-engine/</link>
		<comments>http://martin.ankerl.com/2007/02/08/open-source-search-engine/#comments</comments>
		<pubDate>Thu, 08 Feb 2007 08:52:45 +0000</pubDate>
		<dc:creator>Martin Ankerl</dc:creator>
				<category><![CDATA[coding]]></category>
		<category><![CDATA[freeware]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[news]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[coop]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[search engine]]></category>

		<guid isPermaLink="false">http://martin.ankerl.com/?p=81</guid>
		<description><![CDATA[Give it a try: http://opensource.ankerl.com/ Thanks to Google&#8217;s new co-op feature I was able to create a customized search engine for open source software. It contains a lot of hosting sites like sourceforge.net and rubyforge.org, but also lots of other sites like eclipse.org, kernel.org, apache.org and many many more. Click to add it to your [...]]]></description>
			<content:encoded><![CDATA[<p>Give it a try:</p>
<p><center><br />
<a href="http://opensource.ankerl.com/">http://opensource.ankerl.com/</a></p>
<form id="searchbox_006156709672261707051:fmqpupckn3m" action="http://www.google.com/cse">
<input type="hidden" name="cx" value="006156709672261707051:fmqpupckn3m" />
<input name="q" type="text" size="40" />
<input type="submit" name="sa" value="Search" />
<input type="hidden" name="cof" value="FORID:1" /></form>
<p><script type="text/javascript" src="http://www.google.com/coop/cse/brand?form=searchbox_006156709672261707051%3Afmqpupckn3m"></script><br />
</center></p>
<p>Thanks to Google&#8217;s new <a href="http://www.google.com/coop/">co-op</a> feature I was able to create a customized search engine for open source software. It contains a lot of hosting sites like <a href="http://sourceforge.net/">sourceforge.net</a> and <a href="http://rubyforge.org/">rubyforge.org</a>, but also lots of other sites like <a href="http://www.eclipse.org/">eclipse.org</a>, <a href="http://kernel.org/">kernel.org</a>, <a href="http://apache.org/">apache.org</a> and many many more.</p>
<p>Click <a href="http://fusion.google.com/add?moduleurl=http%3A%2F%2Fwww.google.com%2Fcoop/api/006156709672261707051/cse/fmqpupckn3m/gadget"><img src="http://buttons.googlesyndication.com/fusion/add.gif" width="104" height="17" border="0" alt="Add to Google" /></a> to add it to your customized Google homepage</p>
<div style='clear:both'></div><img src="http://martin.ankerl.com/?ak_action=api_record_view&id=81&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://martin.ankerl.com/2007/02/08/open-source-search-engine/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TextAnalyzer &#8211; Automatically Extract Characteristic Words</title>
		<link>http://martin.ankerl.com/2007/01/09/textanalyzer-automatically-extract-characteristic-words/</link>
		<comments>http://martin.ankerl.com/2007/01/09/textanalyzer-automatically-extract-characteristic-words/#comments</comments>
		<pubDate>Tue, 09 Jan 2007 14:36:56 +0000</pubDate>
		<dc:creator>Martin Ankerl</dc:creator>
				<category><![CDATA[freeware]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[characteristic words]]></category>
		<category><![CDATA[textanalyzer]]></category>

		<guid isPermaLink="false">http://martin.ankerl.com/?p=80</guid>
		<description><![CDATA[TextAnalzyer is a text analyzer tool that finds out words that are characteristic for a given input file. It is independent from any language, and even seems to work well with HTML files. This program is only a little prototype, that shows that this technique seems to work. It&#8217;s public domain, feel free to do [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://martin.ankerl.com/files/textanalyze.rb">TextAnalzyer</a> is a text analyzer tool that finds out words that are characteristic for a given input file. It is independent from any language, and even seems to work well with HTML files.</p>
<p>This program is only a little prototype, that shows that this technique seems to work. It&#8217;s public domain, feel free to do whatever you like with it:<br />
<span id="more-80"></span></p>
<h1>Download</h1>
<p><a href="http://martin.ankerl.com/files/textanalyze.rb">textanalyze.rb</a>, Licence: Public Domain.</p>
<h1>Example</h1>
<ol>
<li>Build an index with a reasonably large amount of data, it should be much larger than the text you want to analyze. For example, I have indexed 76 of Grimm&#8217;s fairy tales with this command:
<p><code>cat *.txt | ruby ../textanalzye.rb c</code></p>
<p>      This creates the file <tt>wordcount.dat</tt> that contains the word count of each word.</li>
<li>To find out which words are characteristic for a specific text, the previously generated reference data is used. To continue the example:
<p><code>cat LittleRedRidingHood.txt |ruby ../textanalzye.rb a</code></p>
<p>This produces the output</p>
<p><code>hood, grandma, riding, hunter, red</code></p>
<p>So the above words seem to be very relevant to LittleRedRidingHood.txt when compared to all of Grimm&#8217;s tales.
</li>
</ol>
<h1>Other Uses</h1>
<p>The previous example seems a bit useless, but there certainly are a lot of useful applications. Here are some ideas:</p>
<ul>
<li>Quickly find out what an unknown text is about</li>
<li>Automatically extract important words from blog entries</li>
<li>Find out what a text is about by reading just 5 words</li>
<li>Automatically create very short descriptions for a large number of documents</li>
</ul>
<p>The currently implemented algorithm even works well with HTML files (To my own surprise. Actually, I am surprised that it works at all…)</p>
<h1>Algorithm</h1>
<p>The main idea is quite simple: the algorithm assumes, that important words are :</p>
<ol>
<li>Often used in the to-be-analyzed text</li>
<li>Seldom used in other texts</li>
</ol>
<p>For example, the second condition ensures that words like &#8220;the&#8221;, &#8220;and&#8221; etc. are not considered important.</p>
<p>The full algorithm to calculate the score of a word (higher==more important) is done with this formula:</p>
<pre>tanh(curVal/curWords*200) - 5*tanh((allVal-curVal)/(allWords-curWords)*200)</pre>
<p>The variables:</p>
<ul>
<li><tt>curVal</tt>: How often the word to score is present in the to-be-analyzed text.</li>
<li><tt>curWords</tt>: Total number of words in the to-be-analyzed text.</li>
<li><tt>allVal</tt>: How often the word to score is present in the indexed dataset.</li>
<li><tt>allWords</tt>: Total number of words of the indexed dataset.</li>
</ul>
<p>Please don&#8217;t ask me how or why this works. I have no idea. I have invented this formula in one of the rare moments when I was enlighted for approximately 10 seconds, quickly wrote it down, and immediately forgot how it worked because my mind was overwhelmed by its beauty and simplicity&#8230; Or something like that <img src='http://martin.ankerl.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<div style='clear:both'></div><img src="http://martin.ankerl.com/?ak_action=api_record_view&id=80&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://martin.ankerl.com/2007/01/09/textanalyzer-automatically-extract-characteristic-words/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
