<?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>Free Mind</title>
	<atom:link href="http://blog.pluskid.org/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://blog.pluskid.org</link>
	<description>We are drowning in information and starving for knowledge.</description>
	<lastBuildDate>Fri, 02 Jul 2010 13:34:26 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Generate Recursive Images</title>
		<link>http://blog.pluskid.org/?p=580</link>
		<comments>http://blog.pluskid.org/?p=580#comments</comments>
		<pubDate>Thu, 24 Jun 2010 07:10:44 +0000</pubDate>
		<dc:creator>pluskid</dc:creator>
				<category><![CDATA[Develop]]></category>
		<category><![CDATA[Fun]]></category>
		<category><![CDATA[Matlab]]></category>
		<category><![CDATA[Optimization]]></category>

		<guid isPermaLink="false">http://blog.pluskid.org/?p=580</guid>
		<description><![CDATA[<p>在上一篇 blog 中我提到了递归图片，还给了一个有趣的例子，这次还说递归图片，再给另一个例子：</p>
<p></p>
<p>不过这次的例子是我自己生成的，而篇 blog 就是要讲如何来生成这样一张递归图片。其实方法很简单，类推一下，多花一些功夫的话，之前给的那个“二次递归”的例子也是可以“轻松”做出来的。  </p>
<p>秘密就在于不动点迭代。我在上一篇 blog 中已经说过了，这个递归的东西要找的其实是一个“不动点”。对于一个函数  来说，一个不动点  就是满足  的值。而不动点的求法就是一个迭代，简单来说，就是随便找一个初始值  ，带进函数里得到  ，然后再带回去得到  ，如此迭代，如果数列  收敛的话，极限  将会是  的一个不动点。</p>
<p>不过，因为我见过的用不动点迭代来求解的情况  都是比较简单的可以写出解析式的函数，由于思维定式，我觉得图片的情况应该会很复杂没有办法这样来解。后来由 van 大神的提点，让我混乱的思维渐渐清晰起来，其实只要这个函数可以求值就好了，并不需要太多的其他条件，如果迭代收敛的话，就可以得到想要的递归图片了！</p>
<p>具体来说，现在我们有一张大图，里面有一个小框，这个小框里将包含这个大图本身的一个缩小版本，当然，缩小版本里还有更小的版本，如此递归。我们令  为所求的递归图的话，那么令  为不动点迭代的函数，这里的  其实代表了这样的操作：把  缩小，然后再贴到一个背景图上去（这个背景图就是最开始的那张大图里除去那个小框的部分。当然，这个函数如果要写出解析式也许是很复杂，但是给定一张图片作为输入，要求出经过这些运算过后的结果是很简单的，那这样是不是就可以求出前面的递归图片了呢？</p>
<p>我写了简单的 Matlab 代码来验证，随便找了一张场景类似的图来做试验，代码里面硬编码了许多数字，就不要说我代码丑了啊！  </p>

ratio = 0.45;
pi = 759; pj = 1496;
w = 1914; h = 1254;
Nit [...]]]></description>
			<content:encoded><![CDATA[<p>在<a href="/?p=572">上一篇 blog</a> 中我提到了递归图片，还给了一个有趣的例子，这次还说递归图片，再给另一个例子：</p>
<p><a href="/wp-content/uploads/2010/06/iter-11.jpg"><img src="/wp-content/uploads/2010/06/iter-11-small.jpg" alt="" title="iter-11-small" width="550" height="367" class="aligncenter size-full wp-image-582" /></a></p>
<p>不过这次的例子是我自己生成的，而篇 blog 就是要讲如何来生成这样一张递归图片。其实方法很简单，类推一下，多花一些功夫的话，之前给的那个“二次递归”的例子也是可以“轻松”做出来的。 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/happy.png' alt=':)' class='wp-smiley' /> </p>
<p><span id="more-580"></span>秘密就在于不动点迭代。我在上一篇 blog 中已经说过了，这个递归的东西要找的其实是一个“不动点”。对于一个函数 <img src='/latexrender/pictures/8fa14cdd754f91cc6554c9e71929cce7.png' title='f' alt='f' align=absmiddle> 来说，一个不动点 <img src='/latexrender/pictures/9dd4e461268c8034f5c8564e155c67a6.png' title='x' alt='x' align=absmiddle> 就是满足 <img src='/latexrender/pictures/f8abf34599677984dfe91b4f300389f6.png' title='f(x)=x' alt='f(x)=x' align=absmiddle> 的值。而不动点的求法就是一个迭代，简单来说，就是随便找一个初始值 <img src='/latexrender/pictures/3e0d691f3a530e6c7e079636f20c111b.png' title='x_0' alt='x_0' align=absmiddle> ，带进函数里得到 <img src='/latexrender/pictures/4ca04272ee9ec3b52eb5e84c3e701080.png' title='x_1=f(x_0)' alt='x_1=f(x_0)' align=absmiddle> ，然后再带回去得到 <img src='/latexrender/pictures/eab995474aa3de74c20ccfa8f64e3536.png' title='x_2=f(x_1)' alt='x_2=f(x_1)' align=absmiddle> ，如此迭代，如果数列 <img src='/latexrender/pictures/3776a491d03f5aac9065dfe81491ce12.png' title='\{x_i\}' alt='\{x_i\}' align=absmiddle> 收敛的话，极限 <img src='/latexrender/pictures/4b978aeeb04a0d8e960efd51a35c393c.png' title='x^*=\lim_{i\rightarrow \infty}x_i' alt='x^*=\lim_{i\rightarrow \infty}x_i' align=absmiddle> 将会是 <img src='/latexrender/pictures/8fa14cdd754f91cc6554c9e71929cce7.png' title='f' alt='f' align=absmiddle> 的一个不动点。</p>
<p>不过，因为我见过的用不动点迭代来求解的情况 <img src='/latexrender/pictures/8fa14cdd754f91cc6554c9e71929cce7.png' title='f' alt='f' align=absmiddle> 都是比较简单的可以写出解析式的函数，由于思维定式，我觉得图片的情况应该会很复杂没有办法这样来解。后来由 <a href="http://blogs.msdn.com/xiangfan/default.aspx">van 大神</a>的提点，让我混乱的思维渐渐清晰起来，其实只要这个函数可以求值就好了，并不需要太多的其他条件，如果迭代收敛的话，就可以得到想要的递归图片了！</p>
<p>具体来说，现在我们有一张大图，里面有一个小框，这个小框里将包含这个大图本身的一个缩小版本，当然，缩小版本里还有更小的版本，如此递归。我们令 <img src='/latexrender/pictures/9dd4e461268c8034f5c8564e155c67a6.png' title='x' alt='x' align=absmiddle> 为所求的递归图的话，那么令 <img src='/latexrender/pictures/4ea69b9b692b39dd000e70830b90a77f.png' title='x=f(x)' alt='x=f(x)' align=absmiddle> 为不动点迭代的函数，这里的 <img src='/latexrender/pictures/8fa14cdd754f91cc6554c9e71929cce7.png' title='f' alt='f' align=absmiddle> 其实代表了这样的操作：把 <img src='/latexrender/pictures/9dd4e461268c8034f5c8564e155c67a6.png' title='x' alt='x' align=absmiddle> 缩小，然后再贴到一个背景图上去（这个背景图就是最开始的那张大图里除去那个小框的部分。当然，这个函数如果要写出解析式也许是很复杂，但是给定一张图片作为输入，要求出经过这些运算过后的结果是很简单的，那这样是不是就可以求出前面的递归图片了呢？</p>
<p>我写了简单的 Matlab 代码来验证，随便找了一张场景类似的图来做试验，代码里面硬编码了许多数字，就不要说我代码丑了啊！ <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/tongue.png' alt=':p' class='wp-smiley' /> </p>

<div class="wp_syntax"><div class="code"><pre class="matlab" style="font-family:monospace;">ratio = <span style="color: #33f;">0.45</span>;
<span style="color: #0000FF;">pi</span> = <span style="color: #33f;">759</span>; pj = <span style="color: #33f;">1496</span>;
w = <span style="color: #33f;">1914</span>; h = <span style="color: #33f;">1254</span>;
Nit = <span style="color: #33f;">100</span>;
threshold = <span style="color: #33f;">10</span>;
&nbsp;
img = <span style="color: #0000FF;">imread</span><span style="color: #080;">&#40;</span><span style="color:#A020F0;">'input.jpg'</span><span style="color: #080;">&#41;</span>;
x = img;
&nbsp;
<span style="color: #0000FF;">for</span> iit = <span style="color: #33f;">1</span>:Nit
   xsmall = imresize<span style="color: #080;">&#40;</span>x, ratio<span style="color: #080;">&#41;</span>;
   xnew = img;
   xnew<span style="color: #080;">&#40;</span><span style="color: #0000FF;">pi</span>:<span style="color: #0000FF;">pi</span>+h-<span style="color: #33f;">1</span>, pj:pj+w-<span style="color: #33f;">1</span>,:<span style="color: #080;">&#41;</span> = xsmall<span style="color: #080;">&#40;</span><span style="color: #33f;">1</span>:h,<span style="color: #33f;">1</span>:w,:<span style="color: #080;">&#41;</span>;
&nbsp;
   <span style="color: #0000FF;">diff</span> = x-xnew;
   <span style="color: #0000FF;">diff</span> = <span style="color: #0000FF;">sum</span><span style="color: #080;">&#40;</span><span style="color: #0000FF;">sum</span><span style="color: #080;">&#40;</span><span style="color: #0000FF;">sum</span><span style="color: #080;">&#40;</span><span style="color: #0000FF;">diff</span>.*<span style="color: #0000FF;">diff</span><span style="color: #080;">&#41;</span><span style="color: #080;">&#41;</span><span style="color: #080;">&#41;</span>;
   <span style="color: #0000FF;">if</span> <span style="color: #0000FF;">diff</span> &lt; threshold
       <span style="color: #0000FF;">break</span>;
   <span style="color: #0000FF;">end</span>
&nbsp;
   x = xnew;
   <span style="color: #0000FF;">imwrite</span><span style="color: #080;">&#40;</span>x, <span style="color: #0000FF;">sprintf</span><span style="color: #080;">&#40;</span><span style="color:#A020F0;">'iter-%02d.jpg'</span>, iit<span style="color: #080;">&#41;</span>, <span style="color:#A020F0;">'jpg'</span><span style="color: #080;">&#41;</span>;
<span style="color: #0000FF;">end</span></pre></div></div>

<p>结果异常好，这是一张 4272&#215;2848 的图片，在 12 步之内就收敛了，结果就是上面的图。下面再看一张迭代第二步的图：</p>
<p><img src="/wp-content/uploads/2010/06/iter-02-small.jpg" alt="" title="iter-02-small" width="550" height="367" class="aligncenter size-full wp-image-589" /></p>
<p>可是，仔细一点看，有没有发现问题？作弊呀！这明明就是把图片不断缩小贴到那个地方这么一个简单的步骤嘛，什么不动点呢？所谓“收敛”其实就是图片缩小到看不清楚了为止吧！嗯，确实没错，过程就是这样的，但是并不是在作弊，因为这个就是不动点迭代呀，我想数学最美妙的地方应该就在于那些可以很直观地表现为一种简单明了的过程的一些东西了吧。</p>
<p>用同样的方法，加上一些复杂一些的变换，诸如旋转呀，甚至三维扭曲呀之类的，可以做出更 cool 的图来，而之前那篇 blog 里的那张双重递归的图，应该也是可以用双重嵌套迭代的方法求出来了。哎呀，好像原本很有意思的东西被这样一摆明了，倒是觉得有些无聊了。 -.-</p>
<p>不过，如果你感兴趣的话，关于不动点迭代，我还可以多说两句。通常讲到不动点迭代求解通常都会举的一个例子是迭代法求平方根，例如，要求 <img src='/latexrender/pictures/c17e049691f2eb2d93102b7d4b309572.png' title='\sqrt{a}' alt='\sqrt{a}' align=absmiddle> 怎么求？写出一个迭代方程 <img src='/latexrender/pictures/1232ee78d6f409f1894378b0abd8c084.png' title='x=f(x)=0.5(a/x + x)' alt='x=f(x)=0.5(a/x + x)' align=absmiddle> （如果你已经发现了，没错，这就是用牛顿法来求方程 <img src='/latexrender/pictures/4640232a3ac5c44cf089bb3519970345.png' title='a-x^2=0' alt='a-x^2=0' align=absmiddle> 的根得到的式子），很显然，如果 <img src='/latexrender/pictures/bbea2a058e4a1137835d1eb33e049fc4.png' title='x=\sqrt{a}' alt='x=\sqrt{a}' align=absmiddle> ，那么 <img src='/latexrender/pictures/4ea69b9b692b39dd000e70830b90a77f.png' title='x=f(x)' alt='x=f(x)' align=absmiddle> 是成立的，所以说 <img src='/latexrender/pictures/bbea2a058e4a1137835d1eb33e049fc4.png' title='x=\sqrt{a}' alt='x=\sqrt{a}' align=absmiddle> 是它的一个不动点。那么反过来用不动点迭代法求 <img src='/latexrender/pictures/c17e049691f2eb2d93102b7d4b309572.png' title='\sqrt{a}' alt='\sqrt{a}' align=absmiddle> 需要满足什么条件呢？首先是这样产生出来的数列是收敛的，第二是该式子的不动点是唯一的：因为如果不唯一的话，那么虽然 <img src='/latexrender/pictures/c17e049691f2eb2d93102b7d4b309572.png' title='\sqrt{a}' alt='\sqrt{a}' align=absmiddle> 是它的不动点，但是我们求出来的不动点可能是另一个值，并不等于 <img src='/latexrender/pictures/c17e049691f2eb2d93102b7d4b309572.png' title='\sqrt{a}' alt='\sqrt{a}' align=absmiddle> 。</p>
<p>首先，第一个条件是可以证明的，由迭代公式 <img src='/latexrender/pictures/9f0c64e1a7f6d30e93d75be646ac892c.png' title='x_{n+1}=0.5(a/x_n + x_n)' alt='x_{n+1}=0.5(a/x_n + x_n)' align=absmiddle> 产生的数列。我们有：</p>
<pre class="tex"><img src='/latexrender/pictures/d91e84ba1ec47681360cb9c648a55d69.png' title='\displaystyle&#10;x_{n+1}-\sqrt{a} = \frac{1}{2}(\frac{\sqrt{a}}{x_n} + x_n) - \sqrt{a} = \frac{(x_n-\sqrt{a})^2}{2x_n}&#10;' alt='\displaystyle&#10;x_{n+1}-\sqrt{a} = \frac{1}{2}(\frac{\sqrt{a}}{x_n} + x_n) - \sqrt{a} = \frac{(x_n-\sqrt{a})^2}{2x_n}&#10;' align=absmiddle></pre>
<p>当 <img src='/latexrender/pictures/b23e942e69bb22c0a85690a47edd644d.png' title='x_n&gt;0' alt='x_n&gt;0' align=absmiddle> 时，上式右边非负，且</p>
<pre class="tex"><img src='/latexrender/pictures/ade328f8c4afb6b6380dec07eef347a3.png' title='\displaystyle&#10;\frac{(x_n-\sqrt{a})^2}{2x_n} = \left(\frac{1}{2}-\frac{\sqrt{a}}{x_n}\right)(x_n-\sqrt{a}) &lt; (x_n-\sqrt{a})&#10;' alt='\displaystyle&#10;\frac{(x_n-\sqrt{a})^2}{2x_n} = \left(\frac{1}{2}-\frac{\sqrt{a}}{x_n}\right)(x_n-\sqrt{a}) &lt; (x_n-\sqrt{a})&#10;' align=absmiddle></pre>
<p>因此 <img src='/latexrender/pictures/e3b82c2ceb30310f01a6b32c48a6f996.png' title='\{x_n-\sqrt{a}\}' alt='\{x_n-\sqrt{a}\}' align=absmiddle> 是非负递减的数列，故可证收敛性。同样对于 <img src='/latexrender/pictures/039f69b44ec763311d82d648369ddc4a.png' title='x_n &lt; 0' alt='x_n &lt; 0' align=absmiddle> 的时候是对称的，类似可以证明。显然 <img src='/latexrender/pictures/5bef92b1854f9c388d11bfbb1720c05d.png' title='x_n' alt='x_n' align=absmiddle> 的符号依赖于初值 <img src='/latexrender/pictures/3e0d691f3a530e6c7e079636f20c111b.png' title='x_0' alt='x_0' align=absmiddle> 的选取，而在迭代过程中是不会改变符号的。由 <img src='/latexrender/pictures/e3b82c2ceb30310f01a6b32c48a6f996.png' title='\{x_n-\sqrt{a}\}' alt='\{x_n-\sqrt{a}\}' align=absmiddle> 的收敛性可以得到 <img src='/latexrender/pictures/19feba130d76f3f59699d04acb4524bb.png' title='\{x_n\}' alt='\{x_n\}' align=absmiddle> 的收敛性。</p>
<p>接下来，关于第二个条件，我们可以直接举出一个反例 <img src='/latexrender/pictures/e80b8a9f25caab8c25272df52bfcfd02.png' title='x=-\sqrt{a}' alt='x=-\sqrt{a}' align=absmiddle> 也是一个不动点，因此不动点是不唯一的。所以，在这里不动点迭代并不能保证一定会求得我们想要的 <img src='/latexrender/pictures/c17e049691f2eb2d93102b7d4b309572.png' title='\sqrt{a}' alt='\sqrt{a}' align=absmiddle> 这个不动点，而这就和初始值的选取有关系——这也是很多迭代方法的一个通常的毛病。</p>
<p>其实对于这个问题，把图画出来就很明了了，蓝线是 <img src='/latexrender/pictures/50bbd36e1fd2333108437a2ca378be62.png' title='f(x)' alt='f(x)' align=absmiddle> 的图像，而不动点即该图像与 <img src='/latexrender/pictures/252307d8397acf15be49b316720e622f.png' title='g(x)=x' alt='g(x)=x' align=absmiddle> 这条直线的交点，刚好有两个，对应的是 <img src='/latexrender/pictures/1a33a30a7bbad03024a02941e82a5867.png' title='\pm\sqrt{a}' alt='\pm\sqrt{a}' align=absmiddle>：</p>
<p><img src="/wp-content/uploads/2010/06/fx.png" alt="" title="fx" width="550" height="347" class="aligncenter size-full wp-image-595" /></p>
<p>注意上图横纵坐标比例不一样，否则直线应该是 45 度角的。迭代的过程其实就是在 x 轴上取一个点，找出曲线上对应点的纵坐标，作过 y 轴上该纵坐标点出的斜率为 -1 的一条直线，该直线于 x 轴的交点就是新的 x 。这个值会逐渐接近不动点 <img src='/latexrender/pictures/5d9075efa0ec4b2e8228505844f11742.png' title='x^*' alt='x^*' align=absmiddle> 。而从图上也可以看出，这里一共就是两个不动点，得到哪个结果刚好取决于初值的正负性。</p>
<p>那么对于其他情况呢？特别是对于我们递归图像的情况呢？这个迭代过程是否收敛呢？要证明这个收敛性似乎就变得非常复杂了。不过，如果从工程师的心态来看的话，那就先试一试好了，如果结果好，那就皆大欢喜了，管他收敛不收敛！ <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/biggrin.png' alt=':D' class='wp-smiley' /> 实际上，这个迭代生成递归图片的方法，从实验来看的话，似乎还是相当好的，就算是初始值取完全随机生成的像素点，也能很快收敛（嗯嗯，那是当然的啦，本身就是一个很简单的复制、缩小、粘贴过程嘛 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/tongue.png' alt=':p' class='wp-smiley' /> ）。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.pluskid.org/?feed=rss2&amp;p=580</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Beyond Recursion</title>
		<link>http://blog.pluskid.org/?p=572</link>
		<comments>http://blog.pluskid.org/?p=572#comments</comments>
		<pubDate>Wed, 23 Jun 2010 07:30:11 +0000</pubDate>
		<dc:creator>pluskid</dc:creator>
				<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Computer Vision]]></category>
		<category><![CDATA[Fun]]></category>
		<category><![CDATA[Research]]></category>

		<guid isPermaLink="false">http://blog.pluskid.org/?p=572</guid>
		<description><![CDATA[<p>Quine，或者说可以打印自己源代码的程序，通常被认为是比递归更神奇的东西，我以前也聊过这个话题。这次再提起来，是因为发现了另一个有趣的例子。</p>
<p>真相是，今天早上实验室网络检修，没法上网，而且由于考试周的接近自习室和图书馆都成了人山人海，几乎没有我等“散户”的容身之地，于是无聊中就把昨天下载的一篇传说很有趣的论文翻出来看了——发现果然很有趣。其实就是 Dahua 之前曾经介绍过的今年 CVPR 会议上分发的一篇最有趣的论文 Paper Gestalt。论文主要是讲了如何用 Computer Vision 的方法来自动判别提交到会议的论文的好坏并决定是否 accept，Dahua 的 blog 上已经介绍得很详细了，我在这里也就不多说了。总的来说，推荐去看一看论文原文，现在可以可以下载到了，语言既严谨又诙谐，简直就是给不幸被断网的小朋友的最佳读物啊！  看到赫然出现的麦克斯韦方程组的时候，我就乐坏了，当然我认为这篇论文的作者并不是 seriously 真的想要用这个系统来作为以后审稿的辅助工具的，但是这个确实很好玩，更重要的是，我看到作者对这个学术社区现状的反思，当然除了诙谐，还略带一些讽刺的意味。所以呀，学术圈也是有可爱的人以及不无聊的事的！  </p>
<p>不过我看到关于这篇论文的介绍的时候，第一时间想到的问题就是：不知道用它自己的这个方法来测试它自己这篇论文，结果会怎样呢？等我看了论文，才发现原来它已经做了这个实验了，根据论文里说的结果，后验概率是 88.4% （adaboost 我不熟悉，不知道这个概率如何得来的，呼唤 kily），也就是说显然这篇论文应该被 CVPR 录用的。  </p>
<p>那么有意思的地方在哪里呢？这里的问题就和你试图构造一个打印自身的程序是会碰到的几乎一模一样。因为论文里说了，88.4% 的后验概率，这个数字是论文的一部分，也就是说如果没有这个数字的话，这篇论文还是不完整的，也就是说这个 88.4% 的概率应该是那篇不完整的论文的概率。那么好了，我填上 88.4% 之后再运行，这个时候，如果出来的结果不是 88.4% 就惨了，是不是需要再更新一下论文，然后再跑？也许这样永远轮回都达不到目标。这里必须要找到一个“不动点”一样的东西，这个 x% 填到论文这里，再运行分类器，得到的结果刚好也是 x%，看起来算是一个方程，如果说对于printf来说还可以巧妙地构造解的话，对于一个（即使相对简单的）分类器来说大概还是有些困难了。</p>
<p>不过其实这里的问题也没有那么严重，因为论文里用的方法是把一篇 paper 的 PDF 文件转换为图片再连接起来，最后抽取一些诸如 Histograms of Oriented Gradients 一类的视觉feature来进行分类。可以想像一个数字的变动对于这样 feature 抽取的影响其实是非常小的，所以几乎可以忽略不计啦。</p>
<p>只是这篇论文它又给自己出了第二个难题：为了展示算法的工作方式（当然，也许仅仅是为了在视觉上欺骗分类器获得更高的 accept 的概率  ），作者在论文中给出了本论文的 PDF 文件在转化为图片之后连接起来的结果。问题出来了：这张图片里，应该是包含它本身的一个缩略图的，这成了一个循环依赖，要完成论文的内容，必须先有一张论文的缩略图，而论文的缩略图必须是基于论文完整的内容的。</p>
<p>当然缩略图嵌套到一定小的范围内人眼不能分辩了之后，就可以作假了，所以这样的图，或者说至少看起来像这样的图，还是可以构造出来的。不过，在这篇论文里，作者似乎没有去钻这个牛角尖，仔细看它的缩略图，其中的布局也和最终版本的论文有些出入，也许只是简单地用了论文的某一个之前的版本吧。看来还不够完美啊！ [...]]]></description>
			<content:encoded><![CDATA[<p>Quine，或者说可以打印自己源代码的程序，通常被认为是比递归更神奇的东西，我以前也<a href="http://lifegoo.pluskid.org/wiki/Quine.html">聊过这个话题</a>。这次再提起来，是因为发现了另一个有趣的例子。</p>
<p>真相是，今天早上实验室网络检修，没法上网，而且由于考试周的接近自习室和图书馆都成了人山人海，几乎没有我等“散户”的容身之地，于是无聊中就把昨天下载的一篇传说很有趣的论文翻出来看了——发现果然很有趣。其实就是 Dahua 之前<a href="http://dahuasky.spaces.live.com/Blog/cns!1AB3BC993DB84FD7!1168.entry?wa=wsignin1.0&#038;sa=464226818">曾经介绍过的</a>今年 CVPR 会议上分发的一篇最有趣的论文 <a href="http://vision.ucsd.edu/sites/default/files/gestalt.pdf">Paper Gestalt</a>。论文主要是讲了如何用 Computer Vision 的方法来自动判别提交到会议的论文的好坏并决定是否 accept，Dahua 的 blog 上已经介绍得很详细了，我在这里也就不多说了。总的来说，推荐去看一看论文原文，现在可以可以<a href="http://vision.ucsd.edu/sites/default/files/gestalt.pdf">下载到</a>了，语言既严谨又诙谐，简直就是给不幸被断网的小朋友的最佳读物啊！ <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/biggrin.png' alt=':D' class='wp-smiley' /> 看到赫然出现的麦克斯韦方程组的时候，我就乐坏了，当然我认为这篇论文的作者并不是 seriously 真的想要用这个系统来作为以后审稿的辅助工具的，但是这个确实很好玩，更重要的是，我看到作者对这个学术社区现状的反思，当然除了诙谐，还略带一些讽刺的意味。所以呀，学术圈也是有可爱的人以及不无聊的事的！ <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/joyful.png' alt='^_^' class='wp-smiley' /> </p>
<p><span id="more-572"></span>不过我看到关于这篇论文的介绍的时候，第一时间想到的问题就是：不知道用它自己的这个方法来测试它自己这篇论文，结果会怎样呢？等我看了论文，才发现原来它已经做了这个实验了，根据论文里说的结果，后验概率是 88.4% （adaboost 我不熟悉，不知道这个概率如何得来的，呼唤 kily），也就是说显然这篇论文应该被 CVPR 录用的。 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/happy.png' alt=':)' class='wp-smiley' /> </p>
<p>那么有意思的地方在哪里呢？这里的问题就和你试图构造一个打印自身的程序是会碰到的几乎一模一样。因为论文里说了，88.4% 的后验概率，这个数字是论文的一部分，也就是说如果没有这个数字的话，这篇论文还是不完整的，也就是说这个 88.4% 的概率应该是那篇不完整的论文的概率。那么好了，我填上 88.4% 之后再运行，这个时候，如果出来的结果不是 88.4% 就惨了，是不是需要再更新一下论文，然后再跑？也许这样永远轮回都达不到目标。这里必须要找到一个“不动点”一样的东西，这个 x% 填到论文这里，再运行分类器，得到的结果刚好也是 x%，看起来算是一个方程，如果说对于printf来说还可以巧妙地构造解的话，对于一个（即使相对简单的）分类器来说大概还是有些困难了。</p>
<p>不过其实这里的问题也没有那么严重，因为论文里用的方法是把一篇 paper 的 PDF 文件转换为图片再连接起来，最后抽取一些诸如 Histograms of Oriented Gradients 一类的视觉feature来进行分类。可以想像一个数字的变动对于这样 feature 抽取的影响其实是非常小的，所以几乎可以忽略不计啦。</p>
<p>只是这篇论文它又给自己出了第二个难题：为了展示算法的工作方式（当然，也许仅仅是为了在视觉上欺骗分类器获得更高的 accept 的概率 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/tongue.png' alt=':p' class='wp-smiley' /> ），作者在论文中给出了本论文的 PDF 文件在转化为图片之后连接起来的结果。问题出来了：这张图片里，应该是包含它本身的一个缩略图的，这成了一个循环依赖，要完成论文的内容，必须先有一张论文的缩略图，而论文的缩略图必须是基于论文完整的内容的。</p>
<p>当然缩略图嵌套到一定小的范围内人眼不能分辩了之后，就可以作假了，所以这样的图，或者说至少看起来像这样的图，还是可以构造出来的。不过，在这篇论文里，作者似乎没有去钻这个牛角尖，仔细看它的缩略图，其中的布局也和最终版本的论文有些出入，也许只是简单地用了论文的某一个之前的版本吧。看来还不够完美啊！ <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/biggrin.png' alt=':D' class='wp-smiley' /> </p>
<p>最后，记得之前在 Google Reader 上看到很多奇特的递归图，不过怎么也找不到是哪个网站了，只找到这一张照片，贴在这里吧：</p>
<p><a href="http://blog.pluskid.org/wp-content/uploads/2010/06/recursive-photo.jpg"><img src="http://blog.pluskid.org/wp-content/uploads/2010/06/recursive-photo-small.jpg" alt="" title="recursive-photo" width="550" height="413" class="aligncenter size-full wp-image-573" /></a></p>
<p>因为到了太小的尺度就可以“糊弄过关”了，所以奇特的图片还是可以做出来的，而且，有些特殊的例子也不难“做”出来，以前曾经尝试过在虚拟机里通过 X11vnc 远程桌面到本地，于是屏幕就递归了…… <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/tongue.png' alt=':p' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://blog.pluskid.org/?feed=rss2&amp;p=572</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>浅谈流形学习</title>
		<link>http://blog.pluskid.org/?p=533</link>
		<comments>http://blog.pluskid.org/?p=533#comments</comments>
		<pubDate>Sat, 29 May 2010 08:11:23 +0000</pubDate>
		<dc:creator>pluskid</dc:creator>
				<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Manifold]]></category>

		<guid isPermaLink="false">http://blog.pluskid.org/?p=533</guid>
		<description><![CDATA[<p>总觉得即使是“浅谈”两个字，还是让这个标题有些过大了，更何况我自己也才刚刚接触这么一个领域。不过懒得想其他标题了，想起来要扯一下这个话题，也是因为和朋友聊起我自己最近在做的方向。Manifold Learning 或者仅仅 Manifold 本身通常就听起来颇有些深奥的感觉，不过如果并不是想要进行严格的理论推导的话，也可以从许多直观的例子得到一些感性的认识，正好我也就借这个机会来简单地谈一下这个话题吧，或者说至少是我到目前为止对这它的认识。</p>
<p>这两个词，在谈 Manifold 之前，不妨先说说 Learning ，也就是 Machine Learning 。而说道 Machine Learning 而不提一下 Artificial Intelligence 的话似乎又显得有些不厚道。人说 AI 是一门最悲剧的学科，因为每当它的一个子领域发展得像模像样之后，就立马自立门户，从此和 AI “再无瓜葛”了，而 Machine Learning 大概要算是最新的一个典型吧。这就让人有点奇怪，比如说数学，分门别类总算是够多了吧？可以不管怎么分，大家兄弟姐妹也都还承认自己是叫“数学”的。那 AI 呢？我觉得这里有很大一部分是它自身定位的问题。</p>
<p>反正现在我是不太清楚 AI 是做什么的，不知道其他人到底清楚不清楚。Wikipedia 上说</p>
<p>Artificial intelligence (AI) is the intelligence  of machines and the branch of computer science that aims to create it.</p>
<p>可是这相当于一个 tautology ，因为到底什么又是 the intelligence of machines [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/2010/05/earth.png" alt="" title="earth" width="280" height="280" class="alignright size-full wp-image-534" />总觉得即使是“浅谈”两个字，还是让这个标题有些过大了，更何况我自己也才刚刚接触这么一个领域。不过懒得想其他标题了，想起来要扯一下这个话题，也是因为和朋友聊起我自己最近在做的方向。Manifold Learning 或者仅仅 Manifold 本身通常就听起来颇有些深奥的感觉，不过如果并不是想要进行严格的理论推导的话，也可以从许多直观的例子得到一些感性的认识，正好我也就借这个机会来简单地谈一下这个话题吧，或者说至少是我到目前为止对这它的认识。</p>
<p>这两个词，在谈 Manifold 之前，不妨先说说 Learning ，也就是 Machine Learning 。而说道 Machine Learning 而不提一下 Artificial Intelligence 的话似乎又显得有些不厚道。人说 AI 是一门最悲剧的学科，因为每当它的一个子领域发展得像模像样之后，就立马自立门户，从此和 AI “再无瓜葛”了，而 Machine Learning 大概要算是最新的一个典型吧。这就让人有点奇怪，比如说数学，分门别类总算是够多了吧？可以不管怎么分，大家兄弟姐妹也都还承认自己是叫“数学”的。那 AI 呢？我觉得这里有很大一部分是它自身定位的问题。</p>
<p><span id="more-533"></span>反正现在我是不太清楚 AI 是做什么的，不知道其他人到底清楚不清楚。Wikipedia 上说</p>
<blockquote><p>Artificial intelligence (AI) is the intelligence  of machines and the branch of computer science that aims to create it.</p></blockquote>
<p>可是这相当于一个 tautology ，因为到底什么又是 <i>the intelligence of machines</i> 呢？一开始的时候，大牛们都野心勃勃，而且好像也是信心满满，就好像曾经广泛认为“牛顿定理揭示了宇宙真理，科学剩下的事情只要按照公式来做计算就可以了”一样，大家可能觉得，不出几十年，人类就可以不用思考，交给 AI 来做了。不过我这里并不想再多说诸如什么是“思考”，什么是“智能”之类的以及随之而来的“图灵测试”之类的话题。我想说的是，到头来，AI 到底是什么，这还是一个问题，或者说，AI 在一开始定了一个过高的目标，几十年后，发现情况并不像当年那么乐观，却又有些下不了台了。</p>
<p>这个时候，AI 的一些旁枝或者子领域果断放下面子，丢掉了那个近乎玄幻的目标，逐渐发展成为“正常”的学科，所以也就不再好称为 AI 了。或者说现在的 AI 有两个意思，一个广义的 AI ，包括了所有相关的以及派生的领域，另一个则是狭义的或者经典的 AI ，专门指那些仍然在执着地追求着真正的“智能”的部分，或者说得不好听一点，就是剩下的部分。</p>
<p>Machine Learning 作为离家出走的典型，虽然名字里带了 Learning 一个词，让人乍一看觉得和 Intelligence 相比不过是换了个说法而已，然而事实上这里的 Learning 的意义要朴素得多。我们来看一看 Machine Learning 的典型的流程就知道了，其实有时候觉得和应用数学或者更通俗的数学建模有些类似，通常我们会有需要分析或者处理的数据，根据一些经验和一些假设，我们可以构建一个模型，这个模型会有一些参数（即使是非参数化方法，也是可以类似地看待的），根据数据来求解模型参数的过程，就叫做 Parameter Estimation ，或者 Model Fitting ，但是搞机器学习的人，通常把它叫做 Learning （或者，换一个角度，叫 Training）——因为根据数据归纳出一个有用的模型，这和我们人类“学习”的过程还是挺类似的吧。不过，如果抛开无聊的抠字眼游戏的话，我们可以看到，Machine Learning 已经抛弃了“智能”的高帽子，它的目的就是要解决具体的问题——而并不关心是否是通过一种“智能”的方式类解决的。</p>
<p>说到这里，其实我们构造模型就类似于写一个类，数据就是构造函数的参数，Learning 就是构造函数运行的过程，成功构造一个对象之后，我们就完成了学习。一些 Machine Learning 的问题到这一步就结束了，另一些情况还会使用得到的模型（对象）对后来的数据进行一些处理，通常会是 Inferencing 。到这个时候，又有些像统计里的东西了，所谓“统计推断”嘛。其实原本统计和机器学习研究的不少问题就是交叉在一起的，不过两派人从不同的角度来看待同样的问题。而且，也确实有 Statistical Learning 这么一个说法存在的，可以把他看成是 Machine Learning 的一个子领域（或者是一个分子或者甚至就是 Machine Learning 本身）。</p>
<p><img src="/wp-content/uploads/2010/05/sphere.png" alt="" title="sphere" width="200" height="200" class="alignleft size-full wp-image-544" />到这里，如果你还没有因为不断地抠字眼而烦躁的话，我已经忍无可忍了。所以，我就假定你已经了解了什么叫 Learning ，或者是已经恶心到懒得去了解了。于是我们转入下一个话题：流形，也就是 Manifold 。不知道你有没有为我在本文开头放上的那个地球的图片感到困惑？这是因为球面是一个很典型的流形的例子，而地球就是一个很典型的“球面”啦（姑且当作球面好啦）。</p>
<p>有时候经常会在 paper 里看到“嵌入在高维空间中的低维流形”，不过高维的数据对于我们这些可怜的低维生物来说总是很难以想像，所以最直观的例子通常都会是嵌入在三维空间中的二维或者一维流行。比如说一块布，可以把它看成一个二维平面，这是一个二维的欧氏空间，现在我们（在三维）中把它扭一扭，它就变成了一个流形（当然，不扭的时候，它也是一个流形，欧氏空间是流形的一种特殊情况）。</p>
<p>所以，直观上来讲，一个流形好比是一个 d 维的空间，在一个 m 维的空间中 (m > d) 被扭曲之后的结果。需要注意的是，流形并不是一个“形状”，而是一个“空间”，如果你觉得“扭曲的空间”难以想象，那么请再回忆之前一块布的例子。如果我没弄错的话，广义相对论似乎就是把我们的时空当作一个四维流（空间三维加上时间一维）形来研究的，引力就是这个流形扭曲的结果。当然，这些都是直观上的概念，其实流形并不需要依靠嵌入在一个“外围空间”而存在，稍微正式一点来说，一个 d 维的流形就是一个在任意点出局部<a href="http://en.wikipedia.org/wiki/Isomorphism">同胚</a>于（简单地说，就是正逆映射都是光滑的一一映射）欧氏空间 <img src='/latexrender/pictures/6784e305cb73c71f585ead18c6fbfe68.png' title='\textcal{R}^d' alt='\textcal{R}^d' align=absmiddle> 。<img src="/wp-content/uploads/2010/05/star.png" alt="" title="star" width="310" height="240" class="alignright size-full wp-image-547" />实际上，正是这种局部与欧氏空间的同胚给我们带来了很多好处，这使得我们在日常生活中许许多多的几何问题都可以使用简单的欧氏几何来解决，因为和地球的尺度比起来，我们的日常生活就算是一个很小的局部啦——我突然想起《七龙珠》里的那个界王住的那种私人小星球，走几步就要绕一圈的感觉，看来界王不仅要体力好（那上面重力似乎是地球的十倍），而且脑力也要好，初中学的必须是<a href="http://en.wikipedia.org/wiki/Riemannian_geometry">黎曼几何</a>了！</p>
<p>那么，除了地球这种简单的例子，实际应用中的数据，怎么知道它是不是一个流形呢？于是不妨又回归直观的感觉。再从球面说起，如果我们事先不知道球面的存在，那么球面上的点，其实就是三维欧氏空间上的点，可以用一个三元组来表示其坐标。但是和空间中的普通点不一样的是，它们允许出现的位置受到了一定的限制，具体到球面，可以可以看一下它的参数方程：</p>
<pre class="tex"><img src='/latexrender/pictures/8590944d3093f05ae3ba2ef4bbd14749.png' title='\displaystyle\begin{aligned}&#10;x &amp;= x_0 + r \sin \theta \; \cos \varphi \\&#10;y &amp;= y_0 + r \sin \theta \; \sin \varphi \qquad (0 \leq \varphi \leq 2\pi \mbox{ and } 0 \leq \theta \leq \pi )\\&#10;z &amp;= z_0 + r \cos \theta&#10;\end{aligned}' alt='\displaystyle\begin{aligned}&#10;x &amp;= x_0 + r \sin \theta \; \cos \varphi \\&#10;y &amp;= y_0 + r \sin \theta \; \sin \varphi \qquad (0 \leq \varphi \leq 2\pi \mbox{ and } 0 \leq \theta \leq \pi )\\&#10;z &amp;= z_0 + r \cos \theta&#10;\end{aligned}' align=absmiddle></pre>
<p>可以看到，这些三维的坐标实际上是由两个变量 <img src='/latexrender/pictures/2554a2bb846cffd697389e5dc8912759.png' title='\theta' alt='\theta' align=absmiddle> 和 <img src='/latexrender/pictures/87567e37a1fe699fe1c5d3a79325da6f.png' title='\varphi' alt='\varphi' align=absmiddle> 生成的，也可以说成是它的自由度是二，也正好对应了它是一个二维的流形。有了这样的感觉之后，再来看流形学习里经常用到的人脸的例子，就很自然了。下图是 <a href="http://isomap.stanford.edu/">Isomap</a> 论文里的一个结果：</p>
<p><img src="/wp-content/uploads/2010/05/isomap.jpg" alt="" title="isomap" width="547" height="411" class="aligncenter size-full wp-image-551" /></p>
<p>这里的图片来自同一张人脸（好吧，其实是人脸模型），每张图片是 64&#215;64 的灰度图，如果把位图按照列（或行）拼起来，就可以得到一个 4096 维的向量，这样一来，每一张图片就可以看成是 4096 维欧氏空间中的一个点。很显然，并不是 4096 维空间中任意一个点都可以对应于一张人脸图片的，这就类似于球面的情形，我们可以假定所有可以是人脸的 4096 维向量实际上分布在一个 d 维 (d < 4096) 的子空间中。而特定到 Isomap 的人脸这个例子，实际上我们知道所有的 698 张图片是拍自同一个人脸（模型），不过是在不同的 pose 和光照下拍摄的，如果把 pose （上下和左右）当作两个自由度，而光照当作一个自由度，那么这些图片实际只有三个自由度，换句话说，存在一个类似于球面一样的参数方程（当然，解析式是没法写出来的），给定一组参数（也就是上下、左右的 pose 和光照这三个值），就可以生成出对应的 4096 维的坐标来。换句话说，这是一个嵌入在 4096 维欧氏空间中的一个 3 维流形。</p>
<p>实际上，上面的那张图就是 Isomap 将这个数据集从 4096 维映射到 3 维空间中，并显示了其中 2 维的结果，图中的小点就是每个人脸在这个二维空间中对应的坐标位置，其中一些标红圈的点被选出来，并在旁边画上了该点对应的原始图片，可以很直观地看出这两个维度正好对应了 pose 的两个自由度平滑变化的结果。</p>
<p>就我目前所知，把流形引入到机器学习领域来主要有两种用途：一是将原来在欧氏空间中适用的算法加以改造，使得它工作在流形上，直接或间接地对流形的结构和性质加以利用；二是直接分析流形的结构，并试图将其映射到一个欧氏空间中，再在得到的结果上运用以前适用于欧氏空间的算法来进行学习。</p>
<p>这里 Isomap 正巧是一个非常典型的例子，因为它实际上是通过“改造一种原本适用于欧氏空间的算法”，达到了“将流形映射到一个欧氏空间”的目的。 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/happy.png' alt=':)' class='wp-smiley' /> </p>
<p>Isomap 所改造的这个方法叫做 <a href="http://en.wikipedia.org/wiki/Multidimensional_scaling">Multidimensional Scaling (MDS)</a> ，MDS 是一种降维方法，它的目的就是使得降维之后的点两两之间的距离尽量不变（也就是和在原是空间中对应的两个点之间的距离要差不多）。只是 MDS 是针对欧氏空间设计的，对于距离的计算也是使用欧氏距离来完成的。如果数据分布在一个流形上的话，欧氏距离就不适用了。</p>
<p>让我们再回到地球——这个在三维空间中的二维流形，假设我们要在三维空间中计算北极点和南极点的距离，这很容易，就是两点相连的线段的长度，可是，如果要在这个流形上算距离就不能这样子算了，我们总不能从北极打个洞钻到南极去吧？要沿着地球表面走才行，当然，如果我随便沿着什么路线走一遍，然后数出总共走了多少步作为距离，这是不成的，因为这样一来如果我沿着不同的路线走，岂不是会得到不同的距离值？总而言之，我们现在需要一个新的定义在地球表面（流形）上的距离度量，理论上来说，任意满足<a href="http://en.wikipedia.org/wiki/Metric_%28mathematics%29#Definition">测度的 4 个条件</a>的函数都可以被定义为距离，不过，为了和欧氏空间对应起来，这里选择一个直线距离的推广定义。</p>
<p>还记得初中学的“两点之间，线段最短”吗？现在，我们反过来说，把线段的概念推广一下，变成“两点之间最短的曲线是线段”，于是流形上的距离定义也就等同于欧氏空间了：流形上两个点之间的距离就是连接两个点的“线段”的长度。虽然只是置换了一个概念，但是现在两者统一起来了，不过，在流形上的线段大概就不一定是“直”的了（于是直线也变成不一定是“直”的了），通常又称作是“测地线”。对于球面这个简单的流形来说，任意一条线段必定是在一个“大圆”上的，于是球面上的直线其实都是一些大圆，也造成了球面这个流形上没有平行线等一系列尴尬的局面（任意两条直线均相交），如果你看过一些数学科普八卦类的书，应该会回忆起不少东西啦！</p>
<p>回到 Isomap ，它主要做了一件事情，就是把 MDS 中原始空间中距离的计算从欧氏距离换为了流形上的测地距离。当然，如果流形的结构事先不知道的话，这个距离是没法算的，于是 Isomap 通过讲数据点连接起来构成一个邻接 Graph 来离散地近似原来的流形，而测地距离也相应地通过 Graph 上的最短路径来近似了。如下图所示：</p>
<p><img src="/wp-content/uploads/2010/05/isomap-graph.png" alt="" title="isomap-graph" width="550" height="419" class="aligncenter size-full wp-image-557" /></p>
<p>这个东西叫做 <a href="http://en.wikipedia.org/wiki/Swiss_roll">Swiss Roll</a> ，姑且把它看作一块卷起来的布好了。图中两个标黑圈的点，如果通过外围欧氏空间中的欧氏距离来计算的话，会是挨得很近的点，可是在流形上它们实际上是距离很远的点：红色的线是 Isomap 求出来的流形上的距离。可以想像，如果是原始的 MDS 的话，降维之后肯定会是很暴力地直接把它投影到二维空间中，完全无视流形结构，而 Isomap 则可以成功地将流形“展开”之后再做投影。</p>
<p>除了 Isomap 之外，Manifold Embedding 的算法还有很多很多，包括 Locally Linear Embedding 、Laplacian Eigenmaps 、Hessian Eigenmaps 、Local Tangent Space Alignment、Semidefinite Embedding (Maximum Variance Unfolding) 等等等等。哪个好哪个坏也不好说，它们都各有特点，而且也各自有不少变种。网上有一个 <a href="http://www.math.ucla.edu/~wittman/mani/index.html">Matlab 的 demo</a> ，给出了几种流行的 manifold embedding 算法在一些 synthetic manifold 上的结果和对比，可以有一个直观的认识。</p>
<p>另一方面是改造现有算法使其适合流形结构甚至专门针对流形的特点来设计新的算法，比较典型的是 graph regularized semi-supervised learning 。简单地说，在 supervised learning 中，我们只能利用有 label 的数据，而（通常都会有很多的）没有 label 的数据则白白浪费掉。在流形假设下，虽然这些数据没有 label ，但是仍然是可以有助于 Learn 出流形的结构的，而学出了流形结构之后实际上我们就是对原来的问题又多了一些认识，于是理所当然地期望能得到更好的结果喽。</p>
<p>当然，所有的这些都是基于同一个假设，那就是数据是分布在一个流形上的（部分算法可能会有稍微宽松一些的假设），然而 real world 的数据，究竟哪些是分别在流形上的呢？这个却是很难说。不过，除了典型的 face 和 hand written digit 之外，大家也有把基于流形的算法直接用在诸如 text 看起来好像也流形没有什么关系的数据上，效果似乎也还不错。</p>
<p>话说回来，虽然和实际应用结合起来是非常重要的一个问题，但是也并不是决定性的，特别是对于搞理论方面的人来说。<img src="/wp-content/uploads/2010/05/ice.png" alt="" title="ice" width="280" height="349" class="alignright size-full wp-image-563" />我想，对于他们来说，其实也像是在做应用啦，不过是把数学里的东西应用到机器学习所研究的问题里来，而且其中关系错综复杂，图论、代数、拓扑、几何……仿佛是十八路诸侯齐聚一堂，所以让我总觉得要再花 500 年来恶补数学才行呀！</p>
<p>不过我表示可以理解其中存在的潜在的 happy 之处，就好比你玩游戏，先玩了《天地劫：神魔至尊传》，然后再玩《天地劫序章：幽城幻剑录》的时候，会发现里面的人物、剧情逐渐地联系起来，也许甚至只是在一个地方出现过的一个完全不起眼的配角，当你再在另一地方突然看到他是，一系列的线索瞬间被联系起来，全局的画面渐渐浮现，那种 happy 的感觉，大概就是所谓的“拨云见日”了吧！ <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/biggrin.png' alt=':D' class='wp-smiley' /> </p>
<p>所以呀，这些东西其实都是差不多的，有时候想想那些泰斗站在顶峰上“一览众山小”的感觉，那种“融会贯通”的感觉，真是让人各种羡慕呀！无奈就只好去爬爬老和山北高峰来体验体验了。 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/tongue.png' alt=':p' class='wp-smiley' /> </p>
<p>最后是总结：所谓 Machine Learning 里的 Learning ，就是在建立一个模型之后，通过给定数据来求解模型参数。而 Manifold Learning 就是在模型里包含了对数据的流形假设。嗯，还有什么？还有就是，我画的坐在楼兰古城城墙上的冰璃，一点都不像…… >_< 幽城迷不要砍我……</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.pluskid.org/?feed=rss2&amp;p=533</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Disable TRAMP on Windows</title>
		<link>http://blog.pluskid.org/?p=526</link>
		<comments>http://blog.pluskid.org/?p=526#comments</comments>
		<pubDate>Wed, 12 May 2010 13:32:56 +0000</pubDate>
		<dc:creator>pluskid</dc:creator>
				<category><![CDATA[Tool]]></category>
		<category><![CDATA[Emacs]]></category>

		<guid isPermaLink="false">http://blog.pluskid.org/?p=526</guid>
		<description><![CDATA[<p>TRAMP 是 Emacs 的一个很重要的组件，可以用于无缝地处理 ssh 、ftp 等远程文件编辑（甚至远程编译、调试等），还包括本地的 sudo 编辑文件等，总之是一个很强大的组件。今天要写这篇关于禁用 TRAMP 的文章，让我一时觉得真是光阴荏苒。</p>
<p>第一是关于 Emacs ，因为我实际上已经好久没有用 Emacs 了，有时候发现不知为什么好像在有意无意地避开它。不过最近编辑 LaTeX 文件比较多，尝试了很多编辑器，最后还是忆起 Emacs 的 cdlatex.el 最趁手，用其他的编辑器来写 TeX 简直就是体力活，所以即使在 Windows 下，也还是决定再启用 Emacs 。第二是关于 TRAMP ，我曾经是很喜欢这个东西的，因为它确实是很好用，如果在中文 Google 上搜索 Emacs TRAMP 的话，第一条的结果依然还是我很古老的那个 blog 上 4 年前的那篇半翻译半整理的文章。</p>
<p>不过这次我真的要禁用 TRAMP 了，因为我是在 Windows 下用 Emacs ，包括 ssh 、sudo 等功能都处于鸡肋或者非常难以配置的状态，而且我也不需要，毕竟我的目的就是编辑 TeX 文件。当然，最主要的原因还是它速度太慢，启动之后第一次按下 C-x C-f 打开文件，总是会在 mini-buffer 显示 loading [...]]]></description>
			<content:encoded><![CDATA[<p>TRAMP 是 Emacs 的一个很重要的组件，可以用于无缝地处理 ssh 、ftp 等远程文件编辑（甚至远程编译、调试等），还包括本地的 sudo 编辑文件等，总之是一个很强大的组件。今天要写这篇关于禁用 TRAMP 的文章，让我一时觉得真是光阴荏苒。</p>
<p>第一是关于 Emacs ，因为我实际上已经好久没有用 Emacs 了，有时候发现不知为什么好像在有意无意地避开它。不过最近编辑 LaTeX 文件比较多，尝试了很多编辑器，最后还是忆起 Emacs 的 cdlatex.el 最趁手，用其他的编辑器来写 TeX 简直就是体力活，所以即使在 Windows 下，也还是决定再启用 Emacs 。第二是关于 TRAMP ，我曾经是很喜欢这个东西的，因为它确实是很好用，如果在中文 Google 上搜索 Emacs TRAMP 的话，第一条的结果依然还是我很古老的那个 blog 上 4 年前的<a href="http://blog.donews.com/pluskid/archive/2006/05/06/858306.aspx">那篇半翻译半整理的文章</a>。</p>
<p><span id="more-526"></span>不过这次我真的要禁用 TRAMP 了，因为我是在 Windows 下用 Emacs ，包括 ssh 、sudo 等功能都处于鸡肋或者非常难以配置的状态，而且我也不需要，毕竟我的目的就是编辑 TeX 文件。当然，最主要的原因还是它速度太慢，启动之后第一次按下 C-x C-f 打开文件，总是会在 mini-buffer 显示 loading tramp&#8230; 然后卡住好几分钟。</p>
<p>我不知道它为什么会这么卡，不过这么 fancy 的东西在 Windows 上比较卡也许也是正常，因为我还开着 ido-mode 呢，或许 Windows 路径里的盘符看着像 ssh 主机地址的 URL 吧，总之现在我只是想禁用它。在我写了上面提到的那篇文章之后不久，TRAMP 成功进入了 Emacs 核心，成为重要组件，结果似乎没有什么比较直接的禁用方法。我搜索了一下，发现网上有讨论说可以有一个选项让 TRAMP 把操作交给 ange-ftp 去做，就能达到间接禁用的目的了。结果下面就有人跟帖说，真好！这么重要的信息应该写到随 Emacs 发布的那个 NEWS 文件里去。后面有人又跟帖说，已经在里面了，祝你好运。 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/biggrin.png' alt=':D' class='wp-smiley' /> </p>
<p>然后这个其实已经写入到 TRAMP 的 FAQ 里去了，在最后一条：</p>
<blockquote><p>
<b>How can I disable tramp?</b></p>
<p>Shame on you, why did you read until now?</p>
<p>If you just want to have Ange-FTP as default remote files access package, you should apply the following code:</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">setq</span> tramp-default-method <span style="color: #ff0000;">&quot;ftp&quot;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>Unloading tramp can be achieved by applying M-x tramp-unload-tramp. This resets also the Ange-FTP plugins.
</p></blockquote>
<p>用了这个方法之后其实也没有完全禁用 TRAMP ，不过现在速度确实变快了很多。原本我以为把 file-name-handler-alist 设置成空就可以完全杜绝，后来发现还是不行。Emacs 果然还是非常深奥啊，不过现在至少是能忍了。 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/tongue.png' alt=':p' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://blog.pluskid.org/?feed=rss2&amp;p=526</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>关于同行互审</title>
		<link>http://blog.pluskid.org/?p=468</link>
		<comments>http://blog.pluskid.org/?p=468#comments</comments>
		<pubDate>Wed, 28 Apr 2010 14:46:35 +0000</pubDate>
		<dc:creator>pluskid</dc:creator>
				<category><![CDATA[Life]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[Thought]]></category>

		<guid isPermaLink="false">http://blog.pluskid.org/?p=468</guid>
		<description><![CDATA[<p>搞科研学术的，对于大部分人来说，发论文就是必不可少的了。可是这个发论文不像平时上课交作业一样，有个老师或者至少是助教来帮你批改，特别是对于一些本身就已经是该领域的领军人物了的人，根本不存在比他们“更高层次”的人来做这种审核。这是一个没法避免的事实，所以学术界几乎都采用同行互审的方式。</p>
<p>其实学术界似乎远没有想象中的那么简单和纯粹，所以我在想是不是什么东西发展到这样一个规模之后必然会有一些诸如潜规则之类的东西出现？总之先不说这个，说 peer review ，其实太具体的东西我也还没有仔细了解过，不过大致的结构还是可以理解的，也就是会有一些比较有声望的人会被选作一个（比如）conference 的评委，然后投到这个会议的论文就（随机地，可能会有一些先验，比如相关研究方向等）分配给各个 reviewer 去审，中间有一些细节可能会有所不同，比如如果自己也投了这个会议的论文的话是不是就不能同时做 reviewer 了，以及 review 到最终决定的过程是否有和原作者之间的 feedback 互动等，但是整体上通常都是如此。不过通常也被划分陈两种比较大的变种，或者说，实际上是三种：</p>

author 公开，reviewer 不公开：也就是最普通的方式了，reviewer 知道自己在审谁的 paper ，但是 author 不知道被谁审了。
author 不公开，reviewer 也不公开：也就是盲审，现在在很多会议上都实行这样的方式。
author 公开，reviewer 也公开：全部公开，似乎不是特别常见，但是也有一些试行。

<p>我想应该第一种是以前最普遍的吧，但是后来人们发现了一个问题，就是如果你是一个很牛的人的话，你的论文或者你挂了名字的论文通常就几乎“百发百中”，一方面可能是大家看到你的名号都不得不给面子，另一方面是一看到你的名号，肯定就会以一种不同的态度（比如，一种仰慕的神色）去读你的论文，相比之下，那些名不见经传的作者通常就比较惨了。</p>
<p>其实这一点我自己也有体会，比如一篇论文是导师告诉你，XX 大牛写的文章，经典，去好好读一下，那么肯定就会无比仰慕地去细读，这个时候是抱着“找亮点”的心情去读的；但是另一方面如果是一篇需要 review 的文章，通常就会是另一种情况——抱着“找毛病”的心情去读。</p>
<p>这似乎也属于发展的趋势，因为现在搞科研学术的人也越来越多，论文的数量也急剧增长，不得不说大部分的论文都还是质量比较低劣的（accept rate 大于 50% 的会议可不多吧？），所以也只好以这样的态度来 review 。所以，有些人就提出要采取盲审的方式，让 reviewer 不知道作者是谁，就不会在 review 的过程中有 bias （或者说不同的 bias ）了。其实简单地说，之前的方式是名人受益，普通人会很惨，而换成盲审之后变成了名人和普通人都很惨了……不过至少看起来有些公平了。 =.=</p>
<p>但是问题还是有，前面说了，peer review 本身就是一个不得已的情况，大家是同行，水平都差不多，凭什么你就能 judge 我的 work 是不是有意义呢？特别是对于大牛来说，让比自己水平低很多的人来 judge 自己的工作似乎就更说不过去了。也确实，并不是所有的 reviewer 都是有能力或者态度端正或者有时间等等的。想想，如果我是一个大牛，我做了很有意义的工作，但是三番五次投出去总是被很弱的 reviewer 看不到我 work [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/2010/04/open.jpg" alt="" title="open" width="200" height="150" class="alignright size-full wp-image-516" />搞科研学术的，对于大部分人来说，发论文就是必不可少的了。可是这个发论文不像平时上课交作业一样，有个老师或者至少是助教来帮你批改，特别是对于一些本身就已经是该领域的领军人物了的人，根本不存在比他们“更高层次”的人来做这种审核。这是一个没法避免的事实，所以学术界几乎都采用同行互审的方式。</p>
<p>其实学术界似乎远没有想象中的那么简单和纯粹，所以我在想是不是什么东西发展到这样一个规模之后必然会有一些诸如潜规则之类的东西出现？总之先不说这个，说 peer review ，其实太具体的东西我也还没有仔细了解过，不过大致的结构还是可以理解的，也就是会有一些比较有声望的人会被选作一个（比如）conference 的评委，然后投到这个会议的论文就（随机地，可能会有一些先验，比如相关研究方向等）分配给各个 reviewer 去审，中间有一些细节可能会有所不同，比如如果自己也投了这个会议的论文的话是不是就不能同时做 reviewer 了，以及 review 到最终决定的过程是否有和原作者之间的 feedback 互动等，但是整体上通常都是如此。不过通常也被划分陈两种比较大的变种，或者说，实际上是三种：</p>
<ol>
<li>author 公开，reviewer 不公开：也就是最普通的方式了，reviewer 知道自己在审谁的 paper ，但是 author 不知道被谁审了。</li>
<li>author 不公开，reviewer 也不公开：也就是盲审，现在在很多会议上都实行这样的方式。</li>
<li>author 公开，reviewer 也公开：全部公开，似乎不是特别常见，但是也有一些<a href="http://portal.acm.org/citation.cfm?id=1273457">试行</a>。</li>
</ol>
<p>我想应该第一种是以前最普遍的吧，但是后来人们发现了一个问题，就是如果你是一个很牛的人的话，你的论文或者你挂了名字的论文通常就几乎“百发百中”，一方面可能是大家看到你的名号都不得不给面子，另一方面是一看到你的名号，肯定就会以一种不同的态度（比如，一种仰慕的神色）去读你的论文，相比之下，那些名不见经传的作者通常就比较惨了。</p>
<p><span id="more-468"></span>其实这一点我自己也有体会，比如一篇论文是导师告诉你，XX 大牛写的文章，经典，去好好读一下，那么肯定就会无比仰慕地去细读，这个时候是抱着“找亮点”的心情去读的；但是另一方面如果是一篇需要 review 的文章，通常就会是另一种情况——抱着“找毛病”的心情去读。</p>
<p>这似乎也属于发展的趋势，因为现在搞科研学术的人也越来越多，论文的数量也急剧增长，不得不说大部分的论文都还是质量比较低劣的（accept rate 大于 50% 的会议可不多吧？），所以也只好以这样的态度来 review 。所以，有些人就提出要采取盲审的方式，让 reviewer 不知道作者是谁，就不会在 review 的过程中有 bias （或者说不同的 bias ）了。其实简单地说，之前的方式是名人受益，普通人会很惨，而换成盲审之后变成了名人和普通人都很惨了……不过至少看起来有些公平了。 =.=</p>
<p>但是问题还是有，前面说了，peer review 本身就是一个不得已的情况，大家是同行，水平都差不多，凭什么你就能 judge 我的 work 是不是有意义呢？特别是对于大牛来说，让比自己水平低很多的人来 judge 自己的工作似乎就更说不过去了。也确实，并不是所有的 reviewer 都是有能力或者态度端正或者有时间等等的。想想，如果我是一个大牛，我做了很有意义的工作，但是三番五次投出去总是被很弱的 reviewer 看不到我 work 的深度结果给 reject 掉了，不郁闷才怪呢！如果这样的事情发生了，至少能说明两个问题：</p>
<ol>
<li>“我”的  RP 已经低到一个很危险的阈值以下了，看来该 BG 提升 RP 了。</li>
<li>盲审制度也是有它的问题的。</li>
</ol>
<p>大概也正是由于这样的原因，所以有些会议迟迟不采取盲审的方式，甚至还有试行之后改回原来的审核方式的吧。不能说业界牛人的 work 总是最好的，但是把它们丢到凡界和大家一起参与“海选”似乎又是另一个极端了。</p>
<p>所以有人提出采用更加 open 的方式，也就是第三种，author 和 reviewer 都公开的方式，公开 reviewer 的好处我想大概是人们不会那么随意地去批评了，如果 review 得不仔细的话，也会被别人笑话的吧，而且也会更加客气一些。不过坏处似乎也是显而易见的，想想拿到一篇某“山头老大”的文章，如果是以前至少还不用担心身份暴露的话，现在是不是会觉得“如果我把这篇文章 reject 了，那以后我投的文章估计也别想中了”？当然我是很夸张地开玩笑，学术界大部分地方应该也还没有黑暗到这种地步的。</p>
<p>不过，不管怎么说，我觉得这种开放式的方式应该还是值得尝试的，因为学术界似乎给人一些有点过于落伍或者说封闭的氛围了。工业界不是现在 Open Source 搞得红红火火？做 research 的人虽然也有许多公布自己的 work 细节甚至源代码和数据，但是许多情况下似乎还是遮遮掩掩的，特别是在工作还没有发表之前，似乎大家也都是经历过了一些惨痛的教训，也就变得如此小心谨慎了。如果工业界封闭源代码都让人觉得有点不爽的话，学术界有时候连自己“正在做什么”这样的信息都不允许透露就更让人觉得奇怪了。</p>
<p>其实有人建议直接增加 conference 中论文的 accept rate ，让大部分论文都可以中，真正的 review 则留给时间和更广大的群众，真正有价值的论文自然是会经过时间过滤，这个是大家都有目共睹的。当然，增加 accept rate 会导致很多人来参加会议，有些比较难处理，相对来说只增加 poster 的命中率要稍微现实一些，不会使得太多的人去做 presentation 导致会议变得旷日持久。</p>
<p>其实更夸张一点，就干脆学习工业界，开源社区那样，把自己的代码放到网上公开的地方，虽然也导致了许多麻烦，从而产生了各种开源协议，而且似乎也没有能有效阻止原本发生的那些诸如剽窃之类的事情。小人总是防不胜防，但是至少开源社区整体还是很成功的一个景象，而且厉害的人物、社区的实力也就自然显示出来了，在公众的注视下，似乎小动作也会少一些。</p>
<p>不过这在学术界可行吗？因为学术界并不是完全和工业界等同的，做科研和写软件并不完全一样，有时候仅仅一个 idea 泄漏出去确实也会是比较严重的事情，万一被别人抢先了呢？这也反应了学术界的一个残酷的事实：没有第二。工业界我们可以百花齐放，同样功能的软件可能会有无数个（比如浏览器、编辑器之类的），而学术界，如果你的工作只是“第二好”，或者你比别人晚了一秒钟做出这个工作，那么你的工作将变得完全没有任何意义。虽然从理想上来说，真正显示你能力的工作，应该是即使你告诉别人了，别人也做不了或者不如你做得好的，可是学术界这么多科研工作者，又有几个是有这样水平的顶尖人物呢？程序员喜欢戏称自己 IT 民工或者码农什么的，做科研的似乎就是称自己在“灌水”了——有时候似乎目的不再那么单纯了，变成了为了发 paper 而发 paper ，甚至其他乱七八糟的东西，但是既然要建立在人类社会这个繁杂的基层之上，我想肯定也就没法单纯起来吧。所以那些真正的领军人物，真正一心做研究的人，才越发显得让人肃然起敬呀。</p>
<p>不过终究世界也是在慢慢发生着变化的，比如 <a href="http://arxiv.org/">arXiv.org</a> 。究竟是好是坏，或者未来会如何，大概还是要得时间来证明吧。 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/happy.png' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://blog.pluskid.org/?feed=rss2&amp;p=468</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>如何生成随机数（上）</title>
		<link>http://blog.pluskid.org/?p=430</link>
		<comments>http://blog.pluskid.org/?p=430#comments</comments>
		<pubDate>Wed, 28 Apr 2010 14:09:22 +0000</pubDate>
		<dc:creator>pluskid</dc:creator>
				<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Statistics]]></category>

		<guid isPermaLink="false">http://blog.pluskid.org/?p=430</guid>
		<description><![CDATA[<p>快三个月没有写日志了，大概是我开始认真写 blog 来第一次，也是因为发生了一些预料之外的事情，中断了许久，到后来又一直非常非常忙，不过我终于又爬上来冒个泡了，表明我还活着。  </p>
<p>第二点要澄清的是，我这里并不是要讲“伪随机”、“真随机”这样的问题，而是关于如何生成服从某个概率分布的随机数（或者说 sample）的问题。比如，你想要从一个服从正态分布的随机变量得到 100 个样本，那么肯定抽到接近其均值的样本的概率要大许多，从而导致抽到的样本很多是集中在那附近的。当然，要解决这个问题，我们通常都假设我们已经有了一个生成 0 到 1 之间均匀分布的随机数的工具，就好像 random.org 给我们的结果那样，事实上许多时候我们也并不太关心它们是真随机数还是伪随机数，看起来差不多就行了。  </p>
<p>现在再回到我们的问题，看起来似乎是很简单的，按照概率分布的话，只要在概率密度大的地方多抽一些样本不就行了吗？可是具体要怎么做呢？要真动起手来，似乎有不是那么直观了。实际上，这个问题曾经也是困扰了我很久，最近又被人问起，那我们不妨在这里一起来总结一下。为了避免一下子就陷入抽象的公式推导，那就还是从一个简单的具体例子出发好了，假设我们要抽样的概率分布其概率密度函数为  ，并且被限制在区间  上，如右上图所示。</p>
<p>好了，假设现在我们要抽 100 个服从这个分布的随机数，直观上来讲，抽出来的接近 3 的数字肯定要比接近 0 的数字要多。那究竟要怎样抽才能得到这样的结果呢？由于我们实际上是不能控制最原始的随机数生成过程的，我们只能得到一组均匀分布的随机数，而这组随机数的生成过程对于我们完全是透明的，所以，我们能做的只有把这组均匀分布的随机数做一些变换让他符合我们的需求。找到下手的点了，可是究竟要怎样变换呢？有一个变换相信大家都是很熟悉的，假设我们有一组  之间的均匀分布的随机数  ，那么令  的话， 就是一组在  之间均匀分布的随机数了，不难想象， 等于某个数  的概率就是  等于  的概率（“等于某个数的概率”这种说法对于连续型随机变量来说其实是不合适的，不过大概可以理解所表达的意思啦）。似乎有一种可以“逆转回去”的感觉了。</p>
<p>于是让我们来考虑更一般的变换。首先，我们知道  的概率密度函数是  ，假设现在我们令  ，不妨先假定  是严格单调递增的函数，这样我们可以求其逆函数  （也是严格单调递增的）。现在来看变换后的随机变量  会服从一个什么样的分布呢？</p>
<p>这里需要小心，因为这里都是连续型的随机变量，并不像离散型随机变量那样可以说成“等于某个值的概率”，因此我们需要转换为概率分布函数来处理，也就是求一个积分啦：</p>

<p>那么  [...]]]></description>
			<content:encoded><![CDATA[<p>快三个月没有写日志了，大概是我开始认真写 blog 来第一次，也是因为发生了一些预料之外的事情，中断了许久，到后来又一直非常非常忙，不过我终于又爬上来冒个泡了，表明我还活着。 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/happy.png' alt=':)' class='wp-smiley' /> </p>
<p><img src="/wp-content/uploads/2010/04/pdf-1.png" alt="" title="pdf-1" width="221" height="177" class="alignright size-full wp-image-435" />第二点要澄清的是，我这里并不是要讲“伪随机”、“真随机”这样的问题，而是关于如何生成服从某个概率分布的随机数（或者说 sample）的问题。比如，你想要从一个服从正态分布的随机变量得到 100 个样本，那么肯定抽到接近其均值的样本的概率要大许多，从而导致抽到的样本很多是集中在那附近的。当然，要解决这个问题，我们通常都假设我们已经有了一个生成 0 到 1 之间均匀分布的随机数的工具，就好像 <a href="http://www.random.org/">random.org</a> 给我们的结果那样，事实上许多时候我们也并不太关心它们是真随机数还是伪随机数，看起来差不多就行了。 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/tongue.png' alt=':p' class='wp-smiley' /> </p>
<p>现在再回到我们的问题，看起来似乎是很简单的，按照概率分布的话，只要在概率密度大的地方多抽一些样本不就行了吗？可是具体要怎么做呢？要真动起手来，似乎有不是那么直观了。实际上，这个问题曾经也是困扰了我很久，最近又被人问起，那我们不妨在这里一起来总结一下。为了避免一下子就陷入抽象的公式推导，那就还是从一个简单的具体例子出发好了，假设我们要抽样的概率分布其概率密度函数为 <img src='/latexrender/pictures/cc7bbecee203df152ff4d13850bbe2ff.png' title='p(x) = \frac{1}{9}x^2' alt='p(x) = \frac{1}{9}x^2' align=absmiddle> ，并且被限制在区间 <img src='/latexrender/pictures/fd78b2f70dafa49beb77b841be00382a.png' title='[0, 3]' alt='[0, 3]' align=absmiddle> 上，如右上图所示。</p>
<p><span id="more-430"></span>好了，假设现在我们要抽 100 个服从这个分布的随机数，直观上来讲，抽出来的接近 3 的数字肯定要比接近 0 的数字要多。那究竟要怎样抽才能得到这样的结果呢？由于我们实际上是不能控制最原始的随机数生成过程的，我们只能得到一组均匀分布的随机数，而这组随机数的生成过程对于我们完全是透明的，所以，我们能做的只有把这组均匀分布的随机数做一些变换让他符合我们的需求。找到下手的点了，可是究竟要怎样变换呢？有一个变换相信大家都是很熟悉的，假设我们有一组 <img src='/latexrender/pictures/ccfcd347d0bf65dc77afe01a3306a96b.png' title='[0,1]' alt='[0,1]' align=absmiddle> 之间的均匀分布的随机数 <img src='/latexrender/pictures/7204a8dfdb61b3496295b04c057e337d.png' title='X_0' alt='X_0' align=absmiddle> ，那么令 <img src='/latexrender/pictures/8ed08f3f3bc2996d8fcaf67a48092303.png' title='X_1=3X_0' alt='X_1=3X_0' align=absmiddle> 的话，<img src='/latexrender/pictures/0d5fa3f335333b23d4aaf795d1336587.png' title='X_1' alt='X_1' align=absmiddle> 就是一组在 <img src='/latexrender/pictures/ed9c05fe24c0f49f5d73f494a921e0c4.png' title='[0,3]' alt='[0,3]' align=absmiddle> 之间均匀分布的随机数了，不难想象，<img src='/latexrender/pictures/0d5fa3f335333b23d4aaf795d1336587.png' title='X_1' alt='X_1' align=absmiddle> 等于某个数 <img src='/latexrender/pictures/5d9075efa0ec4b2e8228505844f11742.png' title='x^*' alt='x^*' align=absmiddle> 的概率就是 <img src='/latexrender/pictures/7204a8dfdb61b3496295b04c057e337d.png' title='X_0' alt='X_0' align=absmiddle> 等于 <img src='/latexrender/pictures/762c80176738565c485c9a2f97b19ef0.png' title='x^*/3' alt='x^*/3' align=absmiddle> 的概率（“等于某个数的概率”这种说法对于连续型随机变量来说其实是不合适的，不过大概可以理解所表达的意思啦）。似乎有一种可以“逆转回去”的感觉了。</p>
<p>于是让我们来考虑更一般的变换。首先，我们知道 <img src='/latexrender/pictures/0d5fa3f335333b23d4aaf795d1336587.png' title='X_1' alt='X_1' align=absmiddle> 的概率密度函数是 <img src='/latexrender/pictures/954dfb31e0ad28718aaa9c48fead247c.png' title='f(x) = 1/3, x\in[0,3]' alt='f(x) = 1/3, x\in[0,3]' align=absmiddle> ，假设现在我们令 <img src='/latexrender/pictures/0363d9a24beaa11cdd996a61a61bda25.png' title='Y = \phi (X_1)' alt='Y = \phi (X_1)' align=absmiddle> ，不妨先假定 <img src='/latexrender/pictures/143c84eda304ce710729dd74d9280351.png' title='\phi(\cdot)' alt='\phi(\cdot)' align=absmiddle> 是严格单调递增的函数，这样我们可以求其逆函数 <img src='/latexrender/pictures/169436856d0b68511d8288eb8caf1f95.png' title='\phi^{-1}(\cdot)' alt='\phi^{-1}(\cdot)' align=absmiddle> （也是严格单调递增的）。现在来看变换后的随机变量 <img src='/latexrender/pictures/57cec4137b614c87cb4e24a3d003a3e0.png' title='Y' alt='Y' align=absmiddle> 会服从一个什么样的分布呢？</p>
<p>这里需要小心，因为这里都是连续型的随机变量，并不像离散型随机变量那样可以说成“等于某个值的概率”，因此我们需要转换为概率分布函数来处理，也就是求一个积分啦：</p>
<pre class="tex"><img src='/latexrender/pictures/24c8b46fce653dd800f9f69ec000ad89.png' title='\displaystyle&#10;F(x) = P(X \leq x) = \int_{-\infty}^x f(t)dt&#10;' alt='\displaystyle&#10;F(x) = P(X \leq x) = \int_{-\infty}^x f(t)dt&#10;' align=absmiddle></pre>
<p>那么 <img src='/latexrender/pictures/0d5fa3f335333b23d4aaf795d1336587.png' title='X_1' alt='X_1' align=absmiddle> 的概率分布函数为 <img src='/latexrender/pictures/8c5a8a95fa12ab9e606ec58ee0619980.png' title='F(x) = \frac{1}{3}x' alt='F(x) = \frac{1}{3}x' align=absmiddle> 。很显然 <img src='/latexrender/pictures/57cec4137b614c87cb4e24a3d003a3e0.png' title='Y' alt='Y' align=absmiddle> 小于或等于某个特定的值 <img src='/latexrender/pictures/67c77cc00e83a60647d826334509d2b3.png' title='y^*' alt='y^*' align=absmiddle> 这件事情是等价于 <img src='/latexrender/pictures/e96e7acb76cb7b3cb46deb1eb36a1cff.png' title='X_1=\phi^{-1}(Y)\leq\phi^{-1}(y^*)' alt='X_1=\phi^{-1}(Y)\leq\phi^{-1}(y^*)' align=absmiddle> 这件事情的。换句话说，<img src='/latexrender/pictures/1f25b21be3187b6a651e05a8e95aa0ee.png' title='P(Y\leq y^*)' alt='P(Y\leq y^*)' align=absmiddle> 等于 <img src='/latexrender/pictures/97afbbde98110d3ebd033a5470478ce2.png' title='P(X_1 \leq \phi^{-1}(y^*))' alt='P(X_1 \leq \phi^{-1}(y^*))' align=absmiddle> 。于是，<img src='/latexrender/pictures/57cec4137b614c87cb4e24a3d003a3e0.png' title='Y' alt='Y' align=absmiddle> 的概率分布函数就可以得到了：</p>
<pre class="tex"><img src='/latexrender/pictures/25bd9d662484d75be643de456d80610e.png' title='\displaystyle&#10;G(y) = P(Y \leq y) = P(X_1 \leq \phi^{-1}(y)) = F(\phi^{-1}(y))&#10;' alt='\displaystyle&#10;G(y) = P(Y \leq y) = P(X_1 \leq \phi^{-1}(y)) = F(\phi^{-1}(y))&#10;' align=absmiddle></pre>
<p>再求导我们就能得到 <img src='/latexrender/pictures/57cec4137b614c87cb4e24a3d003a3e0.png' title='Y' alt='Y' align=absmiddle> 的概率密度函数：</p>
<pre class="tex"><img src='/latexrender/pictures/fa3ec1f5bc5832e8645505d6a48e4b7f.png' title='\displaystyle&#10;g(y) = \frac{dG(y)}{dy} = f(\phi^{-1}(y))\frac{d}{dy}\phi^{-1}(y)&#10;' alt='\displaystyle&#10;g(y) = \frac{dG(y)}{dy} = f(\phi^{-1}(y))\frac{d}{dy}\phi^{-1}(y)&#10;' align=absmiddle></pre>
<p>这样一来，我们就得到了对于一个随机变量进行一个映射 <img src='/latexrender/pictures/143c84eda304ce710729dd74d9280351.png' title='\phi(\cdot)' alt='\phi(\cdot)' align=absmiddle> 之后得到的随即变量的分布，那么，回到我们刚才的问题，我们想让这个结果分布就是我们所求的，然后再反推得 <img src='/latexrender/pictures/143c84eda304ce710729dd74d9280351.png' title='\phi(\cdot)' alt='\phi(\cdot)' align=absmiddle> 即可：</p>
<pre class="tex"><img src='/latexrender/pictures/339c072ed878d6e1c2663aa3f9b1866b.png' title='\displaystyle&#10;\frac{1}{9}y^2 = g(y) = f(\phi^{-1}(y))\frac{d}{dy}\phi^{-1}(y) = \frac{1}{3}\frac{d}{dy}\phi^{-1}(y)&#10;' alt='\displaystyle&#10;\frac{1}{9}y^2 = g(y) = f(\phi^{-1}(y))\frac{d}{dy}\phi^{-1}(y) = \frac{1}{3}\frac{d}{dy}\phi^{-1}(y)&#10;' align=absmiddle></pre>
<p>经过简单的化简就可以得到  <img src='/latexrender/pictures/14aceadfb28b8f4dafd6a032f77dce6f.png' title='\phi^{-1}(y) = \frac{1}{9} y^3' alt='\phi^{-1}(y) = \frac{1}{9} y^3' align=absmiddle> ，亦即 <img src='/latexrender/pictures/1e24a8c50f2d02cb6974e9a85f797e94.png' title='\phi(x) = (9x)^{1/3}' alt='\phi(x) = (9x)^{1/3}' align=absmiddle> 。也就是说，把得到的随机数 <img src='/latexrender/pictures/0d5fa3f335333b23d4aaf795d1336587.png' title='X_1' alt='X_1' align=absmiddle> 带入到到函数 <img src='/latexrender/pictures/143c84eda304ce710729dd74d9280351.png' title='\phi(\cdot)' alt='\phi(\cdot)' align=absmiddle> 中所得到的结果，就是符合我们预期要求的随机数啦！ <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/biggrin.png' alt=':D' class='wp-smiley' /> 让我们来验证一下：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#!/usr/bin/python</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> numpy <span style="color: #ff7700;font-weight:bold;">as</span> np
<span style="color: #ff7700;font-weight:bold;">import</span> matplotlib.<span style="color: black;">pyplot</span> <span style="color: #ff7700;font-weight:bold;">as</span> plot
&nbsp;
N = <span style="color: #ff4500;">10000</span>
X0 = np.<span style="color: #dc143c;">random</span>.<span style="color: black;">rand</span><span style="color: black;">&#40;</span>N<span style="color: black;">&#41;</span>
X1 = <span style="color: #ff4500;">3</span><span style="color: #66cc66;">*</span>X0
Y  = np.<span style="color: black;">power</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">9</span><span style="color: #66cc66;">*</span>X1, <span style="color: #ff4500;">1.0</span>/<span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span>
&nbsp;
t = np.<span style="color: black;">arange</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">3.0</span>, <span style="color: #ff4500;">0.01</span><span style="color: black;">&#41;</span>
y = t<span style="color: #66cc66;">*</span>t/<span style="color: #ff4500;">9</span>
&nbsp;
plot.<span style="color: black;">plot</span><span style="color: black;">&#40;</span>t, y, <span style="color: #483d8b;">'r-'</span>, linewidth=<span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
plot.<span style="color: black;">hist</span><span style="color: black;">&#40;</span>Y, bins=<span style="color: #ff4500;">50</span>, normed=<span style="color: #ff4500;">1</span>, facecolor=<span style="color: #483d8b;">'green'</span>, alpha=<span style="color: #ff4500;">0.75</span><span style="color: black;">&#41;</span>
plot.<span style="color: black;">show</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p><img src="/wp-content/uploads/2010/04/example1.png" alt="" title="example1" width="550" height="420" class="aligncenter size-full wp-image-462" /></p>
<p>这就没错啦，目的达成啦！让我们来总结一下。问题是这样的，我们有一个服从均匀分布的随机变量  <img src='/latexrender/pictures/02129bb861061d1a052c592e2dc6b383.png' title='X' alt='X' align=absmiddle> ，它的概率密度函数为一个常数 <img src='/latexrender/pictures/ada4fc767c55a297011376246862ba0f.png' title='f(x)=C' alt='f(x)=C' align=absmiddle> ，如果是 <img src='/latexrender/pictures/ccfcd347d0bf65dc77afe01a3306a96b.png' title='[0,1]' alt='[0,1]' align=absmiddle> 上的分布，那么常数 <img src='/latexrender/pictures/0d61f8370cad1d412f80b84d143e1257.png' title='C' alt='C' align=absmiddle> 就直接等于 1 了。现在我们要得到一个随机变量 <img src='/latexrender/pictures/57cec4137b614c87cb4e24a3d003a3e0.png' title='Y' alt='Y' align=absmiddle> 使其概率密度函数为 <img src='/latexrender/pictures/cc0952d002442879ab8f7943efd50239.png' title='g(y)' alt='g(y)' align=absmiddle> ，做法就是构造出一个函数 <img src='/latexrender/pictures/143c84eda304ce710729dd74d9280351.png' title='\phi(\cdot)' alt='\phi(\cdot)' align=absmiddle> 满足（在这里加上了绝对值符号，这是因为 <img src='/latexrender/pictures/143c84eda304ce710729dd74d9280351.png' title='\phi(\cdot)' alt='\phi(\cdot)' align=absmiddle> 如果不是递增而是递减的话，推导的过程中有一处就需要反过来）</p>
<pre class="tex"><img src='/latexrender/pictures/87cfff4a3563be0639f55dea0f8013a6.png' title='\displaystyle&#10;g(y) = f(\phi^{-1}(y))\left|\frac{d}{dy}\phi^{-1}(y)\right| = C\left|\frac{d}{dy}\phi^{-1}(y)\right|&#10;' alt='\displaystyle&#10;g(y) = f(\phi^{-1}(y))\left|\frac{d}{dy}\phi^{-1}(y)\right| = C\left|\frac{d}{dy}\phi^{-1}(y)\right|&#10;' align=absmiddle></pre>
<p>反推过来就是，对目标 <img src='/latexrender/pictures/415290769594460e2e485922904f345d.png' title='y' alt='y' align=absmiddle> 的概率密度函数求一个积分（其实就是得到它的概率分布函数 CDF ，如果一开始就拿到的是 CDF 当然更好），然后求其反函数就可以得到需要的变换 <img src='/latexrender/pictures/143c84eda304ce710729dd74d9280351.png' title='\phi(\cdot)' alt='\phi(\cdot)' align=absmiddle> 了。实际上，这种方法有一个听起来稍微专业一点的名字：<a href="http://en.wikipedia.org/wiki/Inverse_transform_sampling_method">Inverse Transform Sampling Method</a> 。不过，虽然看起来很简单，但是实际操作起来却比较困难，因为对于许多函数来说，求逆是比较困难的，求积分就更困难了，如果写不出解析解，不得已只能用数值方法来逼近的话，计算效率就很让人担心了。可事实上也是如此，就连我们最常见的一维标准正态分布，也很难用这样的方法来抽样，因为它的概率密度函数</p>
<pre class="tex"><img src='/latexrender/pictures/8c221c2ef0d48f75d97815a5f7eb0a47.png' title='\displaystyle&#10;g(y) = \frac{1}{\sqrt{2\pi}}e^{-\frac{1}{2}y^2}&#10;' alt='\displaystyle&#10;g(y) = \frac{1}{\sqrt{2\pi}}e^{-\frac{1}{2}y^2}&#10;' align=absmiddle></pre>
<p>的不定积分没有一个解析形式。这可真是一点也不好玩，费了这么大劲，结果好像什么都干不了。看来这个看似简单的问题似乎还是比较复杂的，不过也不要灰心，至少对于高斯分布来说，我们还有一个叫做 <a href="http://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform">Box Muller</a> 的方法可以专门来做这个事情。因为高斯分布比较奇怪，虽然一维的时候概率分布函数无法写出解析式，但是二维的情况却可以通过一些技巧得出一个解析式来。</p>
<p>首先我们来考虑一个二维的且两个维度相互独立的高斯分布，它的概率密度函数为</p>
<pre class="tex"><img src='/latexrender/pictures/9dff76df47d95616f22c096b3bc4c26d.png' title='\displaystyle&#10;f(x,y) = \frac{1}{\sqrt{2\pi}}e^{-\frac{x^2}{2}}\cdot\frac{1}{\sqrt{2\pi}}e^{-\frac{y^2}{2}} = \frac{1}{2\pi}e^{-\frac{x^2+y^2}{2}}&#10;' alt='\displaystyle&#10;f(x,y) = \frac{1}{\sqrt{2\pi}}e^{-\frac{x^2}{2}}\cdot\frac{1}{\sqrt{2\pi}}e^{-\frac{y^2}{2}} = \frac{1}{2\pi}e^{-\frac{x^2+y^2}{2}}&#10;' align=absmiddle></pre>
<p>这个分布是关于原点对称的，如果考虑使用极坐标 <img src='/latexrender/pictures/4673d7f501927e30d77295e73da1629f.png' title='(\theta,r)' alt='(\theta,r)' align=absmiddle> （其中 <img src='/latexrender/pictures/0f20d26e4ccf6c473fa8dad82ec07a62.png' title='\theta\in[0,2\pi), r\in[0,\infty)' alt='\theta\in[0,2\pi), r\in[0,\infty)' align=absmiddle> ）的话，我们有 <img src='/latexrender/pictures/26691e1a265906474f76c18bd015d010.png' title='x = r\cos\theta' alt='x = r\cos\theta' align=absmiddle>，<img src='/latexrender/pictures/0d418bc6ac2910e8f42bb722a1fc219d.png' title='y=r\sin\theta' alt='y=r\sin\theta' align=absmiddle> 这样的变换。这样，概率密度函数是写成：</p>
<pre class="tex"><img src='/latexrender/pictures/6cd14e68c73c84eda5c0cca892da1f5a.png' title='\displaystyle&#10;f(\theta,r) =  \frac{1}{2\pi}e^{-\frac{r^2}{2}}&#10;' alt='\displaystyle&#10;f(\theta,r) =  \frac{1}{2\pi}e^{-\frac{r^2}{2}}&#10;' align=absmiddle></pre>
<p>注意到在给定 <img src='/latexrender/pictures/4b43b0aee35624cd95b910189b3dc231.png' title='r' alt='r' align=absmiddle> 的情况下其概率密度是不依赖于 <img src='/latexrender/pictures/2554a2bb846cffd697389e5dc8912759.png' title='\theta' alt='\theta' align=absmiddle> 的，也就是说对于 <img src='/latexrender/pictures/2554a2bb846cffd697389e5dc8912759.png' title='\theta' alt='\theta' align=absmiddle> 来说是一个均匀分布，这和我们所了解的标准正态分布也是符合的：在一个圆上的点的概率是相等的。确定了 <img src='/latexrender/pictures/2554a2bb846cffd697389e5dc8912759.png' title='\theta' alt='\theta' align=absmiddle> 的分布，让我们再来看 <img src='/latexrender/pictures/4b43b0aee35624cd95b910189b3dc231.png' title='r' alt='r' align=absmiddle>，用类似于前面的方法：</p>
<pre class="tex"><img src='/latexrender/pictures/c109f11cb6bd83ab91af380777e3018c.png' title='\displaystyle&#10;\begin{aligned}&#10;P(r&lt;R) &amp;=  \int_0^{2\pi}\int_0^R\frac{1}{2\pi}e^{\frac{r^2}{2}}rdrd\theta \\&#10; &amp;= \int_0^Re^{-\frac{r^2}{2}}rdr \\&#10; &amp;= 1-e^{-\frac{R^2}{2}}&#10;\end{aligned}&#10;' alt='\displaystyle&#10;\begin{aligned}&#10;P(r&lt;R) &amp;=  \int_0^{2\pi}\int_0^R\frac{1}{2\pi}e^{\frac{r^2}{2}}rdrd\theta \\&#10; &amp;= \int_0^Re^{-\frac{r^2}{2}}rdr \\&#10; &amp;= 1-e^{-\frac{R^2}{2}}&#10;\end{aligned}&#10;' align=absmiddle></pre>
<p>根据前面得出的结论，我现在得到了 <img src='/latexrender/pictures/4b43b0aee35624cd95b910189b3dc231.png' title='r' alt='r' align=absmiddle> 的概率分布函数，是不是只要求一下逆就可以得到一个 <img src='/latexrender/pictures/143c84eda304ce710729dd74d9280351.png' title='\phi(\cdot)' alt='\phi(\cdot)' align=absmiddle> 了？亦即 <img src='/latexrender/pictures/7d891c6681f199a59b749e6ff211aa95.png' title='\phi(t) = \sqrt{-2\log (1-t)}' alt='\phi(t) = \sqrt{-2\log (1-t)}' align=absmiddle> 。</p>
<p>现在只要把这一些线索串起来，假设我们有两个相互独立的平均分布在 <img src='/latexrender/pictures/ccfcd347d0bf65dc77afe01a3306a96b.png' title='[0,1]' alt='[0,1]' align=absmiddle> 上的随机变量 <img src='/latexrender/pictures/2452fee413f58bb9509e88d80d4b9f8d.png' title='T_1' alt='T_1' align=absmiddle> 和 <img src='/latexrender/pictures/6a058d102910f33a7d4cf9ea23067b8c.png' title='T_2' alt='T_2' align=absmiddle> ，那么 <img src='/latexrender/pictures/6256d1ff377418e96d16a76fc48ecd3f.png' title='2\pi T_1' alt='2\pi T_1' align=absmiddle> 就可以得到 <img src='/latexrender/pictures/2554a2bb846cffd697389e5dc8912759.png' title='\theta' alt='\theta' align=absmiddle> 了，而 <img src='/latexrender/pictures/f7218616a844f2031cc5834a7e5cdada.png' title='\phi(T_2) = \sqrt{-2\log(1-T_2)}' alt='\phi(T_2) = \sqrt{-2\log(1-T_2)}' align=absmiddle> 就得到 <img src='/latexrender/pictures/4b43b0aee35624cd95b910189b3dc231.png' title='r' alt='r' align=absmiddle> 了（实际上，由于 <img src='/latexrender/pictures/6a058d102910f33a7d4cf9ea23067b8c.png' title='T_2' alt='T_2' align=absmiddle> 和 <img src='/latexrender/pictures/3970519c9809c47768db00d557a67ff4.png' title='1-T_2' alt='1-T_2' align=absmiddle> 实际上是相同的分布，所以通常直接写为 <img src='/latexrender/pictures/718e7857ce7932cc5df906c3a6d7c628.png' title='\sqrt{-2\log T_2}' alt='\sqrt{-2\log T_2}' align=absmiddle>）。再把极坐标换回笛卡尔坐标：</p>
<pre class="tex"><img src='/latexrender/pictures/251c1179dadcf61d19302e0f49a9387c.png' title='\displaystyle&#10;\begin{aligned}&#10;x = r\cos\theta &amp; = \sqrt{-2\log T_2}\cdot \cos(2\pi T_1) \\&#10;y = r\sin\theta &amp;= \sqrt{-2\log T_2}\cdot \sin(2\pi T_1)&#10;\end{aligned}&#10;' alt='\displaystyle&#10;\begin{aligned}&#10;x = r\cos\theta &amp; = \sqrt{-2\log T_2}\cdot \cos(2\pi T_1) \\&#10;y = r\sin\theta &amp;= \sqrt{-2\log T_2}\cdot \sin(2\pi T_1)&#10;\end{aligned}&#10;' align=absmiddle></pre>
<p>这样我们就能得到一个二维的正态分布的抽样了。可以直观地验证一下，二维不太好画，就画成 heatmap 了，看着比较热的区域就是概率比较大的，程序如下：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#!/usr/bin/python</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> numpy <span style="color: #ff7700;font-weight:bold;">as</span> np
<span style="color: #ff7700;font-weight:bold;">import</span> matplotlib.<span style="color: black;">pyplot</span> <span style="color: #ff7700;font-weight:bold;">as</span> plot
&nbsp;
N = <span style="color: #ff4500;">50000</span>
T1 = np.<span style="color: #dc143c;">random</span>.<span style="color: black;">rand</span><span style="color: black;">&#40;</span>N<span style="color: black;">&#41;</span>
T2 = np.<span style="color: #dc143c;">random</span>.<span style="color: black;">rand</span><span style="color: black;">&#40;</span>N<span style="color: black;">&#41;</span>
&nbsp;
r = np.<span style="color: black;">sqrt</span><span style="color: black;">&#40;</span>-<span style="color: #ff4500;">2</span><span style="color: #66cc66;">*</span>np.<span style="color: black;">log</span><span style="color: black;">&#40;</span>T2<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
theta = <span style="color: #ff4500;">2</span><span style="color: #66cc66;">*</span>np.<span style="color: black;">pi</span><span style="color: #66cc66;">*</span>T1
X = r<span style="color: #66cc66;">*</span>np.<span style="color: black;">cos</span><span style="color: black;">&#40;</span>theta<span style="color: black;">&#41;</span>
Y = r<span style="color: #66cc66;">*</span>np.<span style="color: black;">sin</span><span style="color: black;">&#40;</span>theta<span style="color: black;">&#41;</span>
&nbsp;
heatmap, xedges, yedges = np.<span style="color: black;">histogram2d</span><span style="color: black;">&#40;</span>X, Y, bins=<span style="color: #ff4500;">80</span><span style="color: black;">&#41;</span>
extent = <span style="color: black;">&#91;</span>xedges<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>, xedges<span style="color: black;">&#91;</span>-<span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>, yedges<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>, yedges<span style="color: black;">&#91;</span>-<span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: black;">&#93;</span>
&nbsp;
plot.<span style="color: black;">imshow</span><span style="color: black;">&#40;</span>heatmap, extent=extent<span style="color: black;">&#41;</span>
plot.<span style="color: black;">show</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>画出来的图像这个样子：</p>
<p><a href="/wp-content/uploads/2010/04/example2.png"><img src="http://blog.pluskid.org/wp-content/uploads/2010/04/example2.png" alt="" title="example2" width="546" height="514" class="aligncenter size-full wp-image-501" /></a></p>
<p>不太好看，但是大概的形状是可以看出来的。其实有了二维的高斯分布，再注意到两个维度在我们这里是相互独立的，那么直接取其中任意一个维度，就是一个一维高斯分布了。如下：</p>
<p><img src="http://blog.pluskid.org/wp-content/uploads/2010/04/example3.png" alt="" title="example3" width="550" height="417" class="aligncenter size-full wp-image-503" /></p>
<p>如果 <img src='/latexrender/pictures/8502f9f731d3ff9caad07c4d1f7867be.png' title='X\sim N(0,1)' alt='X\sim N(0,1)' align=absmiddle> 即服从标准正态分布的话，则有 <img src='/latexrender/pictures/54dfe92128a753f9aaa781f3bb37c886.png' title='\sigma X+\mu \sim N(\mu, \sigma^2)' alt='\sigma X+\mu \sim N(\mu, \sigma^2)' align=absmiddle> ，也就是说，有了标准正态分布，其他所有的正态分布的抽样也都可以完成了。这下总算有点心满意足了。不过别急，还有最后一个问题：多元高斯分布。一般最常用不就是二元吗？二元不是我们一开始就推出来了吗？推出来了确实没错，不过我们考虑的是最简单的情形，当然同样可以通过 <img src='/latexrender/pictures/05283bb617a9a86a85c3381ba91458c0.png' title='\sigma X+\mu' alt='\sigma X+\mu' align=absmiddle> 这样的方式来处理每一个维度，不过高维的情形还有一个需要考虑的就是各个维度之间的相关性——我们之前处理的都是两个维度相互独立的情况。对于一般的多维正态分布 <img src='/latexrender/pictures/cfe07334b3015f61a0fcada98ce93e8d.png' title='X\sim N(\mathbf{\mu}, \Sigma)' alt='X\sim N(\mathbf{\mu}, \Sigma)' align=absmiddle> ，如果各个维度之间是相互独立的，就对应于协方差矩阵 <img src='/latexrender/pictures/025b3f94d79319f2067156076bf05243.png' title='\Sigma' alt='\Sigma' align=absmiddle> 是一个对角阵，但是如果 <img src='/latexrender/pictures/025b3f94d79319f2067156076bf05243.png' title='\Sigma' alt='\Sigma' align=absmiddle> 在非对角线的地方存在非零元素的话，就说明对应的两个维度之间存在相关性。</p>
<p>这个问题还是比较好解决的，高斯分布有这样的<a href="http://en.wikipedia.org/wiki/Multivariate_normal_distribution#Affine_transformation">性质</a>：类似于一维的情况，对于多维正态分布 <img src='/latexrender/pictures/cfe07334b3015f61a0fcada98ce93e8d.png' title='X\sim N(\mathbf{\mu}, \Sigma)' alt='X\sim N(\mathbf{\mu}, \Sigma)' align=absmiddle> ，那么新的随机变量 <img src='/latexrender/pictures/b976a5c8ad09d86b76315715916560f2.png' title='X_1=\mathbf{\mu}_1 + LX' alt='X_1=\mathbf{\mu}_1 + LX' align=absmiddle> 将会满足</p>
<pre class="tex"><img src='/latexrender/pictures/723954a3859419094c8d35331c158293.png' title='\displaystyle&#10;X_1 \sim N(\mathbf{\mu}_1+L\mu, L\Sigma L^T)&#10;' alt='\displaystyle&#10;X_1 \sim N(\mathbf{\mu}_1+L\mu, L\Sigma L^T)&#10;' align=absmiddle></pre>
<p>所以，对于一个给定的高斯分布 <img src='/latexrender/pictures/0ee4470356dfccb3df4156e15a5ed1a8.png' title='N(\mathbf{\mu}, \Sigma)' alt='N(\mathbf{\mu}, \Sigma)' align=absmiddle> 来说，只要先生成一个对应维度的标准正态分布 <img src='/latexrender/pictures/8368c21fca09c7cedb6a911884911524.png' title='X\sim N(0, I)' alt='X\sim N(0, I)' align=absmiddle> ，然后令 <img src='/latexrender/pictures/c5400761a11a5d3282ff98424ba26d01.png' title='X_1 = \mu+LX' alt='X_1 = \mu+LX' align=absmiddle> 即可，其中 <img src='/latexrender/pictures/d20caec3b48a1eef164cb4ca81ba2587.png' title='L' alt='L' align=absmiddle> 是对 <img src='/latexrender/pictures/025b3f94d79319f2067156076bf05243.png' title='\Sigma' alt='\Sigma' align=absmiddle> 进行 <a href="http://en.wikipedia.org/wiki/Cholesky_decomposition">Cholesky Decomposition</a> 的结果，即 <img src='/latexrender/pictures/cd167bc7a1095713faf193456c6def81.png' title='\Sigma = LL^T' alt='\Sigma = LL^T' align=absmiddle> 。</p>
<p>结束之前让我们来看看 matlab 画个 3D 图来改善一下心情：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
</pre></td><td class="code"><pre class="matlab" style="font-family:monospace;">N = <span style="color: #33f;">50000</span>;
T1 = <span style="color: #0000FF;">rand</span><span style="color: #080;">&#40;</span><span style="color: #33f;">1</span>, N<span style="color: #080;">&#41;</span>;
T2 = <span style="color: #0000FF;">rand</span><span style="color: #080;">&#40;</span><span style="color: #33f;">1</span>, N<span style="color: #080;">&#41;</span>;
&nbsp;
r = <span style="color: #0000FF;">sqrt</span><span style="color: #080;">&#40;</span>-<span style="color: #33f;">2</span>*<span style="color: #0000FF;">log</span><span style="color: #080;">&#40;</span>T2<span style="color: #080;">&#41;</span><span style="color: #080;">&#41;</span>;
theta = <span style="color: #33f;">2</span>*<span style="color: #0000FF;">pi</span>*T1;
X = <span style="color: #080;">&#91;</span>r.*<span style="color: #0000FF;">cos</span><span style="color: #080;">&#40;</span>theta<span style="color: #080;">&#41;</span>; r.*<span style="color: #0000FF;">sin</span><span style="color: #080;">&#40;</span>theta<span style="color: #080;">&#41;</span><span style="color: #080;">&#93;</span>;
mu = <span style="color: #080;">&#91;</span><span style="color: #33f;">1</span>; <span style="color: #33f;">2</span><span style="color: #080;">&#93;</span>;
Sigma = <span style="color: #080;">&#91;</span><span style="color: #33f;">5</span> <span style="color: #33f;">2</span>; <span style="color: #33f;">2</span> <span style="color: #33f;">1</span><span style="color: #080;">&#93;</span>;
L = <span style="color: #0000FF;">chol</span><span style="color: #080;">&#40;</span>Sigma<span style="color: #080;">&#41;</span>;
&nbsp;
X1 = <span style="color: #0000FF;">repmat</span><span style="color: #080;">&#40;</span>mu, <span style="color: #33f;">1</span>, N<span style="color: #080;">&#41;</span> + L*X;
&nbsp;
nbin = <span style="color: #33f;">30</span>;
&nbsp;
hist3<span style="color: #080;">&#40;</span>X1', <span style="color: #080;">&#91;</span>nbin nbin<span style="color: #080;">&#93;</span><span style="color: #080;">&#41;</span>;
<span style="color: #0000FF;">set</span><span style="color: #080;">&#40;</span><span style="color: #0000FF;">gcf</span>, <span style="color:#A020F0;">'renderer'</span>, <span style="color:#A020F0;">'opengl'</span><span style="color: #080;">&#41;</span>;
<span style="color: #0000FF;">set</span><span style="color: #080;">&#40;</span><span style="color: #0000FF;">get</span><span style="color: #080;">&#40;</span><span style="color: #0000FF;">gca</span>, <span style="color:#A020F0;">'child'</span><span style="color: #080;">&#41;</span>, <span style="color:#A020F0;">'FaceColor'</span>, <span style="color:#A020F0;">'interp'</span>, <span style="color:#A020F0;">'CDataMode'</span>, <span style="color:#A020F0;">'auto'</span><span style="color: #080;">&#41;</span>;
&nbsp;
<span style="color: #080;">&#91;</span>z c<span style="color: #080;">&#93;</span> = hist3<span style="color: #080;">&#40;</span>X1', <span style="color: #080;">&#91;</span>nbin nbin<span style="color: #080;">&#93;</span><span style="color: #080;">&#41;</span>;
<span style="color: #080;">&#91;</span>x y<span style="color: #080;">&#93;</span> = <span style="color: #0000FF;">meshgrid</span><span style="color: #080;">&#40;</span>c<span style="color: #080;">&#123;</span><span style="color: #33f;">1</span><span style="color: #080;">&#125;</span>, c<span style="color: #080;">&#123;</span><span style="color: #33f;">2</span><span style="color: #080;">&#125;</span><span style="color: #080;">&#41;</span>;
&nbsp;
<span style="color: #0000FF;">figure</span>;
<span style="color: #0000FF;">surfc</span><span style="color: #080;">&#40;</span>x,y,-z<span style="color: #080;">&#41;</span>;</pre></td></tr></table></div>

<p>下面两幅图，哪幅好看一些（注意坐标比例不一样，所以看不出形状和旋转了）？似乎都不太好看，不过感觉还是比前面的 heatmap 要好一点啦！</p>
<p><img src="/wp-content/uploads/2010/04/example4-hist.png" alt="" title="example4-hist" width="492" height="419" class="aligncenter size-full wp-image-511" /></p>
<p><img src="/wp-content/uploads/2010/04/example4-surf.png" alt="" title="example4-surf" width="492" height="419" class="aligncenter size-full wp-image-512" /></p>
<p>然后，到这里为止，我们算是把高斯分布弄清楚了，不过这只是给一个介绍性的东西，里面的数学推导也并不严格，而 Box Muller 也并不是最高效的高斯采样的算法，不过，就算我们不打算再深入讨论高斯采样，采样这个问题本身也还有许多不尽人意的地方，我们推导出来的结论可以说只能用于一小部分简单的分布，连高斯分布都要通过 trick 来解决，另一些本身连概率密度函数都写不出来或者有各种奇怪数学特性的分布就更难处理了。所以本文的标题里也说了，这是上篇，如果什么时候有机会抽出时间来写下篇的话，我将会介绍一些更加通用和强大的方法，诸如 Rejection Sampling 、Gibbs Sampling 以及 Markov Chain Monte Carlo (MCMC) 等方法。如果你比较感兴趣，可以先自行 Google 一下解馋！ <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/biggrin.png' alt=':D' class='wp-smiley' /> </p>
<p>末了，还得废话两句，虽然是冒了一个泡，但是也是冒得很艰难，发了这篇日志也并不代表 blog 会恢复往日的发文频率，实际上这篇 blog 也差不多凑了半个多月的时间才折腾完，文章开头的“三个月”我想是不是也该更新为“四个月”了。 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/tongue.png' alt=':p' class='wp-smiley' /> 不过我想，如果有时间和有趣的 idea 的话，我还是会写的，毕竟这也是我的乐趣之一呢！ <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/biggrin.png' alt=':D' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://blog.pluskid.org/?feed=rss2&amp;p=430</wfw:commentRss>
		<slash:comments>26</slash:comments>
		</item>
		<item>
		<title>CV 课程 Project：简单验证码的识别</title>
		<link>http://blog.pluskid.org/?p=428</link>
		<comments>http://blog.pluskid.org/?p=428#comments</comments>
		<pubDate>Mon, 25 Jan 2010 11:40:34 +0000</pubDate>
		<dc:creator>pluskid</dc:creator>
				<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Computer Vision]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[zju]]></category>

		<guid isPermaLink="false">http://blog.pluskid.org/?p=428</guid>
		<description><![CDATA[<p>验证码的识别可以说是一个非常困难的问题，更何况现在有一些验证码让人来辨认都有些不容易，当然正如我标题里说的，我这里讨论的是一种简单的验证码，具体来说就是经过旋转、缩放之后再加上一些随机线条作为干扰而得到的图片，如右图所示。其实这个问题最开始是 MSTC 第四届趣味程序设计竞赛中的一道题，这个学期上了一门《计算机视觉》课，最后要求交一个 Project ，老师给了一些题目，也可以自己想主题，于是我就定了这个主题。</p>
<p>由于最近各种 deadline ，我也没空把详细过程再用 blog 的方式写一遍了，如果感兴趣可以直接看我的 Report 文档以及完整的代码。</p>
<p>其实这个问题我在当时趣味程序设计竞赛的时候就尝试解决过，不过我当时用的办法是 PCA ，事实上，我尝试了 PCA 、LPP 以及 NPE ，从各种 paper 上还有理论推导中的道理来看，似乎都是后两者要更加先进一些，然而结果却是 PCA 最好，当时就令我有些诧异。其实我一直以来都有一些极端的“理想”，就是总希望有那么一些非常“自动化”或者“傻瓜化”的方法，把问题放进去就能得出结果，例如我在中学的时候凡是遇到几何题，几乎都是首先尝试建立坐标系用解析几何的方法来做，虽然构造各种精巧的辅助线来解决问题是许多人的最爱，然而我似乎不太喜欢这种“专门针对某一个问题要重新想一个方法”的方式，却更偏爱通用性更强的办法，就算有时候计算会变得很繁琐，但是最后通常总是能算出来的。在这里也一样，我似乎有一种愿望，或者甚至是一种错觉，我们可以有一种算法，只要把数据放进去，就能得出最完美的结果来，比如通常特征提取和构造的问题，我似乎不太喜欢使用太多 domain 相关的先验知识来人工构造 feature 的方式，而比较偏爱直接使用最原始的 feature ，然后使用降维来自动选择——我甚至在想，是不是首先通过升维（例如，使用 Kernel 的方法）来够找更多的特征以增加数据的区分度，然后再紧接着用降维的方法来提炼，是不是就可以完美地解决一切特征构造和抽取的问题了？</p>
<p>当然，现在我知道这只是一个美好的愿望，其实我一直也都知道这只是一个愿望了，只是仍然有点不自觉地往那个方向倾斜。其实这样的问题，也许如果我们可以得到无穷多的训练数据和无穷多的计算资源的话，应该是可以实现的啦，哈哈！  </p>
<p>总的来说，我正在纠正自己的一些偏见，所以在学习了《计算机视觉》这门课之后我决定做一些尝试，特别是在知道了有一种叫做 SIFT 的特征可以保持旋转和缩放的不变性的时候，我就在想——这不就是正好是解决这个问题所需要的吗？所以我定了这个题目。然而不幸的是当我做出最后结果的时候发现实际结果并不好，还是经典的 PCA 最强。当然，到这个 deadline 前夕，肯定不能再换题目了，另一方面，虽然我一开始的设想被验证是错误的，但是这个 project 并不是没有意义的，我把它和 PCA 做了一些对比，并对结论做了一些分析，自己的理解也更深刻了一些，可以说还是收获挺多的。</p>
<p>另外就是这次我对噪音过滤的方法做了不少对比和尝试，最后的结论是中值滤镜最好，我印象中自己在做趣味程序设计竞赛的时候只做了非常简单的处理，于是好奇地找出以前的代码来看了一下，发现居然就是一个中值滤镜而已！不过当时不知道 OpenCV 也没有学过滤镜什么的，怎么找到这么个方法的来着？真是想不起来了。不过，有时候翻翻自己以前写的代码，确实会如同在论坛上考古一样别有一番趣味的，代码里也散发着岁月的味道，不是吗？  </p>
]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/2010/01/captcha.png" alt="" title="captcha" width="200" height="50" class="alignright size-full wp-image-429" />验证码的识别可以说是一个非常困难的问题，更何况现在有一些验证码让人来辨认都有些不容易，当然正如我标题里说的，我这里讨论的是一种简单的验证码，具体来说就是经过旋转、缩放之后再加上一些随机线条作为干扰而得到的图片，如右图所示。其实这个问题最开始是 MSTC 第四届趣味程序设计竞赛中的一道题，这个学期上了一门《计算机视觉》课，最后要求交一个 Project ，老师给了一些题目，也可以自己想主题，于是我就定了这个主题。</p>
<p>由于最近各种 deadline ，我也没空把详细过程再用 blog 的方式写一遍了，如果感兴趣可以直接看我的 <a href="/wp-content/uploads/2010/01/captcha.pdf">Report 文档</a>以及<a href="/wp-content/uploads/2010/01/captcha_src.zip">完整的代码</a>。</p>
<p><span id="more-428"></span>其实这个问题我在当时趣味程序设计竞赛的时候就尝试解决过，不过我当时用的办法是 PCA ，事实上，我尝试了 PCA 、LPP 以及 NPE ，从各种 paper 上还有理论推导中的道理来看，似乎都是后两者要更加先进一些，然而结果却是 PCA 最好，当时就令我有些诧异。其实我一直以来都有一些极端的“理想”，就是总希望有那么一些非常“自动化”或者“傻瓜化”的方法，把问题放进去就能得出结果，例如我在中学的时候凡是遇到几何题，几乎都是首先尝试建立坐标系用解析几何的方法来做，虽然构造各种精巧的辅助线来解决问题是许多人的最爱，然而我似乎不太喜欢这种“专门针对某一个问题要重新想一个方法”的方式，却更偏爱通用性更强的办法，就算有时候计算会变得很繁琐，但是最后通常总是能算出来的。在这里也一样，我似乎有一种愿望，或者甚至是一种错觉，我们可以有一种算法，只要把数据放进去，就能得出最完美的结果来，比如通常特征提取和构造的问题，我似乎不太喜欢使用太多 domain 相关的先验知识来人工构造 feature 的方式，而比较偏爱直接使用最原始的 feature ，然后使用降维来自动选择——我甚至在想，是不是首先通过升维（例如，使用 Kernel 的方法）来够找更多的特征以增加数据的区分度，然后再紧接着用降维的方法来提炼，是不是就可以完美地解决一切特征构造和抽取的问题了？</p>
<p>当然，现在我知道这只是一个美好的愿望，其实我一直也都知道这只是一个愿望了，只是仍然有点不自觉地往那个方向倾斜。其实这样的问题，也许如果我们可以得到无穷多的训练数据和无穷多的计算资源的话，应该是可以实现的啦，哈哈！ <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/biggrin.png' alt=':D' class='wp-smiley' /> </p>
<p>总的来说，我正在纠正自己的一些偏见，所以在学习了《计算机视觉》这门课之后我决定做一些尝试，特别是在知道了有一种叫做 SIFT 的特征可以保持旋转和缩放的不变性的时候，我就在想——这不就是正好是解决这个问题所需要的吗？所以我定了这个题目。然而不幸的是当我做出最后结果的时候发现实际结果并不好，还是经典的 PCA 最强。当然，到这个 deadline 前夕，肯定不能再换题目了，另一方面，虽然我一开始的设想被验证是错误的，但是这个 project 并不是没有意义的，我把它和 PCA 做了一些对比，并对结论做了一些分析，自己的理解也更深刻了一些，可以说还是收获挺多的。</p>
<p>另外就是这次我对噪音过滤的方法做了不少对比和尝试，最后的结论是中值滤镜最好，我印象中自己在做趣味程序设计竞赛的时候只做了非常简单的处理，于是好奇地找出以前的代码来看了一下，发现居然就是一个中值滤镜而已！不过当时不知道 OpenCV 也没有学过滤镜什么的，怎么找到这么个方法的来着？真是想不起来了。不过，有时候翻翻自己以前写的代码，确实会如同在论坛上考古一样别有一番趣味的，代码里也散发着岁月的味道，不是吗？ <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/wink.png' alt=';)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://blog.pluskid.org/?feed=rss2&amp;p=428</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>荐书：《女士品茶》</title>
		<link>http://blog.pluskid.org/?p=426</link>
		<comments>http://blog.pluskid.org/?p=426#comments</comments>
		<pubDate>Mon, 11 Jan 2010 08:34:20 +0000</pubDate>
		<dc:creator>pluskid</dc:creator>
				<category><![CDATA[Life]]></category>
		<category><![CDATA[Book]]></category>
		<category><![CDATA[Fun]]></category>
		<category><![CDATA[Math]]></category>

		<guid isPermaLink="false">http://blog.pluskid.org/?p=426</guid>
		<description><![CDATA[<p>这本书的全名为《女士品茶：20世纪统计怎样变革了科学》，英文原名为 The Lady Tasting Tea: How Statistics Revolutionized Science in the Twentieth Century 。从副标题已经可以看出来它讲的内容是什么了，而从主标题也可以看出这应该会是一本很有趣的书。事实也确实如此。</p>
<p>我最近发现这本书之后它就几乎占满了我所有的空余时间，本来准备看完以后再来推荐，但是现在看到一半已经实在是忍不住了。总的来说这是一本好书，我最开始发现它也是在 newsmth 的统计版上看到有人推荐，而且书的作者 David Salsburg 本身也是牛人，我们会发现并不是所有的科学家都是那种自己思维极度活跃跟正常人根本无法沟通的，其实有许多牛人写的科普类读物是非常易懂并且非常有趣的，并且这些看似随意的文字描述的背后其实有着坚实的理论基础，完全的外行人会觉得这是一本有趣的书，另一方面，在该领域有很深造诣的人，又会从字里行间读到其背后隐藏掉的复杂数学，可以看作对自己所学知识的一次整理。</p>
<p>当然我是属于前一类读者，虽然有听说过 Pearson 、Fisher 之类的名字，但是对于统计学的产生和发展以及一些更深层次的应用和理论并不是特别了解。所以我就完全把它当作一本休闲读物来看了——确实是非常有趣的。此外，我觉得等以后自己对这个领域有了更多的一些了解之后再回来看一遍这个书，估计又能尝到另一番味道呢。如果容许我剧透一下的话，里面会有各种 8g 趣闻，比如但凡学过“数理统计”这门课的人应该都知道有一个叫做“t 分布”的东西，如果不是特别死板的老师的话，通常会告诉大家叫 t 分布这个名字是因为发表该分别的家伙以 student 署名，不过如果你想知道这个以 student 署名的家伙到底是谁，以及为何要这样匿名来发表，就需要看这本书了。</p>
<p>此外，你还将看到几位统计巨头老 Pearson 、Fisher、Neyman 之间的恩恩怨怨，当然，为何书的名字会叫做《女士品茶》？如果你不了解这个典故的话，也会在书的第一章揭晓。作者带我们目睹整个二十世纪里统计学本身的发展以及为各个领域带来的变革的同时，也为我们展示了上个世纪做 research 的情形。虽然也有各种混乱的情况，但是，至少可以看到许多人做 research 都是为了解决（不管是实际的还是理论上的）某个问题，而不是像今天这样很多人的目的好像就变成发表 paper 了，很难想像今天有谁作出重要成果之后会以“student”这样的匿名方式来发表论文。当然，也许书中所描述的都是巨牛型的人物，所以这个样本并不足以代表“二十世纪整个科研界”的情况吧。从各种牛人们的故事中其实我们也应该认识到：环境并不是决定成功与否的因素，即使在最恶劣的环境下，也会有无法掩盖的璀璨的光芒，所以，环境并不能作为我们不努力的借口啊。  </p>
<p>最后，还是忍不住引用了书中的一段：  </p>
<p>
列昂惕夫最初对经济部门的分类得到了一个 12&#215;12 的矩阵，这样，杰尔姆·科恩菲尔德就要来求它的逆矩阵，看是否存在唯一的逆矩阵。大概花了他一周的时间，得到的结论是分类过粗，必须扩大经济部门的分类数目。于是，科恩菲尔德和列昂惕夫惴惴不安地对经济体系作进一步地细分，最后得到一个 24&#215;24 的矩阵，他们认为这是或许可行的最简单的矩阵形式了。两人都知道，这一项任务根本不可能由一个人完成。科恩菲尔德估计，计算一个 24&#215;24 的矩阵的逆矩阵，即使是一周工作 7 天，也要花上几百年的时间。</p>
<p>第二次世界大战期间，哈佛大学发明了第一台非常原始的计算机。这台计算机采用机械式继电器开关，还常常卡住。战争结束后，没有什么军事任务需要做了，哈佛大学正在找项目来使用这台怪物似的机器，于是科恩菲尔德和列昂惕夫决定将这个 24&#215;24 的矩阵拿过去，用这台叫作“马克Ⅰ号”（Mark Ⅰ）的机器来求它的逆矩阵，完成这一繁琐的计算。事后，当他们要为这一去处过程付费时，却被劳工统计局的会计部门制止。原来，那时政府部门有一项政策，货物可以购买，而服务不能购买。这一理论意味着，政府部门自身拥有各种各样的专业人员来为它服务，如果有什么事情要做，政府机构内部应该有能做这件事的人。</p>
<p>他们对政府中的那名会计解释说，理论上这件事有人能够做到，但是他活不了直到把这件事情做完那么长时间。那名会计对此非常同情，但文件就是那样规定的，所以也无能为力。最后，科恩菲尔德想出了一个办法，顺利地解决了这个难题。方法是由劳工统计局开出一张购买固定资产的订单。什么固定资产呢？在发票上写的是劳工统计局从哈佛大学购买一个“逆矩阵”。
</p>
<p>购买一个“逆矩阵”，看来美国政府制造笑话的能力也并不比我们差，哈哈。向来听说美国那边机构办事都相当死板，严格按照程序来走，一点通融的余地都没有，这下也算真是见识到了。 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/2010/01/lady_taste_tea.jpg" alt="" title="lady_taste_tea" width="250" height="327" class="alignright size-full wp-image-427" />这本书的全名为《女士品茶：20世纪统计怎样变革了科学》，英文原名为 The Lady Tasting Tea: How Statistics Revolutionized Science in the Twentieth Century 。从副标题已经可以看出来它讲的内容是什么了，而从主标题也可以看出这应该会是一本很有趣的书。事实也确实如此。</p>
<p>我最近发现这本书之后它就几乎占满了我所有的空余时间，本来准备看完以后再来推荐，但是现在看到一半已经实在是忍不住了。总的来说这是一本好书，我最开始发现它也是在 newsmth 的统计版上看到有人推荐，而且书的作者 <a href="http://en.wikipedia.org/wiki/David_Salsburg">David Salsburg</a> 本身也是牛人，我们会发现并不是所有的科学家都是那种自己思维极度活跃跟正常人根本无法沟通的，其实有许多牛人写的科普类读物是非常易懂并且非常有趣的，并且这些看似随意的文字描述的背后其实有着坚实的理论基础，完全的外行人会觉得这是一本有趣的书，另一方面，在该领域有很深造诣的人，又会从字里行间读到其背后隐藏掉的复杂数学，可以看作对自己所学知识的一次整理。</p>
<p>当然我是属于前一类读者，虽然有听说过 Pearson 、Fisher 之类的名字，但是对于统计学的产生和发展以及一些更深层次的应用和理论并不是特别了解。所以我就完全把它当作一本休闲读物来看了——确实是非常有趣的。此外，我觉得等以后自己对这个领域有了更多的一些了解之后再回来看一遍这个书，估计又能尝到另一番味道呢。如果容许我剧透一下的话，里面会有各种 8g 趣闻，比如但凡学过“数理统计”这门课的人应该都知道有一个叫做“t 分布”的东西，如果不是特别死板的老师的话，通常会告诉大家叫 t 分布这个名字是因为发表该分别的家伙以 student 署名，不过如果你想知道这个以 student 署名的家伙到底是谁，以及为何要这样匿名来发表，就需要看这本书了。</p>
<p><span id="more-426"></span>此外，你还将看到几位统计巨头老 Pearson 、Fisher、Neyman 之间的恩恩怨怨，当然，为何书的名字会叫做《女士品茶》？如果你不了解这个典故的话，也会在书的第一章揭晓。作者带我们目睹整个二十世纪里统计学本身的发展以及为各个领域带来的变革的同时，也为我们展示了上个世纪做 research 的情形。虽然也有各种混乱的情况，但是，至少可以看到许多人做 research 都是为了解决（不管是实际的还是理论上的）某个问题，而不是像今天这样很多人的目的好像就变成发表 paper 了，很难想像今天有谁作出重要成果之后会以“student”这样的匿名方式来发表论文。当然，也许书中所描述的都是巨牛型的人物，所以这个样本并不足以代表“二十世纪整个科研界”的情况吧。从各种牛人们的故事中其实我们也应该认识到：环境并不是决定成功与否的因素，即使在最恶劣的环境下，也会有无法掩盖的璀璨的光芒，所以，环境并不能作为我们不努力的借口啊。 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/tongue.png' alt=':p' class='wp-smiley' /> </p>
<p>最后，还是忍不住引用了书中的一段： <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/joyful.png' alt='^_^' class='wp-smiley' /> </p>
<blockquote><p>
列昂惕夫最初对经济部门的分类得到了一个 12&#215;12 的矩阵，这样，杰尔姆·科恩菲尔德就要来求它的逆矩阵，看是否存在唯一的逆矩阵。大概花了他一周的时间，得到的结论是分类过粗，必须扩大经济部门的分类数目。于是，科恩菲尔德和列昂惕夫惴惴不安地对经济体系作进一步地细分，最后得到一个 24&#215;24 的矩阵，他们认为这是或许可行的最简单的矩阵形式了。两人都知道，这一项任务根本不可能由一个人完成。科恩菲尔德估计，计算一个 24&#215;24 的矩阵的逆矩阵，即使是一周工作 7 天，也要花上几百年的时间。</p>
<p>第二次世界大战期间，哈佛大学发明了第一台非常原始的计算机。这台计算机采用机械式继电器开关，还常常卡住。战争结束后，没有什么军事任务需要做了，哈佛大学正在找项目来使用这台怪物似的机器，于是科恩菲尔德和列昂惕夫决定将这个 24&#215;24 的矩阵拿过去，用这台叫作“马克Ⅰ号”（<a href="http://en.wikipedia.org/wiki/Harvard_Mark_I">Mark Ⅰ</a>）的机器来求它的逆矩阵，完成这一繁琐的计算。事后，当他们要为这一去处过程付费时，却被劳工统计局的会计部门制止。原来，那时政府部门有一项政策，货物可以购买，而服务不能购买。这一理论意味着，政府部门自身拥有各种各样的专业人员来为它服务，如果有什么事情要做，政府机构内部应该有能做这件事的人。</p>
<p>他们对政府中的那名会计解释说，理论上这件事有人能够做到，但是他活不了直到把这件事情做完那么长时间。那名会计对此非常同情，但文件就是那样规定的，所以也无能为力。最后，科恩菲尔德想出了一个办法，顺利地解决了这个难题。方法是由劳工统计局开出一张购买固定资产的订单。什么固定资产呢？在发票上写的是劳工统计局从哈佛大学购买一个“逆矩阵”。
</p></blockquote>
<p>购买一个“逆矩阵”，看来美国政府制造笑话的能力也并不比我们差，哈哈。向来听说美国那边机构办事都相当死板，严格按照程序来走，一点通融的余地都没有，这下也算真是见识到了。 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/biggrin.png' alt=':D' class='wp-smiley' /> </p>
<p>如果你已经感兴趣了的话，那么可以去网上或图书馆找一下，网上可以找到英文版的扫描版本，也有一个中文版本，是由 Rika Ma 根据翻译版辛勤录入的成果，虽然其中有一些错别字，但是总的来说看起来比扫描版舒服许多。</p>
<p>ps: 今天路过书店的时候无意中发现<a href="http://www.ruanyifeng.com/blog/2009/12/chinese_version_of_mjos_is_on_sale.html">阮一峰翻译的那本 More Joel on Software</a> 已经在书架上了，中文名叫做《软件随想录》。不过由于自己最近状态比较危险（马上就要期末考试了，自己却还没有要开始复习的迹象 -.-bb），而且寒假要读的书也完全排满甚至溢出了，所以还是等过一段时间再关注这本书好了，听说也是一本相当不错的书呢。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.pluskid.org/?feed=rss2&amp;p=426</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Proggit 圣诞套餐</title>
		<link>http://blog.pluskid.org/?p=422</link>
		<comments>http://blog.pluskid.org/?p=422#comments</comments>
		<pubDate>Fri, 25 Dec 2009 14:19:22 +0000</pubDate>
		<dc:creator>pluskid</dc:creator>
				<category><![CDATA[Life]]></category>
		<category><![CDATA[Fun]]></category>

		<guid isPermaLink="false">http://blog.pluskid.org/?p=422</guid>
		<description><![CDATA[<p>大家圣诞快乐！</p>
<p>看论文看到崩溃之后，去 Proggit 逛了一圈，发现那边也很热闹，一连看到几个有趣的帖子，于是决定推荐几个：</p>
<p>&#8230;is there anything Emacs CANNOT do?
“Emacs is an OS” 的说法其实早就不陌生了，虽然大多数时候我会选择坚决捍卫这句话，但是其实自己也是把他当作一句有趣的玩笑来看待的。进入这个 thread ，看到有人说 Emacs 目前还不能编辑视频——这是理所当然的啊，我也这么觉得，不过下面立即有人给了这样一个链接：Acturally&#8230; ，自己点过去看吧，我决定再把 Emacs 打开来拜一拜！  
<p>
Best SourceForge project ever
<p>说实话我也有几次翻看过 SourceForge 的项目列表，不过这个 Best 究竟是什么呢？点开一看，原来一个 MacOS X 用户使用 GIMP 时出了 bug ，于是他专门建立了一个 SourceForge 项目，来说了自己的问题，那看来是相当不能忍了，因为在 SourceForge 上建立一个项目是相当费时的，好像还需要审核（不知道有没有记错，年代久远了），填一大堆乱七八糟的东西，相比之下 Google Code 就要简便许多，github 更是随便就可以建立一个 project 。不过今天算是第一次知道原来 SourceForge 还有这样的功能。</p>
<p>Merry Quine-mas
最后这个点进去是一个日文网站，不过没有关系，看 Ruby 代码就好了：</p>
open("/dev/dsp","wb"){&#124;h&#124;s=%q{d=["-KQW[UZbgu*HNT+]TNOOOTZ+cZTUUUUUZbagmssUZbagm
ss+wmpgja+KQW[dfnu","-KEKOINV[W*HBH+QHBCCCHN+WNHIIIIINVU[aUUINVU[aUU+YOR[^I+KEK
OXZbW","-W[acg vsc*TZ`+eaaaaa--vucavuca+eadsvs+W[dgvrtc","-K991LIL77777dIIIII--
LKKILKKI+Mad[   ^U+K991LHJK"].map{&#124;l&#124;l.unpack("C*").map{&#124;c&#124;[(c/6-4)*12/7-8,"012
35b"[c%6,1].  [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://blog.pluskid.org/wp-content/uploads/2009/12/Reddit-Logo_scaled.png"><img src="http://blog.pluskid.org/wp-content/uploads/2009/12/Reddit-Logo_scaled.png" alt="" title="Reddit Logo_scaled" width="91" height="120" class="alignright size-full wp-image-423" /></a>大家圣诞快乐！</p>
<p>看论文看到崩溃之后，去 <a href="http://www.reddit.com/r/programming/">Proggit</a> 逛了一圈，发现那边也很热闹，一连看到几个有趣的帖子，于是决定推荐几个：</p>
<p><b><a href="http://www.reddit.com/r/programming/comments/ai71t/vlc_developers_have_started_working_on_a_video/c0holsd">&#8230;is there anything Emacs CANNOT do?</a></b><br />
“Emacs is an OS” 的说法其实早就不陌生了，虽然大多数时候我会选择坚决捍卫这句话，但是其实自己也是把他当作一句有趣的玩笑来看待的。进入这个 thread ，看到有人说 Emacs 目前还不能编辑视频——这是理所当然的啊，我也这么觉得，不过下面立即有人给了这样一个链接：<a href="http://1010.co.uk/gneve.html">Acturally&#8230;</a> ，自己点过去看吧，我决定再把 Emacs 打开来拜一拜！ <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/biggrin.png' alt=':D' class='wp-smiley' /> </li>
<p><span id="more-422"></span><br />
<h3><a href="http://www.reddit.com/r/programming/comments/aie1y/best_sourceforge_project_ever/">Best SourceForge project ever</a></h3>
<p>说实话我也有几次翻看过 SourceForge 的项目列表，不过这个 Best 究竟是什么呢？点开一看，原来一个 MacOS X 用户使用 GIMP 时出了 bug ，于是他专门建立了一个 SourceForge 项目，来说了自己的问题，那看来是相当不能忍了，因为在 SourceForge 上建立一个项目是相当费时的，好像还需要审核（不知道有没有记错，年代久远了），填一大堆乱七八糟的东西，相比之下 Google Code 就要简便许多，github 更是随便就可以建立一个 project 。不过今天算是第一次知道原来 SourceForge 还有这样的功能。</p>
<p><b><a href="http://d.hatena.ne.jp/ku-ma-me/20091224">Merry Quine-mas</a></b><br />
最后这个点进去是一个日文网站，不过没有关系，看 Ruby 代码就好了：</p>
<pre><code>open("/dev/dsp","wb"){|h|s=%q{d=["-KQW[UZbgu*HNT+]TNOOOTZ+cZTUUUUUZbagmssUZbagm
ss+wmpgja+KQW[dfnu","-KEKOINV[W*HBH+QHBCCCHN+WNHIIIIINVU[aUUINVU[aUU+YOR[^I+KEK
OXZbW","-W[acg vsc*TZ`+eaaaaa--vucavuca+eadsvs+W[dgvrtc","-K991LIL77777dIIIII--
LKKILKKI+Mad[   ^U+K991LHJK"].map{|l|l.unpack("C*").map{|c|[(c/6-4)*12/7-8,"012
35b"[c%6,1].     hex]}*4};y=32.chr;l="@"+[(m="Jnx4sn3sgd1")+"vnqkc!6sgd2Lnqc4gz
r4bnld;6/Ld       s2dzqsg6qd2@bdhud6gdq2Khmf;77/Lds2du4@dqx4gdzqs6oqd2@ozqd4ghl
4qnnl,+Amc         2gdz++2   @udm   4z        mc      2gdz      4@+u   dm   2zm
c2mz+@stqd+r     hmf",m+"E    zq    sg  !6sgd2Sz  u4@h  nt  q4qd  hfm  r;  6/Ld
s2ldm6sgdhq       2rnmfr6d  l    2  @o       knx      ;77/      Wghkd2    ehdkc
r4zmc4eknn         cr,6qnb  jr  ,2  gh  kkr,4zmc  4okz  hm  r+Rd  2@odz  s+2+,6
qd2@odzs6           +sgd2r  ntmc+@  hm        f+  inx"  ,"  Nn4l  nqd3k  ds1rhm
r6zmc2rn             4@qqnvr4fqnv,6/Nnq2sgnqmr6hm2@edrs6sgd2fqntmc;77/Hd2bnldr4
sn4lzjd               6Hhr2ak    d4@rrh   mfr4eknv+Fzq2zr+2+,6ezq2zr,6+sgd2btqr
d+hr+entmc         ","Hd4qtkdr3s  gd1   vnqkc6vhsg2sqtsg4zmc4fqzbd,6/Amc2lzjdr6
sgd2mz6@s           hnmr2oqnud77/ T   gd2fk   n4@q   hdr4    ne6Hh       r2qhfg
s4@dntr4             @mdrr,+Amc2v   nm+2+6@    cd    qr,  2v  nm6  +@cdqr2ne+Hh
r+knud"               ].map{|a|   a ,b,c,e,  *    f  =a.  sp  lit"      +";[a,c
=b+c+f                 *"2",c   ,b+  e+f*"4  "+  ".  8/        /"]*"6/"  }.join
.tr("                   a-z   /","b-    za\  n").gs  ub  (/^/  ,"       #"<<32)
;c=0;640.tim     es{|w|c<1&#038;&#038;(l=~/(@)?(.*?)(\d)/m;($>.<<$1?$2:y+$2).flush;l=$';c
=eval$3);c-=     1;a=[128]*z=1200;4.times{|j|t,n=d[j].pop;f=0.3456*2**(t/12.0-j
/2);(n>0?(d[     j]<<[t,n-1];z):800).times{|i|t>-3&#038;&#038;a[i]+=16*Math.sin(f.*w*z+i)
}};(h.<<a.pack"C*").flush};puts(s.gsub(/./){"!">$&#038;?"#":y}+%(\nopen("/dev/dsp#{"
(c)Y.Endoh2009";'",'}"wb"){|h|s=%q{#{s}};eval(s.split*"")}))};eval(s.split*"")}
</code></pre>
<p>代码摆成圣诞树和 Merry Xmas 的形状，当然，这段代码是可以运行的（去复制<a href="http://d.hatena.ne.jp/ku-ma-me/20091224">原始网站</a>的代码，不要直接复制这里的版本），会输出有趣的东西，另外戴上耳机，还会听到音乐哦！需要 Linux 环境和 /dev/dsp 设备，据说 Windows 下的 cygwin 也可以。此外，我还发现这段代码还有一个附加效果：贴到学校的 cc98 上之后 cc98 的 javascript 代码会在 mootools 里停止响应，真是强大啊！ =.=bb 而且，贴在这里的时候我把 pre 和 code 标签都用了，还是没能完全阻止 WP 对其中的一些地方进行转义呀，颇不和谐了！</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.pluskid.org/?feed=rss2&amp;p=422</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>解决 Matlab MEX 编译时 GCC 版本的问题</title>
		<link>http://blog.pluskid.org/?p=421</link>
		<comments>http://blog.pluskid.org/?p=421#comments</comments>
		<pubDate>Fri, 25 Dec 2009 11:15:42 +0000</pubDate>
		<dc:creator>pluskid</dc:creator>
				<category><![CDATA[Tool]]></category>
		<category><![CDATA[Matlab]]></category>
		<category><![CDATA[Tip]]></category>

		<guid isPermaLink="false">http://blog.pluskid.org/?p=421</guid>
		<description><![CDATA[<p>在 Matlab 里使用 mex 来编译 C/C++ 代码失败，这个问题算是困扰了我好几个月了，主要是我的环境比较恶劣：系统是 Arch Linux ，感觉这个系统比较喜欢追求最新版本，比较无视老版本软件的兼容性问题吧。再加上系统是 64 位的，出现各种兼容性问题似乎也觉得是理所当然的，然后像 Matlab 这样的软件通常使用较老版本的 GCC ，而 GCC 更新的时候又做了一些大改动，总之结果是我每次试图编译的时候都得到类似这样的错误：

/usr/lib/gcc/x86_64-unknown-linux-gnu/4.4.2/cc1: /opt/matlab/sys/os/glnxa64/libstdc++.so.6: version `GLIBCXX_3.4.11' not found (required by /usr/lib/libgmpxx.so.4)

因为 GCC 新版本里把 GLIBCXX_3.4.11 这个符号去掉了，所以挂掉了。搞得我每次需要运行 mex 编译的代码都要放到另外一台 Windows 机器上跑。也 Google 了很多次都没有找到解决方案。今天一怒之下用了最暴力的方法：/opt/matlab/sys/os/glnxa64/libstdc++.so.6 其实是指向该目录下的另一个文件 libstdc++.so.6.0.9 的软连接，我将他改成指向系统里的新版本的 libstdc++：/usr/lib/libstdc++.so.6.0.13 ，结果问题解决了。那个目录下还有一个 README ，说了这几个文件是从 GCC distribution 里拷贝过来的。于是这样用新版本覆盖应该不会有太大的问题吧？  至少目前还没有出什么问题的。</p>
]]></description>
			<content:encoded><![CDATA[<p>在 Matlab 里使用 mex 来编译 C/C++ 代码失败，这个问题算是困扰了我好几个月了，主要是我的环境比较恶劣：系统是 Arch Linux ，感觉这个系统比较喜欢追求最新版本，比较无视老版本软件的兼容性问题吧。再加上系统是 64 位的，出现各种兼容性问题似乎也觉得是理所当然的，然后像 Matlab 这样的软件通常使用较老版本的 GCC ，而 GCC 更新的时候又做了一些大改动，总之结果是我每次试图编译的时候都得到类似这样的错误：<br />
<code><br />
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.4.2/cc1: /opt/matlab/sys/os/glnxa64/libstdc++.so.6: version `GLIBCXX_3.4.11' not found (required by /usr/lib/libgmpxx.so.4)<br />
</code><br />
因为 GCC 新版本里把 GLIBCXX_3.4.11 这个符号去掉了，所以挂掉了。搞得我每次需要运行 mex 编译的代码都要放到另外一台 Windows 机器上跑。也 Google 了很多次都没有找到解决方案。今天一怒之下用了最暴力的方法：/opt/matlab/sys/os/glnxa64/libstdc++.so.6 其实是指向该目录下的另一个文件 libstdc++.so.6.0.9 的软连接，我将他改成指向系统里的新版本的 libstdc++：/usr/lib/libstdc++.so.6.0.13 ，结果问题解决了。那个目录下还有一个 README ，说了这几个文件是从 GCC distribution 里拷贝过来的。于是这样用新版本覆盖应该不会有太大的问题吧？ <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/tongue.png' alt=':p' class='wp-smiley' /> 至少目前还没有出什么问题的。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.pluskid.org/?feed=rss2&amp;p=421</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>2009 飘着</title>
		<link>http://blog.pluskid.org/?p=420</link>
		<comments>http://blog.pluskid.org/?p=420#comments</comments>
		<pubDate>Thu, 24 Dec 2009 19:54:33 +0000</pubDate>
		<dc:creator>pluskid</dc:creator>
				<category><![CDATA[Life]]></category>

		<guid isPermaLink="false">http://blog.pluskid.org/?p=420</guid>
		<description><![CDATA[<p>2009 年快要过去了，虽然我总是觉得奥运会明明就是今年举行的，但是日历上确实写着 2009 年。大家都写总结了，于是我也写吧，又不想用“总结”这么土的标题，就打开播放列表，找来找去看到“飘着”这首歌，回想自己这一年，也许这个词是很贴切的呢，寒假没有回家，也就飘在学校了，上半年住在玉泉校区，几乎每天飘来紫金港，下半年搬到紫金港校区，也几乎每天飘去玉泉。所以，就这个标题吧，很合适呢，歌也是很好听的。</p>
<p>说起来要写总结还真不是一件容易的事，我自己的记忆向来比较混乱，又是喜欢大幅度跳跃式的搜索，所以需要借助一些辅助的东西，比如 blog 、记事本、零碎的日记等等。其实这个新 blog 的开始大致就是从 2009 年开始的，所以目前的 Post 几乎就可以代表 2009 年了。</p>
<p>2009 年的第一篇 blog 就是关于 2008 年的总结，在那里提到了要给 2009 年定计划，然而最后其实也没有什么计划，甚至连新年愿望也没有什么想法，然而一下子就又该新年了，似乎是到了每天都要感慨光阴似箭的年龄，所以今年写总结的气氛明显感觉比去年要沉重一些呀。  </p>
<p>2009 年其实发生的事情还是比较多的吧，一个寒假和一个暑假让我明白了不少事。</p>
<p>寒假没有回家，没有回家的原因当然不是为了要好好学习，但是既然留在了学校就想一定要好好学习吧！实际上收获似乎比较小，主要的事情是在实验室做一个实验，好像是 Active Learning 以及  Content Based Image Retrieval 相关的，不过既然我连实验是做些什么都不记得了，可以看出来并没有太多 interesting 的东西出来。不过由于时间比较宽松，虽然可以在一些现有的代码基础上跑新的实验，我还是重新把整个 code base 写了一遍，并开始尝试各种 fancy 的 Matlab 代码组织方式，包括 packaging 以及 OOP 等，最后我写了一篇 blog 放弃在 Matlab 中 OOP 了，结论很明显，此路不通，自找麻烦。当然，与 Matlab 的斗争并没有这么终止，反正我是相当方案那种杂乱无章的代码组织方式，于是我还评估过一些 alternative [...]]]></description>
			<content:encoded><![CDATA[<p>2009 年快要过去了，虽然我总是觉得奥运会明明就是今年举行的，但是日历上确实写着 2009 年。大家都写总结了，于是我也写吧，又不想用“总结”这么土的标题，就打开播放列表，找来找去看到“飘着”这首歌，回想自己这一年，也许这个词是很贴切的呢，寒假没有回家，也就飘在学校了，上半年住在玉泉校区，几乎每天飘来紫金港，下半年搬到紫金港校区，也几乎每天飘去玉泉。所以，就这个标题吧，很合适呢，歌也是很好听的。</p>
<p>说起来要写总结还真不是一件容易的事，我自己的记忆向来比较混乱，又是喜欢大幅度跳跃式的搜索，所以需要借助一些辅助的东西，比如 blog 、记事本、零碎的日记等等。其实这个新 blog 的开始大致就是从 2009 年开始的，所以目前的 Post 几乎就可以代表 2009 年了。</p>
<p>2009 年的第一篇 blog 就是<a href="/?p=31">关于 2008 年的总结</a>，在那里提到了要给 2009 年定计划，然而最后其实也没有什么计划，甚至连新年愿望也没有什么想法，然而一下子就又该新年了，似乎是到了每天都要感慨光阴似箭的年龄，所以今年写总结的气氛明显感觉比去年要沉重一些呀。 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/tongue.png' alt=':p' class='wp-smiley' /> </p>
<p>2009 年其实发生的事情还是比较多的吧，一个寒假和一个暑假让我明白了不少事。</p>
<p><span id="more-420"></span>寒假没有回家，没有回家的原因当然不是为了要好好学习，但是既然留在了学校就想一定要好好学习吧！实际上收获似乎比较小，主要的事情是在实验室做一个实验，好像是 Active Learning 以及  Content Based Image Retrieval 相关的，不过既然我连实验是做些什么都不记得了，可以看出来并没有太多 interesting 的东西出来。不过由于时间比较宽松，虽然可以在一些现有的代码基础上跑新的实验，我还是重新把整个 code base 写了一遍，并开始尝试各种 fancy 的 Matlab 代码组织方式，包括 packaging 以及 OOP 等，最后我写了一篇 blog <a href="/?p=62">放弃在 Matlab 中 OOP 了</a>，结论很明显，此路不通，自找麻烦。当然，与 Matlab 的斗争并没有这么终止，反正我是相当方案那种杂乱无章的代码组织方式，于是我还评估过一些 alternative ，比如 numpy/scipy ，各有好处吧，我也写过一篇 blog <a href="/?p=71">Matlab vs. scipy &#038; numpy</a> ，结论大致是，numpy 看起来很美好，但是还是用 Matlab 吧……事实上，我还尝试至少在一些小范围的实验上使用 numpy ，比如“<a href="/?page_id=78">漫谈 Clustering 系列</a>”的实验，甚至尝试把所有提到过的算法都用 numpy 实现，做成一个库，然而也没有能坚持下来，一来我通常对某一个算法的具体最优实现方法并没有太多了解，比如 kmeans 如何用 KDTree 来加速，如何处理 sparse 的情况等等，这样一来我做这样一个东西似乎除了用作 blog 文章的 demo 代码之外，在实际应用中意义并不大了；二来虽然 Matlab 确实很 ad hoc ，但是 it just works ，对比这两者，numpy 就好比优质新鲜的各种食材，如果下功夫并且有那个能力的话，做出满汉全席应该都不是不可能的；而 Matlab 就好比面包，最大的好处就是可以直接吃。所以，选择哪一个其实就取决于你是要填饱肚子还是要学习厨艺了。</p>
<p>除了跑实验之外，就是看各种书以及乱七八糟的论文吧，用这些形容词是因为确实很乱，说起来，相关的可以看的书真的很多的，然而我其实每一本都没有能认真看完了，而论文则是从某个 topic 找到一些相关的论文，然后通过引用等关系找到更多的，各种都看，也没有太在意论文的质量、出处、作者之类的。然后对自己学到的一些东西做了一些总结，“<a href="/?page_id=78">漫谈 Clustering 系列</a>”的不少文章应该就是在这段时间写的吧，算是一个整理吧，好处还是很多的，可以加深理解，加固记忆，而且这样的中文资料也比较少，对别人也有一些帮助。不过发现我自己的遗忘效率还真是有点高，有时回过去看，发现不少文章里总结过的东西，自己也一点没有印象了，大概还是没有理解得透彻吧。</p>
<p>不过，越来越多地偏向于理论算法相关的东西，我也稍有些担心，有一些自己更加擅长的 industry 相关的东西不能随便丢了，所以也尝试不要太脱离，比如 Open Source ，寒假<a href="/?p=206">把 Yasnippet 加入 ELPA</a>了；自己也开始关注 Google V8 Javascript Engine 的东西，<a href="/?p=186">翻译了一下它的 Design Principle</a>，本来还打算仔细研究它的实现代码的，因为我对 Language VM 也有一些经验和兴趣，不过最后这个愿望实际上是搁浅了，似乎并不是一件可以花“不太多”的时间就可以完成的事；另外，也<a href="/?p=223">帮朋友做一些东西</a>，这个待会再细说好了；如果还有什么的话，大概就是 VIM 的渐渐熟悉吧，我目前的状态是 VIM 和 Emacs 两者都用，主要是 VIM 在 Windows 下用起来要稍好用一些，但是写 TeX 文件的话，还是 AucTeX+cdlatex 最舒服啊。</p>
<p>其实寒假是相当松散的，除了学习之外，我还时常冒着冷风去<a href="/?p=270">画画</a>，春节是学校给没回家的同学组织<a href="/?p=64">过的</a>，免费蹭了一顿饭，相当丰盛啊，伤心的是为啥自己那么容易就饱了？然后在 11 楼寝室看到杭州烟花齐放的景象，相当漂亮的。</p>
<p>总而言之，额，悲剧，我本来想说的是，寒假是相当虚度的，可是为什么写了这么多东西？-_-bbbb 啊，那看来其实还是做了一些事的，怎么会？是不是我把时间搞错了？反正，不管怎么说吧，其实寒假，大概还是很痛苦吧，一个人。而且读了这么多年的书，生物钟大概是形成了，整个寒假学习的效率其实是相当低的，而且最终也还是没有忍住，把《空之轨迹 3rd》装上玩了一遍。另外，关于家里，我一直都觉得，自己的父母对我约束并不多，平时也会尊重我自己的一些决定，所以我也很尊重他们的决定，而且这几年也一直都觉得自己对这些事也很淡然，不管他们分不分开，我无非是再多了一个家吧，或者再多一个叔叔再多一个阿姨好了，爸爸还是爸爸，妈妈还是妈妈，并没有太多变化。小时候在外面读书，其实是不太想家的，因为那个时候似乎没有注意到家的温馨，然而等到我开始注意时，又没有了。当然我现在依然很淡然，其实关于家庭，我不知道是我们这一代还是一直都是这样，其实我的情况比许多人要幸福吧。我只是觉得有点可悲的是自己直到这么多年以后才发现自己并不是那么不在意的。每个家里应该都还是挺温馨的吧，不过似乎与我无关，大家对我也都很客气，甚至很关心，可是我却很沉默，仿佛自己是一个陌生人一样。但是不能不回家的呢，我向来是一个比较听话的人，这次实属意外，所以在 3 月底还是回了一趟家，给爷爷扫墓。总是让父母惦记着，朋友们也时常问起，其实是很过意不去的。</p>
<p>啊，气氛不太对，总之呢，我的得出的结论是，还是时常回家看看吧，至于学习，假期的时候还是可以更多地放松一些吧，不要太勉强，这个寒假看起来做了不少事吧，其实也都是零零碎碎，并没有完成的或者完整的东西，也就是那种“忙忙碌碌但是什么都没有做”吧。</p>
<p>另一个是暑假。错过了自己想要做的事。发现机会不是等来的，而且像大四暑假这种黄金时段，错过了就不会再有第二个了。本来想，Google Summer of Code 或者实习，至少做一件事。由于自己悲剧地到现在为止还没有正式地去公司实习过，所以想去了，于是放弃了 GSoC 。取舍是必须的，然而提前退出大概算是失误吧，或者说我对“去实习”这件事太有信心了？与导师稍微聊了一下，他也支持，甚至还很主动地表示如果实验室到时候没有特别的事的话，应该还可以帮我联系 MSRA 的熟人，让我可以在那边做一些更有趣而不像平常实习那样那么多苦力的事情，我很高兴，于是也就没有自己去联系任何公司，实际上由于 moonykily 的推荐，他实习的那个组（似乎是多媒体组？）还打电话来面试过我，而且后来听说结果也还不错，但是我也并没有再去联系。到后来导师几次问我暑假自己有没有什么安排，我也都很腼腆地说自己没有安排，不过如果实验室不忙的话，还是想去实习一下。</p>
<p>结果是暑假我留在了实验室，什么都没有做。不知道导师是忘记了，或者是实验室有事。其实实验室是有一些事，我在暑假也做了一些事，Image Annotation 相关的，总之我觉得不是需要花一个暑假来做的事吧。不过，结果是我留下来了，浪费了一个很大的机会，当然不是怪导师，这个事情如果有失误的话也完全是在我自己了，大家应该都明白如果真的想要去做一件事，通常都是能做成的。其实另一方面也是一样，如果自己本身就没有很坚定的话，那是肯定什么都做不成的吧。至少我还是得到了一个教训的。我太被动了。</p>
<p>那么暑假呢？留下来之后，由于生物钟的关系（好吧，其实是个很好的借口），效率变得非常低下，到后来果然又玩了些游戏：之前没有玩过的《仙剑三》、《天之痕》和之前玩过的《三国志 11》。我有一个记事本，主要功能是对每天的事情做简单的计划或者记录，每当我看到里面 8 月整个是空白时都觉得一阵心寒…… -.-bb </p>
<p>不过我也没有完全堕落掉啦，从 blog 上来看的话，大致做了几件事：</p>
<ul>
<li>Yasnippet 维护和后续开发工作的移交：<a href="/?p=348">Open Source Never Die</a></li>
<li>Windows 7 各种测试版趋于稳定，发布在即。于是我将笔记本装成了 Windows ，开始明确划分笔记本和实验室的台式机的职能。</li>
<li>试图研究输入法（特别是在装 Windows 之前），因为 sunpinyin 的文档比较丰富，所以看了一些它的设计文档以及相关的代码，本意是想是否能把自己学的一些相关的东西用到里面加以改进，期间再结合看的一些其他相关的东西，开始写“<a href="/?page_id=353">漫谈 Language Model 系列</a>”，计划写四篇，其实大致结构也都有谱了，本来想应该可以很快写完，但是最后写了两篇之后搁置了，直接原因是我想在实现篇中用一些实际搜集的数据。</li>
<li>于是为了搜集数据我开始折腾一些爬虫相关的东西，反正假期时间比较充裕，于是我“完美主义”的瘾有些犯，最后无意中找到 Scrapy 这个东西，很有意思，研究了一下，也写了几篇 blog <a href="/?p=366">[1]</a>、<a href="/?p=381">[2]</a>。不过，由于到 8 月份真正做事情的效率已经算落到低谷了，搜集实际数据的进展相当缓慢，最后 8 月末去做了另一件事，所以最终这个任务没有完成，Language Model 系列 blog 也无限期暂停了，暑期对于输入法的研究所完成的全部工作也到此为止。</li>
</ul>
<p>总结呀，暑假真悲剧呀！同寒假一样吧，很快就会颓废起来，但是这么长的时间不能全让他颓废了，仅靠自律还是很不靠谱的，如果是在实习之类的有外部约束的情况下应该会好一些吧。我不得不认识到，原来我的自制能力还是不行啊，特别是在比较长期的情况下。所以我现在比较担心的是我的整个研究生阶段了。其实两年半时间非常短，实验室比较宽松，导师总是说给大家更多的自有大家可以自己选感兴趣的研究方向，然而实际上并不好选。第一学期有很多课还是比较好的状态，到后来没有课了，一切全由自己来把握的话，我真的很担心猛然发现已经是毕业前夕了却还没有选出自己感兴趣的研究方向。那可真是天大的悲剧。自己也要多花心思，也要多和导师沟通一下才行。 >_<</p>
<p>另外，2009 年也尝试了一些类似于兼职的事吧，不过像我这么懒散被动的人，确实几次都是被抓的，在我看来几次都是悲剧吧——我都没有坚持到做完。一次是关于 Sentiment Analysis 的东西，朋友的创业，找我的时候我只是说可以帮忙，其实也是在给自己留后路，由于那段时间实验室一直比较忙，所以一直没能太深入，后来就渐渐没有再关注了。再一次是 LaTeX 排版，完全是人力活，我也不知道那个人是怎么找到我的，而且我也不知道我为啥就答应了，也许是我说话太委婉了，是不是该好好学习一下如何来拒绝别人？结果也是比较枯燥，最后粗略地完成了任务，并表示拒绝任何后续的东西了。=.=bb 然后 8 月低去了一趟温州，是朋友的创业公司，直接去同客户接触了，一个 Erlang 的项目，算是从某种程度上体验了一下实习吧，最后没有能坚持下去一方面也可以说是没有办法吧，项目的计划是 2 个月，我参与了 9 月和 10 月，项目没有按预期完成，但是我不能再花太多时间在里面了，实验室、课程和俱乐部的事迫使我退出了，当然还有一个重要的原因是这样结果延期的项目到后期其实是相当痛苦的工作。不过总结起来在这件事情上还是有相当多的收获的：</p>
<ul>
<li>与客户直接接触，幸运的是这是一个挺有想法并且也很聪明的客户，不过，我想以后我再也不想做这样的事了吧。另外，特别讨厌他抽烟。</li>
<li>体验了一下“上班”的生活：起床，去公司，下班，回来睡觉。每天重复，让人崩溃。还是在学校里好啊，读书真好！</li>
<li>了解了一些创业公司的事情（虽然并不深入），结论是，太辛苦啦，真不容易啊，所谓辛苦在于，即使你是一个很纯粹的技术公司，你也不能只是关注与技术，而是要去 care 一个公司运作的方方面面，各种细节的问题会瞬间让一个人的大脑离散化吧，特别是像创业公司这样小规模，没有成熟的管理流程，没有足够的人手，什么都要靠自己的情况。特别是在成为俱乐部 President 并开始关心整个俱乐部的各方面事情之后，越发觉得维持一个公司的运作对于我来说是 totally mission impossible 的吧。</li>
<li>在实际项目中使用 Erlang ，得到了一些经验，也有不少失败的尝试。对于 Erlang 这个语言，只好说是<strike>又爱又恨</strike>相当恶心吧，不过关于 Actor 以及 Message Passing 这样的东西我是彻底喜欢上了，导致后来自己在其他语言里写一些多线程的程序时都会不自觉地开始做一些“山寨版的 Actor 实现”了。</li>
</ul>
<p>不过反正也还是中途退出了嘛，我可以找出许多理直气壮的理由来，但是说到底还是有些愧疚的。每次都没能帮忙帮到底，下次各位要找兼职什么的还是不要来找我了…… =.=!!</p>
<p>然后呢，9 月开始由临时宿舍搬到紫金港，开始了正式的研究生生活。一如 houshui 在 MSTC 月刊里的那篇文章一样，读的不是研，是寂寞。关于同班同学，除了实验室原本就认识的两三个人之外，其他的人至今完全不认识，连班长是谁都不知道，荒唐的事还有更多，比如，我至今不知道三个室友叫什么名字，再比如，我至今还在睡着凉席。其实我也就是这样样子了，不熟悉的人，即使上下铺一年也几乎不说话，这样的事情也不是第一次在我身上发生；熟悉的人，站在路边也能一口气聊上一个多小时不知道时间，也是有过的。但其实这就是研究生生活呀。其实我该习惯了，因为本科无非是稍微好一点而已，不过现在是连寝室也不是一个让人觉得值得回去的地方了。</p>
<p>如我一开始说的那样，自己这一年不停地在 ZJG 和 YQ 之间来回，之前住在 YQ 来 ZJG 是因为要来实验室，现在住在 ZJG 要去 YQ 是因为要上课。然而有时候我发现自己即使没有课或者跷课了还是会跑到 YQ 去，到了 YQ 自己在 bgs 里转来转去，又很想回 ZJG 了，好像不停地在找什么东西一样。当然什么都没有找到。最近听到一首歌里有唱这样一句：</p>
<blockquote><p>
there&#8217;s a reason why<br />
people don&#8217;t stay where they are
</p></blockquote>
<p>不知道是不是说的这个意思，但是当我细想自己一天的生活时，才发现如果没有 MSTC 的话，我也就是每天早上起床去实验室，晚上回寝室睡觉，大概数周甚至数月只开口说几句话也是有可能的啊——真是太恐怖了。</p>
<p>其实对于俱乐部，大家也都应该是有很深的感情的，而我想我和大部分人的区别大概在于：MSTC 是我的课余生活或者说社交圈的<b>全部</b>，特别是现在这个情况越发极端了。于是接手俱乐部 President 时没有犹豫。我以为我能干得很好呢，才发现这是一件多么不容易的事。当然我会努力，从九月份到现在，其实是能很明显地感觉到自己在慢慢成长的。当然，变得成熟了我也还是会是 kid 啦 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/tongue.png' alt=':p' class='wp-smiley' /> ，另外，作为主席也和更多的人有了接触，越发体验到大家各自的强大和可爱之处了。 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/biggrin.png' alt=':D' class='wp-smiley' /> </p>
<p>困死了，两点半了，接下来我开始列表式吧…… >_<</p>
<ul>
<li><a href="/?p=338">本科毕业</a>，穿了学士服，同学各奔东西，一切无非是一眨眼之间。</li>
<li>自己买了一个 Thinkpad X200 。</li>
<li>感情上巨大波折，重新思考许多问题，以及自己的处事方式。</li>
<li>喜欢上孙燕姿的歌，之前总是单纯喜欢歌，现在大概是第一次开始因为歌手的声线而喜欢歌吧，也是第一次把一个歌手的几乎所有歌都找来听。</li>
<li>犬夜叉漫画结局了。</li>
<li>开始使用记事本，一年的时间证明，对我来说，这是优于各种电脑或者电子设备上的各种 GTD 类工具的，不管是做 plan 还是简单的记录。</li>
<li>有些时候变得不那么完美主义了，开始接受许多“it just works”的方案。没有太多的精力来折腾了。</li>
<li>GIP 0 结束，GIP 1 也将在本周结束。活动形式趋于成熟，然而目前来看气氛有一些不太健康的感觉，不知道是不是因为《Beautiful Code》这本书本身的原因，大家在读每一章的时候似乎有些不自觉地以一种批判的态度来对待了，而不是更加注重我们从中能得到什么或者是怎么去吸收作者试图传达给我们的东西。今后我会尝试改变吧。</li>
<li>仍然在与 Matlab 作斗争中，尝试了 fancy 的 OOP ，再尝试了无比混乱没有任何组织的代码方式，目前正在尝试折衷方案。发现自己解决很多问题的方法都是通过一些尝试来逐步调整。</li>
<li>仍然没有 paper ，虽然帮导师做了一些工作，然而运气不太好的是投的几篇 paper 都被据了，自己原创的 idea 则更是没有，要做一些理论推导就更显得功底不足了，有一点点迷茫，不过应该会慢慢好起来吧。</li>
<li>完整看了《梦断代码》、《麦田里的守望者》、《明朝那些事》、《炼金术士》、《上帝与新物理学》、《世界是平的》。</li>
<li>MSTC President 和实验室 IRML Group 小组长，一些管理方面的事情开始多起来，时常被外部事情打断，处理事情的方式开始出现严重多线程时间分片的情况，目前平衡还没有把握好，最典型的情况是拿出手机看时间，看的过程中突然去想其他事了，当看完把手机放回口袋的时候发现完全不知道是几点了——这样的情况现在还时常发生。</li>
<li>第一次坐船。杭州的水上巴士。</li>
<li>见了一个小学 &#038; 初中同学，解开了八年的结——对自己很无语呀……</li>
<li>参与 Rhythm 发起的 CPiE 的翻译，除了 V8 设计文档之外，第一次翻译东西吧。</li>
<li>尝试再恢复 <a href="/?p=304">iRobot</a> 开发，最终还是再次暂停。 T_T</li>
<li>手机又丢了一个，新手机依然是 Nokia ，第一次用智能手机，虽然是最低档的那种。</li>
<li>第一次用 RPGMaker。</li>
<li>选了日语课，开始学日语，虽然现在还非常非常初级。</li>
<li>第一次吃披萨。</li>
<li>第一次从玉泉步行到紫金港。</li>
<li>第一次看日食。</li>
<li>第二次 K 歌。</li>
<li>似乎渐渐开始能喝出各种茶叶的不同了。</li>
<li>现在睡前总是会在大脑里把各种各样的事都过一遍，结果每次都要非常困难才能睡着，亟待改进。</li>
<li>……</li>
</ul>
<p>列了好多，总结起来，其实就是整天都在忙碌，但是却什么都没有做，或者说做了很多事情，却一事无成吧。不过我也不是那么沮丧。毕竟看起来还是相当充实的一年。而且失败的东西其实是能得到许多教训的吧。</p>
<p>太晚了，要赶回去睡了，都快要四点了，于是不写了吧。每次写都要这么悲剧啊。 >_< bb</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.pluskid.org/?feed=rss2&amp;p=420</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>名字空间的问题</title>
		<link>http://blog.pluskid.org/?p=418</link>
		<comments>http://blog.pluskid.org/?p=418#comments</comments>
		<pubDate>Tue, 24 Nov 2009 14:56:48 +0000</pubDate>
		<dc:creator>pluskid</dc:creator>
				<category><![CDATA[Bugs]]></category>
		<category><![CDATA[Fun]]></category>

		<guid isPermaLink="false">http://blog.pluskid.org/?p=418</guid>
		<description><![CDATA[<p>昨天晚上回到寝室，发现室友都没有在玩游戏，而是关灯睡觉了，很是惊奇，后来才发现原来寝室没有交电费给停电了，我倒是觉得挺开心的 -.- ，至少这样可以早睡了。于是也整理睡觉，正要关机的时候收到湖边的短信，让我帮忙调一段很短的汇编代码，其实我汇编已经几乎忘光了，连 mov 的方向在两种汇编下面分别是怎样的都分不清楚了，不过既然是很短的，那还是看看能不能帮上忙吧。于是第二天一大早就起来，然后就邂逅了这个让我不断地以为自己肯定是还没有睡醒的 bug 。代码是嵌入在 C 语言里的，大概是某个题目吧，要求用汇编来实现把小写字母转换为大写，也就是实现注释中那段 C 语言的功能：</p>
<p></p>

#include &#60;conio.h&#62;
int ch;
void main&#40;void&#41;
&#123;
    _cputs&#40;&#34;please input a char: \n&#34;&#41;;
    while &#40; 1 &#41;
    &#123;
        ch = _getche&#40;&#41;;
        _asm &#123;
    [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/2009/11/asm_bug.png" alt="asm_bug" title="asm_bug" width="339" height="99" class="alignright size-full wp-image-419" />昨天晚上回到寝室，发现室友都没有在玩游戏，而是关灯睡觉了，很是惊奇，后来才发现原来寝室没有交电费给停电了，我倒是觉得挺开心的 -.- ，至少这样可以早睡了。于是也整理睡觉，正要关机的时候收到湖边的短信，让我帮忙调一段很短的汇编代码，其实我汇编已经几乎忘光了，连 mov 的方向在两种汇编下面分别是怎样的都分不清楚了，不过既然是很短的，那还是看看能不能帮上忙吧。于是第二天一大早就起来，然后就邂逅了这个让我不断地以为自己肯定是还没有睡醒的 bug 。代码是嵌入在 C 语言里的，大概是某个题目吧，要求用汇编来实现把小写字母转换为大写，也就是实现注释中那段 C 语言的功能：</p>
<p><span id="more-418"></span></p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#include &lt;conio.h&gt;</span>
<span style="color: #993333;">int</span> ch<span style="color: #339933;">;</span>
<span style="color: #993333;">void</span> main<span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    _cputs<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;please input a char: <span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span> <span style="color: #0000dd;">1</span> <span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
        ch <span style="color: #339933;">=</span> _getche<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        _asm <span style="color: #009900;">&#123;</span>
            mov al<span style="color: #339933;">,</span> ch
            cmp al<span style="color: #339933;">,</span> <span style="color: #ff0000;">'@'</span>
            je end1
            cmp al<span style="color: #339933;">,</span><span style="color: #ff0000;">'a'</span>
            jl end2
            cmp al<span style="color: #339933;">,</span><span style="color: #ff0000;">'z'</span>
            jg end2
            sub al<span style="color: #339933;">,</span>20h
            mov ch<span style="color: #339933;">,</span> al    
end2<span style="color: #339933;">:</span>    
        <span style="color: #009900;">&#125;</span>
        <span style="color: #808080; font-style: italic;">/*if (ch == '@' )  break;
        if ( ch &gt;= 'a' &amp;&amp; ch &lt;= 'z' )
        {
        ch=ch-0x20;
        }*/</span>
        _putch<span style="color: #009900;">&#40;</span>c<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
end1<span style="color: #339933;">:</span> <span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>其实如果你是一个比较细心的人，也许一眼就能看出问题所在了。不过可惜我没那么细心，而且时常会忽略掉一些重要线索，比如上次<a href="/?p=410">电脑过热</a>的问题。总之，我看了一下，没发现什么问题，于是决定运行一下试试看，结果嘛：所有的字母都没有转换，而且输入 @ 也没有按照预期的退出。</p>
<p>这个问题是很诡异的，于是我设了个断点，观察 al 的值，发现和我输入的字母简直风马牛不相及，怎么会呢？！我再把断点提前，就在 _getche() 后面断下来，按下 a 之后，程序中断了，此时观察 al 和 ch 的值，竟然都是 0&#215;61 ，也是就是 &#8216;a&#8217; ，然后我再次确认了一下调试的时候光标所指的这一行是“即将要执行的行”而不是“刚才执行过的行”，看来 _getche() 里面大概用了 al 来用了，所以刚好也是这个值吧。</p>
<p>然而，当我按下 Step 之后……光标跳到下一条语句，说明 mov 指令被执行了，此时在观察，发现 al 变了，只是，变得和 ch 不一样了，至于具体是什么值嘛，那就是天马行空了，几乎每次重新执行都不一样。真是邪门了，赋值之前两个东西还是一样的，赋值之后反而不一样了……我真怀疑自己是不是太惦记着所以现在是梦见自己在调试，确实今天的事情都很不符合常理呀，比如早上 7 点多实验室竟然就有人了，还比如……唔，还有一件比较诡异的事是什么？一时想不起来了，总之，先调程序要紧。</p>
<p>于是我怀疑是不是调用某个函数的时候用到了 al 寄存器，让它被改掉了？于是我把 al 改成了 dl ，运行了下发现结果竟然是一模一样的：赋值之前 dl 居然也等于 &#8216;a&#8217; ，而赋值之后又变成了奇怪的值！天哪！然后我打开 Visual Studio 的寄存器窗口观察，发现我选 dl 是凑巧，而且凑巧在调用 _getche() 之后 al 和 dl 都等于读入的那个字符，还真是够巧的，我偏偏就选了个 D ……然而更关键的是，赋值变掉的问题还没有解决。而且，这附近就一个 _getche() ，已经调用过了，后面也没有什么函数调用，就一个光棍 mov 语句，还能怎么样？难道 VS 偷偷地插入一些调试相关的代码进去了？于是我不放心地查看了一下生成出来的汇编代码，虽然确实是有一些调试相关的东西（例如 call __RTC_CheckEsp 之类的），不过都离得比较远。这就奇了怪了……</p>
<p>其实答案很简单。我被 Visual Studio 骗了，当然，如果我够细心，我本来早该发现问题的，或者说如果我没有忽略掉那个被我忘掉的诡异的地方的话：就是 ch 是个 int 类型的变量，4 个字节，而 al 是 1 个字节的寄存器，怎么能这样直接 mov 呢？不过我没有仔细想这个问题，大概觉得能自动转换之类的吧，因为脑海里对 x86 汇编的印象就是寻址模式千奇百怪极其复杂。</p>
<p>反正我也不记得我是怎么醒悟的了，不过终于还是醒悟了：ch 明明就是个寄存器嘛。调试的时候，Visual Studio 看到 ch ，就把它当全局变量显示给我看了，值是正确的，没问题；可是，实际执行的是把 ch 寄存器的值赋给了 al 寄存器，于是，是个随机值嘛，一切真相大白了，把变量 ch 改了个名字，一切正常了（当然，这个时候编译器报错了，说 al 的长度和这个 int 型变量不匹配，不能赋值，于是我直接赋给了 eax ）。</p>
<p>标题写成这个样子，一方面是为了避免如果标题写得太明显的话，故事讲起来就没有意思了，另一方面，也确实可以顺便说一下，其实这就是名字空间的问题了。在这里，是变量和寄存器两个名字空间，变量的名字和寄存器的名字重复了，也就导致了问题。其他的编程语言里也有类似的问题，通常有“函数”的名字、“变量”的名字、“类型”或者“类”的名字等，例如，在 C 语言里面，类型的名字和变量名应该是属于不同的名字空间里，然而，如果定义某个变量和某个类型名字重复的话，是会出错的；而在 Python 之类的脚本语言里，“类型”本身也就是一个 first-class value ，类型的名字也就和变量名是同样的东西了，压根就在同一名字空间里。</p>
<p>这一方面（就我所知的范围内）最典型的应该还要数 Lisp 的两大变种（或者说方言）：Common Lisp 和 Scheme 。前者就是分了两个名字空间：函数和变量，可以定义一个函数 foo 和一个变量 foo ，它们被放在不同的名字空间里，互相不影响；而 Scheme 则坚持一个名字空间，函数和变量都一视同仁。其实想想两种都有它的道理，也不能直接说哪种好哪种坏了。</p>
<p>不过，既然大部分情况还是被放在同一个名字空间里，或者说即使是不同的名字空间也不允许重复，遇到 C 语言之类的情况还会得到编译错误，但是如果是像 Python 之类的动态语言，估计就会 silently fail 了——因为用一个完全不同类型的东西去覆盖一个变量并不是什么不合法的行为呀。比如我最近正在写的一个程序里，有一个 module 的名字叫做 plugin ，在另一个 module 通过 import plugin 就能得到这个 module ，然而我有好几次都差点将一个局部变量命名为 plugin 了，这样就会隐藏掉 plugin module 了。为了避免类似的问题，通常一些变成习惯中也有为不同名字空间的名字选择特定的命名规范的习惯，例如大小写、前缀、后缀之类的。不过，Python (<= 2.6) 的标准库可以说是完美地违反了这类的约定呀：大小写、下划线、CamelCase 被混乱地用在 package 、module 、class 、function 等命名上，标准库看起来一点都不标准。说来也真是邪门，我写 Python 程序应该都写了不少了，可是每次用它的正则表达式库都要去查一遍文档，而且十次有九次都还会出点错才能写对，看来是不是该找一本书来系统地看一下了。 :-/</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.pluskid.org/?feed=rss2&amp;p=418</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Growl for Windows meets GreaseMonkey</title>
		<link>http://blog.pluskid.org/?p=415</link>
		<comments>http://blog.pluskid.org/?p=415#comments</comments>
		<pubDate>Sat, 21 Nov 2009 13:43:27 +0000</pubDate>
		<dc:creator>pluskid</dc:creator>
				<category><![CDATA[Tool]]></category>
		<category><![CDATA[GreaseMonkey]]></category>
		<category><![CDATA[zju]]></category>

		<guid isPermaLink="false">http://blog.pluskid.org/?p=415</guid>
		<description><![CDATA[<p>最近学校的 CC98 论坛简直慢得令人发指，总是让我感觉又回到了蓝田时代——洗完脸刷完牙回来了一个页面还没有打开。加上考试周到了，又特别喜欢灌水，所以老是想去刷新页面看有没有新帖子，其实主要也是看的俱乐部水楼、晚安楼之类的地方，不过每次等半天页面终于出来了，发现没有新帖也觉得相当浪费时间和感情。想起 quark 之前有做过一个监视 98 的新帖子，然后用 notification-daemon 给出提示的东西，于是我想也做一个类似的东西好了，让它自己在后台刷新，网络慢点也没有关系，至少我不用一直等在那里了，有新帖的时候再关注一下好了。</p>
<p>不过在 Windows 下没有 notification-daemon 可以用，而且诸如 Python、Ruby 之类的脚本在 Windows 下跑起来都觉得相当别扭。记得之前看过一个叫做 Growl for Windows 的东西（这家伙的缩写真有点那个……），好像就是 Mac 下的那个 Growl 的 clone 。于是去仔细看了一下，似乎支持一套标准的协议，而且各个语言的 binding 都很全。</p>
<p>抓取和解析 98 帖子的 Python 库我是有现成的，不过确实还真不想在 Windows 下开一个黑框框来跑个 Python 程序，怎么看怎么别扭。然后又发现 Growl for Windows 支持订阅网络上的 notification ，这样一来就可以在实验室的机器上跑 notification server 了，这个是比较赞的，只是这样一来估计回调之类的就不好实现了——比如点一下 notification popup 能弹出一个框来输入回帖或者是在浏览器里打开该帖子的页面之类的，不过这个需求倒不是特别大。</p>
<p>可是当我真正开始尝试做的时候，才发现虽然 Growl for Windows 的主页上列出了各种语言的 binding ，但是其实各个 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/2009/11/Growl-for-Windows.png" alt="" title="Growl-for-Windows" width="288" height="125" class="alignright size-full wp-image-521" />最近学校的 CC98 论坛简直慢得令人发指，总是让我感觉又回到了蓝田时代——洗完脸刷完牙回来了一个页面还没有打开。加上考试周到了，又特别喜欢灌水，所以老是想去刷新页面看有没有新帖子，其实主要也是看的俱乐部水楼、晚安楼之类的地方，不过每次等半天页面终于出来了，发现没有新帖也觉得相当浪费时间和感情。想起 <a href="http://lihdd.net">quark</a> 之前有做过一个监视 98 的新帖子，然后用 notification-daemon 给出提示的东西，于是我想也做一个类似的东西好了，让它自己在后台刷新，网络慢点也没有关系，至少我不用一直等在那里了，有新帖的时候再关注一下好了。</p>
<p>不过在 Windows 下没有 notification-daemon 可以用，而且诸如 Python、Ruby 之类的脚本在 Windows 下跑起来都觉得相当别扭。记得之前看过一个叫做 <a href="http://www.growlforwindows.com/">Growl for Windows</a> 的东西（这家伙的缩写真有点那个……），好像就是 Mac 下的那个 Growl 的 clone 。于是去仔细看了一下，似乎支持一套标准的协议，而且各个语言的 binding 都很全。</p>
<p><span id="more-415"></span>抓取和解析 98 帖子的 Python 库我是有现成的，不过确实还真不想在 Windows 下开一个黑框框来跑个 Python 程序，怎么看怎么别扭。然后又发现 Growl for Windows 支持订阅网络上的 notification ，这样一来就可以在实验室的机器上跑 notification server 了，这个是比较赞的，只是这样一来估计回调之类的就不好实现了——比如点一下 notification popup 能弹出一个框来输入回帖或者是在浏览器里打开该帖子的页面之类的，不过这个需求倒不是特别大。</p>
<p>可是当我真正开始尝试做的时候，才发现虽然 Growl for Windows 的主页上列出了各种语言的 binding ，但是其实各个 binding 的完成程度和质量真实良莠不齐，似乎要弄一个 server 也比较麻烦，稍微尝试了一下，出了各种错误，最后直接放弃了——因为发现了一个看起来似乎更好的选择：GreaseMonkey。</p>
<p>只要在 Firefox 里装上 <a href="https://addons.mozilla.org/en-US/firefox/addon/11611">Growl/GNTP</a> 扩展之后，就可以在 GM 里脚本里来发送 Growl Notification 了，不过，每个脚本里还需要粘贴这样一段脚本：</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #006600; font-style: italic;">// -- GrowlMonkey stuff below here - do not edit</span>
GrowlMonkey <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
    <span style="color: #003366; font-weight: bold;">function</span> fireGrowlEvent<span style="color: #009900;">&#40;</span>type<span style="color: #339933;">,</span> data<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
        <span style="color: #003366; font-weight: bold;">var</span> element <span style="color: #339933;">=</span> document.<span style="color: #660066;">createElement</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;GrowlEventElement&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        element.<span style="color: #660066;">setAttribute</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;data&quot;</span><span style="color: #339933;">,</span> JSON.<span style="color: #660066;">stringify</span><span style="color: #009900;">&#40;</span>data<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        document.<span style="color: #660066;">documentElement</span>.<span style="color: #660066;">appendChild</span><span style="color: #009900;">&#40;</span>element<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #003366; font-weight: bold;">var</span> evt <span style="color: #339933;">=</span> document.<span style="color: #660066;">createEvent</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;Events&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        evt.<span style="color: #660066;">initEvent</span><span style="color: #009900;">&#40;</span>type<span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">true</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">false</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        element.<span style="color: #660066;">dispatchEvent</span><span style="color: #009900;">&#40;</span>evt<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #009900;">&#123;</span>
        register <span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>appName<span style="color: #339933;">,</span> icon<span style="color: #339933;">,</span> notificationTypes<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
            <span style="color: #003366; font-weight: bold;">var</span> r <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
            r.<span style="color: #660066;">appName</span> <span style="color: #339933;">=</span> appName<span style="color: #339933;">;</span>
            r.<span style="color: #660066;">icon</span> <span style="color: #339933;">=</span> icon<span style="color: #339933;">;</span>
            r.<span style="color: #660066;">notificationTypes</span> <span style="color: #339933;">=</span> notificationTypes<span style="color: #339933;">;</span>
            fireGrowlEvent<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;GrowlRegister&quot;</span><span style="color: #339933;">,</span> r<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
&nbsp;
        notify <span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>appName<span style="color: #339933;">,</span> notificationType<span style="color: #339933;">,</span> title<span style="color: #339933;">,</span> text<span style="color: #339933;">,</span> icon<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
            <span style="color: #003366; font-weight: bold;">var</span> n <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
            n.<span style="color: #660066;">appName</span> <span style="color: #339933;">=</span> appName<span style="color: #339933;">;</span>
            n.<span style="color: #660066;">type</span> <span style="color: #339933;">=</span> notificationType<span style="color: #339933;">;</span>
            n.<span style="color: #660066;">title</span> <span style="color: #339933;">=</span> title<span style="color: #339933;">;</span>
            n.<span style="color: #660066;">text</span> <span style="color: #339933;">=</span> text<span style="color: #339933;">;</span>
            n.<span style="color: #660066;">icon</span> <span style="color: #339933;">=</span> icon<span style="color: #339933;">;</span>
            fireGrowlEvent<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;GrowlNotify&quot;</span><span style="color: #339933;">,</span> n<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #006600; font-style: italic;">/* json2.js 
 * 2008-01-17
 * Public Domain
 * No warranty expressed or implied. Use at your own risk.
 * See http://www.JSON.org/js.html
*/</span>
<span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">JSON</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>JSON<span style="color: #339933;">=</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><span style="color: #003366; font-weight: bold;">function</span> f<span style="color: #009900;">&#40;</span>n<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><span style="color: #000066; font-weight: bold;">return</span> n<span style="color: #339933;">&lt;</span><span style="color: #CC0000;">10</span><span style="color: #339933;">?</span><span style="color: #3366CC;">'0'</span><span style="color: #339933;">+</span>n<span style="color: #339933;">:</span>n<span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span>
Date.<span style="color: #660066;">prototype</span>.<span style="color: #660066;">toJSON</span><span style="color: #339933;">=</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><span style="color: #000066; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">getUTCFullYear</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #3366CC;">'-'</span><span style="color: #339933;">+</span>
f<span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">getUTCMonth</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #3366CC;">'-'</span><span style="color: #339933;">+</span>
f<span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">getUTCDate</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #3366CC;">'T'</span><span style="color: #339933;">+</span>
f<span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">getUTCHours</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #3366CC;">':'</span><span style="color: #339933;">+</span>
f<span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">getUTCMinutes</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #3366CC;">':'</span><span style="color: #339933;">+</span>
f<span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">getUTCSeconds</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #3366CC;">'Z'</span><span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>var m<span style="color: #339933;">=</span><span style="color: #009900;">&#123;</span><span style="color: #3366CC;">'<span style="color: #000099; font-weight: bold;">\b</span>'</span><span style="color: #339933;">:</span><span style="color: #3366CC;">'<span style="color: #000099; font-weight: bold;">\\</span>b'</span><span style="color: #339933;">,</span><span style="color: #3366CC;">'<span style="color: #000099; font-weight: bold;">\t</span>'</span><span style="color: #339933;">:</span><span style="color: #3366CC;">'<span style="color: #000099; font-weight: bold;">\\</span>t'</span><span style="color: #339933;">,</span><span style="color: #3366CC;">'<span style="color: #000099; font-weight: bold;">\n</span>'</span><span style="color: #339933;">:</span><span style="color: #3366CC;">'<span style="color: #000099; font-weight: bold;">\\</span>n'</span><span style="color: #339933;">,</span><span style="color: #3366CC;">'<span style="color: #000099; font-weight: bold;">\f</span>'</span><span style="color: #339933;">:</span><span style="color: #3366CC;">'<span style="color: #000099; font-weight: bold;">\\</span>f'</span><span style="color: #339933;">,</span><span style="color: #3366CC;">'<span style="color: #000099; font-weight: bold;">\r</span>'</span><span style="color: #339933;">:</span><span style="color: #3366CC;">'<span style="color: #000099; font-weight: bold;">\\</span>r'</span><span style="color: #339933;">,</span><span style="color: #3366CC;">'&quot;'</span><span style="color: #339933;">:</span><span style="color: #3366CC;">'<span style="color: #000099; font-weight: bold;">\\</span>&quot;'</span><span style="color: #339933;">,</span><span style="color: #3366CC;">'<span style="color: #000099; font-weight: bold;">\\</span>'</span><span style="color: #339933;">:</span><span style="color: #3366CC;">'<span style="color: #000099; font-weight: bold;">\\</span><span style="color: #000099; font-weight: bold;">\\</span>'</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>function stringify<span style="color: #009900;">&#40;</span>value<span style="color: #339933;">,</span>whitelist<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><span style="color: #003366; font-weight: bold;">var</span> a<span style="color: #339933;">,</span>i<span style="color: #339933;">,</span>k<span style="color: #339933;">,</span>l<span style="color: #339933;">,</span>r<span style="color: #339933;">=</span><span style="color: #009966; font-style: italic;">/[&quot;\\\x00-\x1f\x7f-\x9f]/g</span><span style="color: #339933;">,</span>v<span style="color: #339933;">;</span>switch<span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">typeof</span> value<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><span style="color: #000066; font-weight: bold;">case</span><span style="color: #3366CC;">'string'</span><span style="color: #339933;">:</span><span style="color: #000066; font-weight: bold;">return</span> r.<span style="color: #660066;">test</span><span style="color: #009900;">&#40;</span>value<span style="color: #009900;">&#41;</span><span style="color: #339933;">?</span><span style="color: #3366CC;">'&quot;'</span><span style="color: #339933;">+</span>value.<span style="color: #660066;">replace</span><span style="color: #009900;">&#40;</span>r<span style="color: #339933;">,</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>a<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><span style="color: #003366; font-weight: bold;">var</span> c<span style="color: #339933;">=</span>m<span style="color: #009900;">&#91;</span>a<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>if<span style="color: #009900;">&#40;</span>c<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><span style="color: #000066; font-weight: bold;">return</span> c<span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span>
c<span style="color: #339933;">=</span>a.<span style="color: #660066;">charCodeAt</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>return<span style="color: #3366CC;">'<span style="color: #000099; font-weight: bold;">\\</span>u00'</span><span style="color: #339933;">+</span>Math.<span style="color: #660066;">floor</span><span style="color: #009900;">&#40;</span>c<span style="color: #339933;">/</span><span style="color: #CC0000;">16</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">toString</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">16</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span>
<span style="color: #009900;">&#40;</span>c<span style="color: #339933;">%</span>16<span style="color: #009900;">&#41;</span>.<span style="color: #660066;">toString</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">16</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #3366CC;">'&quot;'</span><span style="color: #339933;">:</span><span style="color: #3366CC;">'&quot;'</span><span style="color: #339933;">+</span>value<span style="color: #339933;">+</span><span style="color: #3366CC;">'&quot;'</span><span style="color: #339933;">;</span>case<span style="color: #3366CC;">'number'</span><span style="color: #339933;">:</span><span style="color: #000066; font-weight: bold;">return</span> isFinite<span style="color: #009900;">&#40;</span>value<span style="color: #009900;">&#41;</span><span style="color: #339933;">?</span>String<span style="color: #009900;">&#40;</span>value<span style="color: #009900;">&#41;</span><span style="color: #339933;">:</span><span style="color: #3366CC;">'null'</span><span style="color: #339933;">;</span>case<span style="color: #3366CC;">'boolean'</span><span style="color: #339933;">:</span><span style="color: #000066; font-weight: bold;">case</span><span style="color: #3366CC;">'null'</span><span style="color: #339933;">:</span><span style="color: #000066; font-weight: bold;">return</span> String<span style="color: #009900;">&#40;</span>value<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>case<span style="color: #3366CC;">'object'</span><span style="color: #339933;">:</span><span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>value<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><span style="color: #000066; font-weight: bold;">return</span><span style="color: #3366CC;">'null'</span><span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span>
<span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">typeof</span> value.<span style="color: #660066;">toJSON</span><span style="color: #339933;">===</span><span style="color: #3366CC;">'function'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><span style="color: #000066; font-weight: bold;">return</span> stringify<span style="color: #009900;">&#40;</span>value.<span style="color: #660066;">toJSON</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span>
a<span style="color: #339933;">=</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>if<span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">typeof</span> value.<span style="color: #660066;">length</span><span style="color: #339933;">===</span><span style="color: #3366CC;">'number'</span><span style="color: #339933;">&amp;&amp;!</span><span style="color: #009900;">&#40;</span>value.<span style="color: #660066;">propertyIsEnumerable</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'length'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>l<span style="color: #339933;">=</span>value.<span style="color: #660066;">length</span><span style="color: #339933;">;</span>for<span style="color: #009900;">&#40;</span>i<span style="color: #339933;">=</span><span style="color: #CC0000;">0</span><span style="color: #339933;">;</span>i<span style="color: #339933;">&lt;</span>l<span style="color: #339933;">;</span>i<span style="color: #339933;">+=</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>a.<span style="color: #660066;">push</span><span style="color: #009900;">&#40;</span>stringify<span style="color: #009900;">&#40;</span>value<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span>whitelist<span style="color: #009900;">&#41;</span><span style="color: #339933;">||</span><span style="color: #3366CC;">'null'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span>
<span style="color: #000066; font-weight: bold;">return</span><span style="color: #3366CC;">'['</span><span style="color: #339933;">+</span>a.<span style="color: #660066;">join</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">','</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #3366CC;">']'</span><span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span>
<span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>whitelist<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>l<span style="color: #339933;">=</span>whitelist.<span style="color: #660066;">length</span><span style="color: #339933;">;</span>for<span style="color: #009900;">&#40;</span>i<span style="color: #339933;">=</span><span style="color: #CC0000;">0</span><span style="color: #339933;">;</span>i<span style="color: #339933;">&lt;</span>l<span style="color: #339933;">;</span>i<span style="color: #339933;">+=</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>k<span style="color: #339933;">=</span>whitelist<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>if<span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">typeof</span> k<span style="color: #339933;">===</span><span style="color: #3366CC;">'string'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>v<span style="color: #339933;">=</span>stringify<span style="color: #009900;">&#40;</span>value<span style="color: #009900;">&#91;</span>k<span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span>whitelist<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>if<span style="color: #009900;">&#40;</span>v<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>a.<span style="color: #660066;">push</span><span style="color: #009900;">&#40;</span>stringify<span style="color: #009900;">&#40;</span>k<span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #3366CC;">':'</span><span style="color: #339933;">+</span>v<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#125;</span><span style="color: #000066; font-weight: bold;">else</span><span style="color: #009900;">&#123;</span><span style="color: #000066; font-weight: bold;">for</span><span style="color: #009900;">&#40;</span>k <span style="color: #000066; font-weight: bold;">in</span> value<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">typeof</span> k<span style="color: #339933;">===</span><span style="color: #3366CC;">'string'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>v<span style="color: #339933;">=</span>stringify<span style="color: #009900;">&#40;</span>value<span style="color: #009900;">&#91;</span>k<span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span>whitelist<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>if<span style="color: #009900;">&#40;</span>v<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>a.<span style="color: #660066;">push</span><span style="color: #009900;">&#40;</span>stringify<span style="color: #009900;">&#40;</span>k<span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #3366CC;">':'</span><span style="color: #339933;">+</span>v<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#125;</span>
<span style="color: #000066; font-weight: bold;">return</span><span style="color: #3366CC;">'{'</span><span style="color: #339933;">+</span>a.<span style="color: #660066;">join</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">','</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #3366CC;">'}'</span><span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#125;</span>
<span style="color: #000066; font-weight: bold;">return</span><span style="color: #009900;">&#123;</span>stringify<span style="color: #339933;">:</span>stringify<span style="color: #339933;">,</span>parse<span style="color: #339933;">:</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>text<span style="color: #339933;">,</span>filter<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><span style="color: #003366; font-weight: bold;">var</span> j<span style="color: #339933;">;</span>function walk<span style="color: #009900;">&#40;</span>k<span style="color: #339933;">,</span>v<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><span style="color: #003366; font-weight: bold;">var</span> i<span style="color: #339933;">,</span>n<span style="color: #339933;">;</span>if<span style="color: #009900;">&#40;</span>v<span style="color: #339933;">&amp;&amp;</span>typeof v<span style="color: #339933;">===</span><span style="color: #3366CC;">'object'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><span style="color: #000066; font-weight: bold;">for</span><span style="color: #009900;">&#40;</span>i <span style="color: #000066; font-weight: bold;">in</span> v<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>Object.<span style="color: #660066;">prototype</span>.<span style="color: #660066;">hasOwnProperty</span>.<span style="color: #660066;">apply</span><span style="color: #009900;">&#40;</span>v<span style="color: #339933;">,</span><span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>n<span style="color: #339933;">=</span>walk<span style="color: #009900;">&#40;</span>i<span style="color: #339933;">,</span>v<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>if<span style="color: #009900;">&#40;</span>n<span style="color: #339933;">!==</span>undefined<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>v<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">=</span>n<span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#125;</span>
<span style="color: #000066; font-weight: bold;">return</span> filter<span style="color: #009900;">&#40;</span>k<span style="color: #339933;">,</span>v<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span>
<span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span><span style="color: #009966; font-style: italic;">/^[\],:{}\s]*$/</span>.<span style="color: #660066;">test</span><span style="color: #009900;">&#40;</span>text.<span style="color: #660066;">replace</span><span style="color: #009900;">&#40;</span><span style="color: #009966; font-style: italic;">/\\./g</span><span style="color: #339933;">,</span><span style="color: #3366CC;">'@'</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">replace</span><span style="color: #009900;">&#40;</span><span style="color: #009966; font-style: italic;">/&quot;[^&quot;\\\n\r]*&quot;|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g</span><span style="color: #339933;">,</span><span style="color: #3366CC;">']'</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">replace</span><span style="color: #009900;">&#40;</span><span style="color: #009966; font-style: italic;">/(?:^|:|,)(?:\s*\[)+/g</span><span style="color: #339933;">,</span><span style="color: #3366CC;">''</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>j<span style="color: #339933;">=</span><span style="color: #000066; font-weight: bold;">eval</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'('</span><span style="color: #339933;">+</span>text<span style="color: #339933;">+</span><span style="color: #3366CC;">')'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>return <span style="color: #000066; font-weight: bold;">typeof</span> filter<span style="color: #339933;">===</span><span style="color: #3366CC;">'function'</span><span style="color: #339933;">?</span>walk<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">''</span><span style="color: #339933;">,</span>j<span style="color: #009900;">&#41;</span><span style="color: #339933;">:</span>j<span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span>
<span style="color: #000066; font-weight: bold;">throw</span> <span style="color: #003366; font-weight: bold;">new</span> SyntaxError<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'parseJSON'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span></pre></div></div>

<p>其实就是一个简单的 API 包装（还附带了一个简易的 json encoder），有了这个之后，剩下的就是要在 GM 脚本里注册 Notification 类别以及发送 Notification 了。注册的代码像这个样子：</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #003366; font-weight: bold;">var</span> ntNewPost <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
ntNewPost.<span style="color: #000066;">name</span> <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;newpost&quot;</span><span style="color: #339933;">;</span>
ntNewPost.<span style="color: #660066;">displayName</span> <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;New Post&quot;</span><span style="color: #339933;">;</span>
ntNewPost.<span style="color: #660066;">enabled</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">true</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #003366; font-weight: bold;">var</span> types <span style="color: #339933;">=</span> <span style="color: #009900;">&#91;</span>ntNewPost<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
GrowlMonkey.<span style="color: #660066;">register</span><span style="color: #009900;">&#40;</span>AppName<span style="color: #339933;">,</span> <span style="color: #3366CC;">''</span><span style="color: #339933;">,</span> types<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>可以一次注册多种消息类型，发送 notification 的时候需要给定 AppName 和类别名字。notification 发送的代码像这个样子（我就直接把整个函数给出来了，这里用了 jQuery）：</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #003366; font-weight: bold;">function</span> notifyPosts<span style="color: #009900;">&#40;</span>posts<span style="color: #339933;">,</span> thread<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    $<span style="color: #009900;">&#40;</span>posts<span style="color: #009900;">&#41;</span>.<span style="color: #660066;">each</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span> <span style="color: #009900;">&#40;</span>index<span style="color: #339933;">,</span> p<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        GrowlMonkey.<span style="color: #660066;">notify</span><span style="color: #009900;">&#40;</span>AppName<span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;newpost&quot;</span><span style="color: #339933;">,</span> p.<span style="color: #660066;">author</span> <span style="color: #339933;">+</span> <span style="color: #3366CC;">&quot;@&quot;</span> <span style="color: #339933;">+</span> thread.<span style="color: #000066;">name</span><span style="color: #339933;">,</span> 
                           filterUBB<span style="color: #009900;">&#40;</span>p.<span style="color: #660066;">content</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
                           <span style="color: #3366CC;">&quot;http://www.cc98.org/face/face&quot;</span> <span style="color: #339933;">+</span> p.<span style="color: #660066;">face</span> <span style="color: #339933;">+</span> <span style="color: #3366CC;">&quot;.gif&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>前面两个参数分别是 AppName 和 notification 类别的名字，然后是标题、内容和图标，似乎这个 API 里没有提供诸如 callbackurl 之类的支持，要不然也许可以点击 notification 的时候打开指定的帖子或者回复的页面。我把那个 Firefox 扩展 xpi 文件解压开来看了看里面的内容，似乎代码比较少，竟然没有看明白到底是在哪里处理了它这里声明的 GrowlNotify 事件，不过暂时这样的需求也不大，所以先不管了。</p>
<p>有了 notify 的工具，剩下的就是监视帖子内容了，其实说是监视，无非也就是定时刷新一下，看有没有更新。一开始我想用 GM 提供的 persistence storage ，可是后来想想没有必要，如果我三天不开浏览器（虽然有点不太可能），难不成下次开的时候还把之前的帖子一股脑全部弹出来吗？一般旧的帖子会专门去考古看一下，要监视的也就是打开浏览器之后新出现的帖子，所以这个信息就保存在内存中就可以了。这样一来，事情就好办很多了，因为是在 GM 里，什么 cookie 呀之类的都不用管，在加上 jQuery ，写起来也相当方便。首先定义要监视的帖子的列表：</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #003366; font-weight: bold;">var</span> WatchThreads <span style="color: #339933;">=</span> <span style="color: #009900;">&#91;</span>
    <span style="color: #009900;">&#123;</span>board<span style="color: #339933;">:</span><span style="color: #CC0000;">60</span><span style="color: #339933;">,</span> thread<span style="color: #339933;">:</span><span style="color: #CC0000;">541996</span><span style="color: #339933;">,</span> <span style="color: #000066;">name</span><span style="color: #339933;">:</span><span style="color: #3366CC;">&quot;水库&quot;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
    <span style="color: #009900;">&#123;</span>board<span style="color: #339933;">:</span><span style="color: #CC0000;">60</span><span style="color: #339933;">,</span> thread<span style="color: #339933;">:</span><span style="color: #CC0000;">3017942</span><span style="color: #339933;">,</span> <span style="color: #000066;">name</span><span style="color: #339933;">:</span><span style="color: #3366CC;">&quot;征人打乒乓&quot;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
    <span style="color: #009900;">&#123;</span>board<span style="color: #339933;">:</span><span style="color: #CC0000;">60</span><span style="color: #339933;">,</span> thread<span style="color: #339933;">:</span><span style="color: #CC0000;">2482989</span><span style="color: #339933;">,</span> <span style="color: #000066;">name</span><span style="color: #339933;">:</span><span style="color: #3366CC;">&quot;吃饭楼&quot;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
    <span style="color: #009900;">&#123;</span>board<span style="color: #339933;">:</span><span style="color: #CC0000;">60</span><span style="color: #339933;">,</span> thread<span style="color: #339933;">:</span><span style="color: #CC0000;">1999585</span><span style="color: #339933;">,</span> <span style="color: #000066;">name</span><span style="color: #339933;">:</span><span style="color: #3366CC;">&quot;晚安楼&quot;</span><span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span></pre></div></div>

<p>然后，在文档加载完成之后进行 Growl 注册，同时在 Growl 注册完成之后对监视的帖子进行初始化，也就是获取目前的帖子数目：</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">$<span style="color: #009900;">&#40;</span>document<span style="color: #009900;">&#41;</span>.<span style="color: #660066;">ready</span><span style="color: #009900;">&#40;</span>registerGrowl<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #003366; font-weight: bold;">function</span> registerGrowl<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #009966; font-style: italic;">/* ...register Growl... */</span>
&nbsp;
    initWatchThreads<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #003366; font-weight: bold;">function</span> initWatchThreads<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    $<span style="color: #009900;">&#40;</span>WatchThreads<span style="color: #009900;">&#41;</span>.<span style="color: #660066;">each</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span> <span style="color: #009900;">&#40;</span>idx<span style="color: #339933;">,</span> w<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        w.<span style="color: #660066;">lastFloor</span> <span style="color: #339933;">=</span> <span style="color: #339933;">-</span><span style="color: #CC0000;">1</span><span style="color: #339933;">;</span>
        w.<span style="color: #660066;">newLastFloor</span> <span style="color: #339933;">=</span> <span style="color: #339933;">-</span><span style="color: #CC0000;">1</span><span style="color: #339933;">;</span>
        loopWatchThread<span style="color: #009900;">&#40;</span>w<span style="color: #339933;">,</span> MaxPage<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>这里我直接把 lastFloor 设置为 -1 去调用监视函数，让它自己去设置这个值，这样就不用再专门再写一个初始化函数了。这里的 newLastFloor 可以看作一个监视中间过程中的临时变量，由于 js 的执行特性是异步 + 回调串联起来的，和传统的直线顺序执行模型还有一些不一样，所以有些地方看起来有点奇怪。这里的 loopWatchThread 里的 Thread 并不是指“线程”，而是指一个讨论“贴”，虽然这里看起来有点像为每个要跟踪的帖子启动了一个线程（而且效果也差不多），但是实际上是通过 setTimeout 串联起来的异步调用而已。loopWatchThread 的定义如下：</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #003366; font-weight: bold;">function</span> loopWatchThread<span style="color: #009900;">&#40;</span>t<span style="color: #339933;">,</span> page<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    $.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'/dispbbs.asp'</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#123;</span>boardID<span style="color: #339933;">:</span>t.<span style="color: #660066;">board</span><span style="color: #339933;">,</span> ID<span style="color: #339933;">:</span>t.<span style="color: #660066;">thread</span><span style="color: #339933;">,</span> star<span style="color: #339933;">:</span>page<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
          <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>data<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
              feedPage<span style="color: #009900;">&#40;</span>data<span style="color: #339933;">,</span> t<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
          <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #003366; font-weight: bold;">function</span> feedPage<span style="color: #009900;">&#40;</span>page<span style="color: #339933;">,</span> thread<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #003366; font-weight: bold;">var</span> parts <span style="color: #339933;">=</span> page.<span style="color: #660066;">split</span><span style="color: #009900;">&#40;</span>RePostSep<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #003366; font-weight: bold;">var</span> index <span style="color: #339933;">=</span> parts.<span style="color: #660066;">length</span><span style="color: #339933;">-</span><span style="color: #CC0000;">1</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #003366; font-weight: bold;">var</span> lastPost <span style="color: #339933;">=</span> parsePost<span style="color: #009900;">&#40;</span>parts<span style="color: #009900;">&#91;</span>index<span style="color: #339933;">--</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>DebugEnable<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        notifyPosts<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#91;</span>lastPost<span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span> thread<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #003366; font-weight: bold;">var</span> posts <span style="color: #339933;">=</span> <span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
    <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>thread.<span style="color: #660066;">newLastFloor</span> <span style="color: #339933;">==</span> <span style="color: #339933;">-</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        thread.<span style="color: #660066;">newLastFloor</span> <span style="color: #339933;">=</span> lastPost.<span style="color: #660066;">floor</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>thread.<span style="color: #660066;">lastFloor</span> <span style="color: #339933;">==</span> <span style="color: #339933;">-</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        thread.<span style="color: #660066;">lastFloor</span> <span style="color: #339933;">=</span> thread.<span style="color: #660066;">newLastFloor</span><span style="color: #339933;">;</span>
        thread.<span style="color: #660066;">newLastFloor</span> <span style="color: #339933;">=</span> <span style="color: #339933;">-</span><span style="color: #CC0000;">1</span><span style="color: #339933;">;</span>
        setTimeout<span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>loopWatchThread<span style="color: #009900;">&#40;</span>thread<span style="color: #339933;">,</span> MaxPage<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> WatchInterval<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #000066; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>lastPost.<span style="color: #660066;">floor</span> <span style="color: #339933;">&gt;</span> thread.<span style="color: #660066;">lastFloor</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            posts.<span style="color: #660066;">push</span><span style="color: #009900;">&#40;</span>lastPost<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
            <span style="color: #000066; font-weight: bold;">while</span> <span style="color: #009900;">&#40;</span>index <span style="color: #339933;">&gt;</span> <span style="color: #CC0000;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                post <span style="color: #339933;">=</span> parsePost<span style="color: #009900;">&#40;</span>parts<span style="color: #009900;">&#91;</span>index<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
                <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>post.<span style="color: #660066;">floor</span> <span style="color: #339933;">&gt;</span> thread.<span style="color: #660066;">lastFloor</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                    posts.<span style="color: #660066;">push</span><span style="color: #009900;">&#40;</span>post<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
                <span style="color: #009900;">&#125;</span> <span style="color: #000066; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
                    <span style="color: #000066; font-weight: bold;">break</span><span style="color: #339933;">;</span>
                <span style="color: #009900;">&#125;</span>
&nbsp;
                index <span style="color: #339933;">-=</span> <span style="color: #CC0000;">1</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
            notifyPosts<span style="color: #009900;">&#40;</span>posts<span style="color: #339933;">,</span> thread<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
            <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>index <span style="color: #339933;">&lt;</span> <span style="color: #CC0000;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                <span style="color: #006600; font-style: italic;">// need check more pages</span>
                <span style="color: #003366; font-weight: bold;">var</span> prevPage <span style="color: #339933;">=</span> Math.<span style="color: #660066;">floor</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>posts<span style="color: #009900;">&#91;</span>posts.<span style="color: #660066;">length</span><span style="color: #339933;">-</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#93;</span>.<span style="color: #660066;">floor</span><span style="color: #339933;">-</span><span style="color: #CC0000;">1</span> <span style="color: #339933;">+</span> <span style="color: #CC0000;">9</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">/</span><span style="color: #CC0000;">10</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
                loopWatchThread<span style="color: #009900;">&#40;</span>thread<span style="color: #339933;">,</span> prevPage<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span> <span style="color: #000066; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
                thread.<span style="color: #660066;">lastFloor</span> <span style="color: #339933;">=</span> thread.<span style="color: #660066;">newLastFloor</span><span style="color: #339933;">;</span>
                thread.<span style="color: #660066;">newLastFloor</span> <span style="color: #339933;">=</span> <span style="color: #339933;">-</span><span style="color: #CC0000;">1</span><span style="color: #339933;">;</span>
                setTimeout<span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>loopWatchThread<span style="color: #009900;">&#40;</span>thread<span style="color: #339933;">,</span> MaxPage<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> WatchInterval<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
        <span style="color: #009900;">&#125;</span> <span style="color: #000066; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
            thread.<span style="color: #660066;">lastFloor</span> <span style="color: #339933;">=</span> thread.<span style="color: #660066;">newLastFloor</span><span style="color: #339933;">;</span>
            thread.<span style="color: #660066;">newLastFloor</span> <span style="color: #339933;">=</span> <span style="color: #339933;">-</span><span style="color: #CC0000;">1</span><span style="color: #339933;">;</span>
            setTimeout<span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>loopWatchThread<span style="color: #009900;">&#40;</span>thread<span style="color: #339933;">,</span> MaxPage<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> WatchInterval<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>由于要处理向前翻页的情况，所以有点复杂。简单来说就是检查最后帖子的最后一页，然后从最后一个帖子开始向前遍历，直到碰到目前已经检查过的帖子数为止。检查完一遍之后通过 setTimeout 让 loopWatchThread 在 WatchInterval 时间之后自动再被调用，也就实现了定时监视了。</p>
<p>整个框架大概就是这个样子，其实可以看到这可以比较容易扩展为一个更有趣的 notification 系统，就是对于新贴的通知，可以在内部做一些诸如插件之类的，比如，每次检测到 quark 发的贴，就回一个“quark 是坏人”；或者每次看到前方有整，就奋力向前之类的；或者可以做一些自动化一点的东西，类似于机器人，比如看到有人询问问题，就去网上搜索答案，并给出结果（GM 里允许跨 domain 做 AJAX Request 的）之类的。</p>
<p>另外，其实如果 Firefox 自身提供了比较好的 notification 接口的话（在有新扩展更新等时候它会弹出 notification ，不过那个看起来比较笨，而且不知道有没有接口可以调用），完全可以脱离 Growl 。目前的效果嘛，如本文一开始的截图中所示，Growl 可以有主题选择，自带的主题似乎就这个比较好看一点。感觉还不错，这样就不用老是等 98 缓慢地刷新了，看到有感兴趣的帖子就过去回复一下。</p>
<p>不过，Growl for Windows 也有诸多不和谐的地方，比如它的缩写太恶心（痛恨啊！今天科学社会主义考试，我还忍不住痛斥了一下这个东西 -.-），还有 notification 弹出的位置不能设定，并且占用了 Alt+X 键（这还让我怎么用 Emacs ？虽然在 Windows 下用 Emacs 几乎主要就用来写 LaTeX 了），且不能设置为其他键或者禁用，总的来说可定制性约等于 0 （我听说 Mac 下的软件几乎都是这样的？ <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/emoticon_waii.png' alt='8-O' class='wp-smiley' /> ）。</p>
<p>不过，总的来说，还是凑合。完整的代码可以从<a href="/wp-content/uploads/2009/11/cc98_notify.user.js">这里下载</a>。 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/happy.png' alt=':)' class='wp-smiley' /> 还有就是，GM 脚本调试起来还真是痛苦啊……</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.pluskid.org/?feed=rss2&amp;p=415</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>为 Python 的 HTTP 客户端加入自定义的 Cookie</title>
		<link>http://blog.pluskid.org/?p=414</link>
		<comments>http://blog.pluskid.org/?p=414#comments</comments>
		<pubDate>Fri, 13 Nov 2009 11:10:21 +0000</pubDate>
		<dc:creator>pluskid</dc:creator>
				<category><![CDATA[Develop]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://blog.pluskid.org/?p=414</guid>
		<description><![CDATA[<p>几乎所有脚本语言都提供了方便的 HTTP 客户端处理的功能，Python 也不例外，使用 urllib 和 urllib2 可以很方便地进行 HTTP GET 和 POST 等各种操作。并且还允许以类似于插件的形式加入一些 handler ，来定制 request 和 response ，比如代理的支持和 cookie 的支持都是这样添加进来的。具体来说，通过如下方式构造一个 opener ：</p>

opener = urllib2.build_opener&#40;urllib2.HTTPCookieProcessor&#40;&#41;&#41;

<p>然后这个 opener 就可以处理 cookie 了，相当方便，并且可定制性也……好吧，总之，现在我希望能在客户端手动插入一些 cookie 值，但是不管是 HTTPCookieProcessor 还是 cookielib 里的 CookieJar 都没有提供类似的方法可以来实现。</p>
<p>看起来，也并不是我一个人有这样的需求，因为我在查找解决方案的时候，还找到了有人给 Python 提交的这个 Patch，就是添加这个功能。不过看起来好像还没有被 accept 的样子，这样对标准库做暴力 patch 的方式可移植性似乎也不好。所以我还是另外找了解决方案，其实也很简单：看了 HTTPCookieProcessor 的实现代码之后，发现我可以做类似的事情，也就是在写一个 handler ，把我想要的 cookie 值强制放到 request 对象的 header 中去。</p>
<p>于是我查了 Python [...]]]></description>
			<content:encoded><![CDATA[<p>几乎所有脚本语言都提供了方便的 HTTP 客户端处理的功能，Python 也不例外，使用 urllib 和 urllib2 可以很方便地进行 HTTP GET 和 POST 等各种操作。并且还允许以类似于插件的形式加入一些 handler ，来定制 request 和 response ，比如代理的支持和 cookie 的支持都是这样添加进来的。具体来说，通过如下方式构造一个 opener ：</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">opener = <span style="color: #dc143c;">urllib2</span>.<span style="color: black;">build_opener</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">urllib2</span>.<span style="color: black;">HTTPCookieProcessor</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>然后这个 opener 就可以处理 cookie 了，相当方便，并且可定制性也……好吧，总之，现在我希望能在客户端手动插入一些 cookie 值，但是不管是 HTTPCookieProcessor 还是 cookielib 里的 CookieJar 都没有提供类似的方法可以来实现。</p>
<p><span id="more-414"></span>看起来，也并不是我一个人有这样的需求，因为我在查找解决方案的时候，还找到了有人给 Python 提交的<a href="http://bugs.python.org/issue6588">这个 Patch</a>，就是添加这个功能。不过看起来好像还没有被 accept 的样子，这样对标准库做暴力 patch 的方式可移植性似乎也不好。所以我还是另外找了解决方案，其实也很简单：看了 HTTPCookieProcessor 的实现代码之后，发现我可以做类似的事情，也就是在写一个 handler ，把我想要的 cookie 值强制放到 request 对象的 header 中去。</p>
<p>于是我查了 Python 的文档，对于 handler 的接口好像几乎没有描述，于是我就照着 HTTPCookieProcessor 来写了。这个 handler 应该放在正常的 cookie 处理 handler 的后面，然后检查已经存在的 cookie header ，再进行合并一下。不过比较诡异的是在 Python 的文档里并没有找到 Request 对象有 get_header 之类的方法可以得到已经存在的 header 项的值，觉得很诡异，于是直接查了源代码，才找到了，确实有这个方法。之前有听人说过 Ruby 的文档做得如何如何的烂，Python 的文档做得如何如何的好，我虽然没觉得 Ruby 的文档很烂，但是也觉得 Python 的文档确实不错，我最喜欢它末尾的 Examples 。两个文档系统倒是走的不同的路，Ruby 的文档是从代码中抽取（特定格式的）注释来自动生成的，类似于 javadoc ；而 Python 现在用的是独立于源代码的文档系统，人工写的，不过到头来居然连函数都漏掉了，课件人工维护文档的弊端还是很明显的。其实我见过的文档系统，最好用的应该还是属于 Emacs/Elisp 了吧。 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/happy.png' alt=':)' class='wp-smiley' /> 不过，废话少讲，handler 如下：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> SimpleCookieHandler<span style="color: black;">&#40;</span><span style="color: #dc143c;">urllib2</span>.<span style="color: black;">BaseHandler</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> http_request<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, req<span style="color: black;">&#41;</span>:
        simple_cookie = <span style="color: #483d8b;">'cc98Simple=1'</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> req.<span style="color: black;">has_header</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Cookie'</span><span style="color: black;">&#41;</span>:
            req.<span style="color: black;">add_unredirected_header</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Cookie'</span>, simple_cookie<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">else</span>:
            cookie = req.<span style="color: black;">get_header</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Cookie'</span><span style="color: black;">&#41;</span>
            req.<span style="color: black;">add_unredirected_header</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Cookie'</span>, simple_cookie + <span style="color: #483d8b;">'; '</span> + cookie<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> req</pre></td></tr></table></div>

<p>然后，构造 opener 的时候加上这个 handler 就可以了：</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">opener = <span style="color: #dc143c;">urllib2</span>.<span style="color: black;">build_opener</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">urllib2</span>.<span style="color: black;">HTTPCookieProcessor</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>, SimpleCookieHandler<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>但总归是一个 workaround ，期待那个 patch 被加入到标准库中吧。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.pluskid.org/?feed=rss2&amp;p=414</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CPU 风扇的问题</title>
		<link>http://blog.pluskid.org/?p=410</link>
		<comments>http://blog.pluskid.org/?p=410#comments</comments>
		<pubDate>Wed, 11 Nov 2009 03:47:52 +0000</pubDate>
		<dc:creator>pluskid</dc:creator>
				<category><![CDATA[Bugs]]></category>
		<category><![CDATA[Bug]]></category>
		<category><![CDATA[Fun]]></category>

		<guid isPermaLink="false">http://blog.pluskid.org/?p=410</guid>
		<description><![CDATA[<p>在上次讲调试的小课堂里我曾经提到过我前一阵子遇到过一个 Matlab 的问题，具体症状是，开启 Matlab 来做计算，过了大约一天以后，再去看，就会发现系统处于完全无响应的状态，完全挂掉了，键盘有一个灯在闪烁。</p>
<p>碰到了好几次之后我相当崩溃，这个 bug 几乎处于不可调试的状态，首先要重现 bug 就很麻烦，虽然几乎都能重新，但是我不可能一直坐在那里盯着屏幕等一天（具体需要几个小时来重现我也不太清除），而且 bug 出现之后系统会挂掉，唯一的办法是强制重启，这个时候所有现场都被销毁了。另一方面，阅读代码的必杀技也没法用，因为我的程序是一段不长的 Matlab 代码，在这样的脚本语言里，内存都是自动管理的，我能想到的危害最大的操作大概就是申请超大内存了，一般会有两个结局：如果 swap 空间还能承受得了的话，会使用 swap 空间，并导致系统奇慢无比，不过并没有挂掉；如果 swap 空间都不够用了，那么 Linux 系统会把这个进程 kill 掉，而不是同归于尽。总的来说，不太可能是这段 Matlab 脚本导致了这么严重的问题，如果真有问题的话，估计是出在 Matlab 了，并且还是非常强悍的 bug ，会让系统彻底挂掉。</p>
<p>最近我终于发现了问题的原因（应该是这个原因吧），之所以发现应该是完全处于运气好，或者说之所以一直没有发现真正原因也是由于运气不好吧。事实证明，我在小册子里列的 7 Golden Rules of Debugging 里的第四条“检查电源是否插好”确实也是非常重要的。因为这里的问题就在于这样的和代码本身并无关系的外部原因——在大多数情况下都不会出现的异常外部情况（比如电源没有插上）。</p>
<p>我这里的问题是 CPU 风扇挂掉了。所以计算量过大的时候 CPU 过热，导致系统挂掉了。我之所以发现是因为那天我碰巧目睹了这个 bug 发生，于是我重启了电脑，结果屏幕上不断闪现 CPU 过热的错误信息，我打开机箱一看，才发现风扇不转了。而以前之所以没有发现，是因为我都是把程序启动起来，过了很久过来看，系统挂掉了，这个时候通常是已经挂掉很久了，CPU 也已经冷却了，再重启的时候就没有出现错误了。 :-/</p>
<p>解决问题的办法嘛，把灰尘清理了一下，然后暴力把风扇脖子使劲拧了几下，它终于吭哧吭哧地开始转了，不过看起来总是很吃力的样子，虽然冬天到了，不过估计还是应该抽空换一个了。</p>
<p>其实这个问题我应该很早就发现才对！因为最近一段时间感觉那个电脑的发热量变得很大，坐在旁边感觉比以前热很多，但是似乎这样一个信息被我自动无视了。看来，真的是要对生活中出现的异常情况保持敏锐的嗅觉才行啊，不能太迟钝了。  </p>
]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/2009/11/tuxfan.png" alt="tuxfan" title="tuxfan" width="160" height="160" class="alignright size-full wp-image-413" />在上次<a href="/?p=403">讲调试的小课堂</a>里我曾经提到过我前一阵子遇到过一个 Matlab 的问题，具体症状是，开启 Matlab 来做计算，过了大约一天以后，再去看，就会发现系统处于完全无响应的状态，完全挂掉了，键盘有一个灯在闪烁。</p>
<p>碰到了好几次之后我相当崩溃，这个 bug 几乎处于不可调试的状态，首先要重现 bug 就很麻烦，虽然几乎都能重新，但是我不可能一直坐在那里盯着屏幕等一天（具体需要几个小时来重现我也不太清除），而且 bug 出现之后系统会挂掉，唯一的办法是强制重启，这个时候所有现场都被销毁了。另一方面，阅读代码的必杀技也没法用，因为我的程序是一段不长的 Matlab 代码，在这样的脚本语言里，内存都是自动管理的，我能想到的危害最大的操作大概就是申请超大内存了，一般会有两个结局：如果 swap 空间还能承受得了的话，会使用 swap 空间，并导致系统奇慢无比，不过并没有挂掉；如果 swap 空间都不够用了，那么 Linux 系统会把这个进程 kill 掉，而不是同归于尽。总的来说，不太可能是这段 Matlab 脚本导致了这么严重的问题，如果真有问题的话，估计是出在 Matlab 了，并且还是非常强悍的 bug ，会让系统彻底挂掉。</p>
<p><span id="more-410"></span>最近我终于发现了问题的原因（应该是这个原因吧），之所以发现应该是完全处于运气好，或者说之所以一直没有发现真正原因也是由于运气不好吧。事实证明，我在小册子里列的 7 Golden Rules of Debugging 里的第四条“检查电源是否插好”确实也是非常重要的。因为这里的问题就在于这样的和代码本身并无关系的外部原因——在大多数情况下都不会出现的异常外部情况（比如电源没有插上）。</p>
<p>我这里的问题是 CPU 风扇挂掉了。所以计算量过大的时候 CPU 过热，导致系统挂掉了。我之所以发现是因为那天我碰巧目睹了这个 bug 发生，于是我重启了电脑，结果屏幕上不断闪现 CPU 过热的错误信息，我打开机箱一看，才发现风扇不转了。而以前之所以没有发现，是因为我都是把程序启动起来，过了很久过来看，系统挂掉了，这个时候通常是已经挂掉很久了，CPU 也已经冷却了，再重启的时候就没有出现错误了。 :-/</p>
<p>解决问题的办法嘛，把灰尘清理了一下，然后暴力把风扇脖子使劲拧了几下，它终于吭哧吭哧地开始转了，不过看起来总是很吃力的样子，虽然冬天到了，不过估计还是应该抽空换一个了。</p>
<p>其实这个问题我应该很早就发现才对！因为最近一段时间感觉那个电脑的发热量变得很大，坐在旁边感觉比以前热很多，但是似乎这样一个信息被我自动无视了。看来，真的是要对生活中出现的异常情况保持敏锐的嗅觉才行啊，不能太迟钝了。 <img src='http://blog.pluskid.org/wp-content/plugins/smilies-themer/adiumicons/biggrin.png' alt=':D' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://blog.pluskid.org/?feed=rss2&amp;p=410</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>
