关于数学

    最近一段时间开始看数学了,估计是这几年来第一次主动读数学的东西。不过其实也不是纯粹的数学,主要是结合了计算机的一些算法啊,解题的思考啊,等等。

    上个学期浅浅学过数据挖掘,特别是BP神经网络过后,突然又对算法这块感兴趣。这里也有求职的因素在内,毕竟很多工资高的企业,比如知名外企+百度都很看重这个。听说百度实习考试,考的基本上是算法,最简单似乎是快速排序的非递归算法,我没有细想,只想到一个类似递归的迭代算法,不知道认不认(刚才百度了下,结果基本上是用栈的,估计和我想得差不多吧)。Google的就不用说了,看到某Google牛人博客,说他应聘面试的时候,考官问了一题,他给了一个比较不普遍的解答,考官问为什么,答来自TAOCP第二卷,考官说这题就是考你有没有看过TAOCP的。TAOCP我虽然有3卷英文的,但实在是没有拜读的勇气,更没有精力,研究生阶段看完CLRS就很好了。

    一开始考试完在图书馆翻了翻Tom Mitchell的Machine Learning和Luger的AI,主要是一些启发式的算法和贪心算法(这两个的之间也有交叉),觉得似乎基础还要补一补,又想起了Jetty毕业前的建议,开始从Programming Pearls研究起,做习题,主要做一下算法的题目。

    接下来订阅了刘未鹏的博客谈论组,上面常有一些很好的文章和讨论。特别是最近的一篇。也第一次知道波利亚的超级经典:How to Solve It,国内也有译本,不过听说翻译的极差,不推荐。英文的有电子版,不过是DJVU的版本,书倒是不厚。这本书我还没看,是下一个读书计划。看在很多大牛推荐讨论的份上,也大大的推荐一把。

    昨天晚上书架上翻出“什么是数学”,去年在china-pub上跟风买的,本来以为是有点科技哲学和趣味的书,买来以后翻了翻,觉得倒有点像教材,于是束之高阁。昨天顿时觉得大错特错。

    前几天研究一个问题时碰到马尔可夫链,不懂,没学过,也挺惭愧的。想找概率的书,却发现不见了,很是懊恼,今天抢了大三小朋友的一本,知道了随机过程、马尔可夫是啥回事。

   讲了好多意识流,也困了,睡觉去。。。读书任重而道远,况且还要做项目。。。

    P.S. 驾照半个月前就考完并到手了,还是挺开心的,都很顺利。不过还要去交警九大队落户,从桥北起要走挺远的,好麻烦。。

java diff 及wiki相关

diff的原理在于找两个字符串之间的最大相同子串(Longest Common Subsequence)以及编辑距离,比较有名的实现是UnixLinux上常用的diff(GNU Diff)。

实现

Java里Diff的实现我找了一下,主要是两个,java-diff 和bsmi上的Diff ,前者为LGPL,后一个为GPL。其实代码也都不多,都实现了LCS算法。前一个协议上对我们比较有利,而且文档、测试和例子多一些。
JavaDiff里主要有两个类,Diff和Difference类。前者是算法,后者是差异的表示类。下面讲一下例子:

Object[] a = new Object[] {         "a",         "b",         "c",         "d",         "e"     };     Object[] b = new Object[] {         "a",         "x",         "y",         "b",         "c",         "j",         "e",     };     Difference[] expected = new Difference[] {         new Difference(1-1,  1,  2),         new Difference(3,  3,  5,  5),     };     Diff diff = new Diff(a, b);     List diffOut = diff.diff();

差别有三处,用两个Difference对象表示。一个Difference对象表示替换,增加,删除。Difference的构造函数:

public Difference(int delStart, int delEnd, int addStart, int addEnd)

如果delEnd或者addEnd为-1的话,就代表没有删除或者增加行为。
回到例子,两个字符串之间的差别在于,目标字符串在第1-2行(从0算起)增加了x,y,第3行的d被第5行的j替换。Difference虽然只说明了行号和动作,但我们可以推算出来增加了什么,删除了什么,替换了什么。下面是另一个更长的例子,来自测试用例:

public void testStrings1()     {         Object[] a = new Object[] {             "a",             "b",             "c",             "e",             "h",             "j",             "l",             "m",             "n",             "p"         };         Object[] b = new Object[] {             "b",             "c",             "d",             "e",             "f",             "j",             "k",             "l",             "m",             "r",             "s",             "t"         };         Difference[] expected = new Difference[] {             new Difference(0,  0,  0-1),             new Difference(3-1,  2,  2),             new Difference(4,  4,  4,  4),             new Difference(6-1,  6,  6),             new Difference(8,  9,  911),         };         runDiff(a, b, expected);     }

上面比较的都是一个个字符串的差异,推广一下,把每一行文本当作一个字母,就可以得到文件的差异。在java-diff的etc下有一个FileDiff.java,是一个很好的参考。得到之间的差异之后,我们要把这个差异表示出来,这个需要包装一下,不过难度不大。

版本保存

还有一个wiki版本的保存问题。大的维基引擎如MediaWiki(就是维基百科那个,顺便说一下,维基百科的英文版终于可以访问了)没时间研究,就是 JSPWiki也没来得及看)(JSPWiki连数据库也不用,Web用自己写的框架,可读性可能比较不行)。只研究了trac的wiki实现。trac 的wiki实现很简单,就是把每一个版本都保存在数据库,毕竟都是文本的,还可以接受。每次比较的时候就从数据库里取两个版本出来做一个diff,具体实 现在PYTHON/site-packages/trac/wiki/web_ui.py(_render_diff函数)。trac提供两种形式的 diff结果,一个是tabular的表格形式,就是很直观的对比,还有一个是Unified的形式,也就是经常看见的diff结果。这是通过页面上 javascript读table里的文字转换成Unified格式的diff文本,虽然个人不推荐这种方式。wiki的文本修改又有一个特点,就是每一 行其实内容可能比较多,只改了几个字,这样就要对这一行的两个版本再做一个diff,然后把删除的文本用<del>标签,增加的文本用 <ins>标签展示出来。
最后提一下JSR-170,一个用来管理仓库内容(主要是大型CMS)的API,支持版本控制,存储多元化,很复杂,有两个商业实现和一个Apache JackRabbit的开源实现,这里 是一个参考资料。JSR170也是里面的例子也是把每一个版本都存储下来。

参考资料