过去我做的课题是蛋白质折叠,其中用到一个算法,前不久在做一个论文库的tag系统的时候意识到这两者其实是同一个问题,于是就把原来的优化算法改进了一下,不过想了一下,估计也没有什么系统会用吧,但是基于不想浪费的原则,就把这个优化写一下。9 Y& a, p/ {' f* I; V% n' L- F% I
& C+ C6 F ~! b1 M
这个算法是用来计算两个主题在tag上面的相似度。假如一个主题的tag的数目是有限的(用在论文库上,还有一个限制就是主题词数目是一样多的),那么我们可以找出所有和它有至少一个tag相同的主题,然后在里面找出tag相同数目最多的那个主题作为最相似主题。/ T d2 M( v. g) H" v; l
4 \1 _4 y A6 d0 Z D9 }- I然后,我们把二进制表示为这样一种排序,最低位是0110,0110,0110重复,第二位是0011,1100,0011,1100重复,第三位是0000,1111,1111,0000,重复,简单说就是呈现一种回文的结构。这种排序表示为数字,就是0,1,3,2,6,7,5,4的顺序……! l2 u. U# }7 S W) ]( t" m7 \4 [! K
0005 X3 H e- n7 J4 u+ a
0010 p9 I E* L# H8 J/ ?
011 0 |* b+ R2 S$ Y' F; p010 9 V9 H5 ]; T" r" P% b2 f1106 P( Q! Y& q1 B. Z' g
111% M Z+ r8 S( \0 s7 ^( n1 i
101# y, M! K+ _. `# `! ~- l" c, G
100 ; T {& B) `/ N; ?) h % d7 [. I# w6 J% x* H: n可以看到,任意相邻数字的二进制表示永远只相差一位,而且相邻的高位几乎总是相同的(用数学归纳法可以简单证明)。这就保证了,如果我找到一个优质解,那么很大概率在它的附近找到另外一个优质解(但只有一定概率,不是肯定保证)。我们把tag的二进制数表示按照这种方法排序后,我们就知道了所有相邻位置相同到第几位。一旦知道一个条目的二进制表示到倒数第四个字节得分1,那么肯定不会超过最高得分6,然后接下来所有和前一个位置的前四位相同的一系列条目都可以跳过。 由于每个条目和前一个条目相同到第几位是可以预先算好的,所以这个算法几乎不用存储中间结果。 5 C! E) y" @# C! _2 p, ^" j( [' `" g1 q' l% H: C8 n
其实不按回文排列排序也可以,但是在密集区域这样可能更好,特别是可以用普通的对半分解排序函数更有效地找到试探解,这使得新数据的插入,删除都可以使用传统技术而不必把效率下降太多。 q3 D" f1 o' k. [8 p! a4 V, [/ T5 ?& P/ R' h
这种表虽然效率不是最高,不过考虑到tag往往是幂数分形分布的(也就是最大类有1000个,次大类100个,次次大类10个),这种算法和二叉树只有常数倍的差距,即理论上是平均情况为O(log_2 N) 和O(N^[1/m],实际都退化到O(N^[1/m]。程序的复杂度更小,空间占用为O(1),而数据的插入,排序都可以用传统的线性排序技术,属于一种值得推荐的算法。 4 E' Y! O- l6 }! i% a; l/ {: e9 `0 u# c8 Y9 ~* @
$ p R8 r1 N, O* P2 R' l可惜的是,由于限定了条件是tag数有限,所以应用范围不广。这种算法不知道有没有人发明过(我觉得应该有,但是不会是这个领域),在基于关键词的倒排表里面可能有一些用处。 x1 ^ v* a1 E' b% c. {8 P3 z r. _2 D, ~3 ^0 U `