在线时间 4 小时 最后登录 2015-5-5 注册时间 2015-4-8 听众数 10 收听数 0 能力 0 分 体力 104 点 威望 0 点 阅读权限 20 积分 47 相册 0 日志 0 记录 0 帖子 30 主题 12 精华 0 分享 0 好友 5
升级 44.21%
TA的每日心情 慵懒 2015-5-5 09:30
签到天数: 12 天
[LV.3]偶尔看看II
自我介绍 学生
过去我做的课题是蛋白质折叠,其中用到一个算法,前不久在做一个论文库的tag系统的时候意识到这两者其实是同一个问题,于是就把原来的优化算法改进了一下,不过想了一下,估计也没有什么系统会用吧,但是基于不想浪费的原则,就把这个优化写一下。 9 B [# j; ]- F2 S6 n5 |
6 e) p5 m4 Z+ F( ^5 _0 n
这个算法是用来计算两个主题在tag上面的相似度。假如一个主题的tag的数目是有限的(用在论文库上,还有一个限制就是主题词数目是一样多的),那么我们可以找出所有和它有至少一个tag相同的主题,然后在里面找出tag相同数目最多的那个主题作为最相似主题。 / S. [( P- m( _1 o4 W
3 G6 R: B1 q+ c$ Q/ [+ L 最简单的想法是做一个二叉树,第一级是有没有tag a,第二级是有没有tag b,但是这个和二叉树不同,二叉树的不同级优先级是不一样的,而这个问题里面优先级是一样的。 , |1 G* J9 T. C/ K7 |* r y6 [
然后的想法是仍然做一个二叉树,不过使用贪恋法找一个尝试解,然后其实根本不必算到最后就可以砍掉很多分支。因为假如最优解是5个相同,你现在得分只有1,然后下面只剩3层,可想最优情况也只能得到4,所以整个分支都可以砍掉。当找到更优解的时候就替换掉暂时的尝试最优解。
' v& P1 b1 ~% Y; p8 {8 |8 @
! K" D& \6 Y9 L2 N 这在速度上当然是最优解法,不过有没有空间上花费较少,而效率上下降不多的解法呢? , r4 T- P( E% m* j J9 @7 m- ^" o
我想了一下,使用这种方法: ) i8 U7 o$ l9 ]: V8 C# Z
把每种tag按照有无,把一个主题的tag编码成一个二进制串,第n位为1则表示拥有第n个tag。里面的1的数目是固定的。然后把两个主题的二进制表示进行按位and操作,结果字节用查表法就可以查出里面1的数目,最后相加,可以节省一点if操作。
9 ^ \+ v C' K" @* t( l0 C2 R
0 d1 N; d# Q3 O8 k+ w* ^& c7 B3 S 然后,我们把二进制表示为这样一种排序,最低位是0110,0110,0110重复,第二位是0011,1100,0011,1100重复,第三位是0000,1111,1111,0000,重复,简单说就是呈现一种回文的结构。这种排序表示为数字,就是0,1,3,2,6,7,5,4的顺序…… 4 `# x8 ~. R( J* D1 `' j
000
! t, I. g3 M( r _" ` 001 0 w+ g' l9 i7 H( L5 w7 }& f
011
, {; o/ m7 M, @5 F 010 5 I4 X4 e3 \* y
110
6 X( S* J5 T. c. i- ]5 [ 111
9 f% _( V+ h- ?+ y, e: i2 ` 101 7 q, y$ Q2 K, v
100 . q6 \+ f4 c0 ?+ R% |
' d3 r' x3 p/ x V( {. Q/ F* @6 D
可以看到,任意相邻数字的二进制表示永远只相差一位,而且相邻的高位几乎总是相同的(用数学归纳法可以简单证明)。这就保证了,如果我找到一个优质解,那么很大概率在它的附近找到另外一个优质解(但只有一定概率,不是肯定保证)。我们把tag的二进制数表示按照这种方法排序后,我们就知道了所有相邻位置相同到第几位。一旦知道一个条目的二进制表示到倒数第四个字节得分1,那么肯定不会超过最高得分6,然后接下来所有和前一个位置的前四位相同的一系列条目都可以跳过。 由于每个条目和前一个条目相同到第几位是可以预先算好的,所以这个算法几乎不用存储中间结果。 % q+ b) B( b3 b1 m; N3 r
6 T! |, `- q# [% ]8 f
其实不按回文排列排序也可以,但是在密集区域这样可能更好,特别是可以用普通的对半分解排序函数更有效地找到试探解,这使得新数据的插入,删除都可以使用传统技术而不必把效率下降太多。 9 A& P4 Z Y; \% z: T: ?# r
& d: [4 n9 e8 m3 K7 J- X 这种表虽然效率不是最高,不过考虑到tag往往是幂数分形分布的(也就是最大类有1000个,次大类100个,次次大类10个),这种算法和二叉树只有常数倍的差距,即理论上是平均情况为O(log_2 N) 和O(N^[1/m],实际都退化到O(N^[1/m]。程序的复杂度更小,空间占用为O(1),而数据的插入,排序都可以用传统的线性排序技术,属于一种值得推荐的算法。 / }+ W$ p$ k7 ~, d+ o7 O
: _- a Z) m t* k7 H, p & _/ L! l' L& [
可惜的是,由于限定了条件是tag数有限,所以应用范围不广。这种算法不知道有没有人发明过(我觉得应该有,但是不会是这个领域),在基于关键词的倒排表里面可能有一些用处。 9 ?* v8 X2 m! v) |: V/ R+ Y
' S$ R4 b. S3 e8 J. l! s
zan