0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

列划分算法,使得这个本就很牛的算法性能直接提高一倍

算法与数据结构 来源:未知 作者:李倩 2018-09-23 14:36 次阅读

前言

我最近一直在公司做检索性能优化。当我看到这个算法之前,我也不认为我负责的检索系统性能还有改进的余地。但是这个算法确实太牛掰了,足足让服务性能提高50%,我不得不和大家分享一下。其实前一段时间的博客中也写到过这个算法,只是没有细讲,今天我准备把它单独拎出来,说道说道。说实话,本人数学功底一般,算法证明不是我强项,所以文中的证明只是我在论文作者的基础上加入了自己的思考方法,并且还没有完全证明出来,请大家见谅 ! 欢迎爱思考的小伙伴进行补充。我只要达到抛砖引玉的作用,就知足了。

回归正题,我们的检索服务中用到了最小编辑距离算法,这个算法本身是平方量级的时间复杂度,并且很少人在帖子中提到小于这个复杂度的算法。但是我无意中发现了另外一个更牛的算法:列划分算法,使得这个本就很牛的算法性能直接提高一倍。接下来进入正题。

列划分算法

这个算法比较难理解,出自如下论文:《Theoretical and empirical comparisons of approximate string matching algorithms》。In Proceedings of the 3rd Annual Symposium on Combinatorial Pattern Matching, number 664 in Lecture Notes in Computer Science, pages 175~184. Springer-Verlag, 1992。Author:WI Chang ,J Lampe。所以有必要先给大家普及一些共识。

编辑矩阵最小编辑距离在计算过程中使用动态规划算法计算的那个矩阵,了解这个算法的都懂,我不赘述。但是我们的编辑矩阵有个特点:第一行都是0,这么做的好处是:只要文本串T中的任意一个子序列与模式串P的编辑距离小于某个固定的数值,就会被发现。

给大伙一个样例,文本串T=annealing,模式串P=annual:

注意,第一行都是0,这是与传统最小编辑距离的最大区别,其余的动归方程完全相同。

对角线法则编辑矩阵沿着右下方对角线方向数值非递减,并且至多相差1。

行列法则每行每列相邻两个数至多相差1。

观察编辑距离矩阵,我们发现如下事实:每一列是由若干段连续数字组成。所以我们把编辑矩阵的每一列划分成若干连续序列,如下图所示:

红色框中就是一个一个的序列,序列内部连续。

序列-δ 定义对于编辑矩阵的每一个元素D[j][i] (j是行,i是列),若 j – D[j][i] = δ,我们就说D[j][i]属于i列上的 序列-δ,我们还观察到随着j增大,j – D[j][i]是非递减的。如下图所示:

序列-δ终止位置每个序列都会有起始和终止位置。序列-δ的终止位置为j,如果j是序列-δ的最小横坐标,并且满足D[j+1][i]属于序列-ε,并且ε>δ(即j+1-D[j+1][i]>δ)。

长度为0的序列我们发现如果按照如上定义,每一列上δ的值并不一定连续,总是或有或无的缺少一个数值。所以我们定义长度为0的序列:当D[j+1][i] < D[j][i]时,我们就在序列-δ和序列-(δ+2)之间人为插入一个长度为0的序列-(δ+1)。如下图所示:

所以,我们按照这个定义,就可以对编辑矩阵的每列进行一个划分,划分的每一段都是一串连续数字。

说了这么多,这个定义有什么用呢?假若,我们每次都能根据前一列的列划分情况直接推导出后一列的列划分情况,那么就可以省去好多计算,毕竟每一个划分中的每一段的数字都是连续的,这就暗示我们可以直接用一个常数时间的加法直接得到某一个编辑矩阵的元素值,而不用使用最小编辑距离的动态规划算法去计算。

接下来的重点来了,我们介绍这个推导公式,请打起十二分精神!我们按照序列-δ长度是否为0来介绍这个推论。由于其中一个推论文字描述太繁琐,不容易理解,所以我画了个图:

接下来烧脑开始。

推论1:如果列i上长度为0的 序列-δ 的结束位置为j,则列i+1上的 序列-δ 的结束位置为 j+1。

证明:由推论前提我们知道 δ = j – D[j][i] + 1 (想想前面说的δ值不连续,我们就人为插入一个中间值,只不过长度为0)。我们观察编辑矩阵就会发现如下两个事实:

事实1:D[j+1][i+1] = D[j][i] ( 别问为什么, 自己观察, 看看是不是都这样, 其实可以用反证法,我们就不证明了)。

事实2:D[j+2][i+1] <= D[j][i]。

通过事实1,我们知道D[j+1][i+1]确实属于 序列-δ,因为 j + 1 – D[j+1][i+1] = j + 1 – D[j][i] = δ。

通过事实2,我们知道列i+1上的序列δ,终止位置为j+1。

所以推论1证明结束。

推论2: 文字描述略,请看图

证明:

设这个序列长度为L,除了每列的第一个序列外,其余序列的其余位置均是当前的编辑距离小于等于该列上一个位置的编辑距离:即D[j-L+1][i]<=D[j-L][i],所以,我们可以推出:D[j-L+1][i] <= D[j-L][i];

再根据编辑矩阵对角线非递减我们知道,D[j-L+1][i+1] >= D[j-L][i];

综上两点我们得到如下大小关系:D[j-L+1][i+1] >= D[j-L+1][i]。

此外我们知道我们当前列的序列-δ截止位置为j,也意味着D[j+1][i] <= D[j][i],同样根据对角线法则,我们得出D[j+2][i+1] <= D[j+1][i] + 1 <= D[j][i] + 1。

接下来到了最精彩的一步,我们知道列i当前序列-δ内的值是连续的,如果起始编辑距离为A,那么终止编辑距离为A+L-1。

而由我们的推导可以发现:D[j-L+1][i+1] >= A,D[j+2][i+1] <= (A+L-1) + 1 = A+L,而之间跨越的长度为 (j+2)-(j-L+1)+1= L+2。 我们可以推出列i+1上从行j-L+1到行j+2之间的序列一定不连续,否则D[j+2][i+1] >= A+L+2-1= A+L+1,与我们先前的推导矛盾。所以,在j-L+1和j+2之间一定有一个列终止,这样才能消去一个序号。

此外我们还有一个疑问,列i+1上的序列-δ结束位置一定在j-L+1和j+1之间么?我们要证明这个事。

证明:

因为δ=j-D[j][i]=j-L+1-D[j-L+1][i]>=j-L+1-D[j-L+1][i+1],即列i+1上的 序列-δ的结束位置一定在j-L+1或者之后;

由于j+1-D[j+1][i]>δ,根据对角线法则D[j+2][i+1] <= D[j+1][i]+1,有j+2-D[j+2][i+1]>=j+2-(D[j+1][i]+1)=j+1-D[j+1][i] > δ, 固列i+1上的序列-δ的终止位置一定在j+2之前,即j-L+1到j+1之间。

后面推论2的分情况讨论,我一个也没证明出来,作者在论文中轻飘飘的一句话“后面很好证明,他就不去证明了”,但是却消耗了我所有脑细胞。所以,如果哪位小伙伴把推论2剩下的内容证明出来了,欢迎给我留言,我也学习学习。

这个算法的时间复杂度是多少呢?作者用启发式的方法证明了算法的复杂度约为$ O(mn/sqrt[2]{b}) $,其中b是字符集大小。

代码实现

接下来说一下代码实现,给出我总结出来的步骤,否则很容易踩坑。

编辑矩阵第一列,肯定只有一个序列。

每次遍历前一列的所有序列,根据推论1和推论2计算后一列的划分情况。

如果前一列遍历完毕,但是下一列还有剩余的元素没有划分。没关系,下一列剩下的元素都归为一个新的序列。

预处理一个表,表中记录T中的每个字符在P中的位置。可以直接用哈希算法(最好直接ascii码)进行定位,如果位置不唯一,可以拉链。进行列划分计算时,从前往后遍历那一链上的位置,直到找到第一个符合条件的,速度出奇的快。尽可能少使用或者不要使用map进行定位,测试发现相当慢。

接下来做最不愿意做的事:贴一个代码,很丑。

inlineintloc(intfind[][200],int*len,intch,intpos){for(inti=0;i< len[ch]; ++i) {    if(find[ch][i] >=pos)returnfind[ch][i];}return-1;}intnew_column_partition(char*p,char*t){intlen_p=strlen(p);intlen_t=strlen(t);intfind[26][200];intlen[26]={0};intpart[200];//记录每一个序列的结束位置//生成loc表,用来快速查询for(inti=0;i< len_p; ++i) {    find[p[i] - 'a'][len[p[i] - 'a']++] = i + 1;  }  int pre_cn = 0, next_cn = 1, min_v = len_p;  part[0] = len_p;  for(int i = 0; i < len_t; ++i) {    //前一列partition数    pre_cn = next_cn;    next_cn = 0;    int l = part[0] + 1;    int b = 1;    int e = l;    int tmp;    int tmp_value = 0;    int pre_v = part[0];    //前一列第0个partition长度肯定>=1if(len[t[i]-'a']>0&&(tmp=loc(find,len,t[i]-'a',b))!=-1&&tmp<= e) {      part[next_cn++] = tmp - 1;    } else if(pre_cn >=2&&part[1]-part[0]!=0){part[next_cn++]=part[0]+1;}else{part[next_cn++]=part[0];}//每列第一个partition尾值tmp_value=part[0];//遍历前一列剩下的partitionfor(intj=1;j< pre_cn && part[next_cn - 1] < len_p; ++j) {      int x = part[j], y = pre_v;      pre_v = part[j];      l = x - y;      if(l == 0) {        part[next_cn++] = x + 1;      } else {        b = x - l + 2;        e = x + 1;        if(b <= len_p && len[t[i] - 'a'] >0&&(tmp=loc(find,len,t[i]-'a',b))!=-1&&tmp<= e) {          part[next_cn++] = tmp - 1;        } else if(j + 1 < pre_cn && part[j + 1] - x != 0) {          part[next_cn++] = x + 1;        } else {          part[next_cn++] = x;        }      }      l = part[j] - part[j - 1];      if(l == 0) {        //新得到的partition长度为0,那么下一个partition的起始值比上一个partition尾值少1        tmp_value -= 1;      } else {        tmp_value += l - 1;      }    }    if(part[next_cn - 1] != len_p) {      part[next_cn++] = len_p;        tmp_value += len_p - part[next_cn - 2] - 1;      if(tmp_value < min_v) {        min_v = tmp_value;      }    } else {      min_v = min_v < tmp_value ? min_v : tmp_value;    }  }  return min_v;}

结语

这个算法应用到线上之后,效果非常明显,如下对比。

优化前CPU

优化后CPU:

能力有限,证明不充分,有兴趣的小伙伴可以直接去看原版论文,欢迎交流,共同进步。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 算法
    +关注

    关注

    23

    文章

    4453

    浏览量

    90746
  • 数值
    +关注

    关注

    0

    文章

    79

    浏览量

    14290

原文标题:死磕一周算法,我让服务性能提高 50%

文章出处:【微信号:TheAlgorithm,微信公众号:算法与数据结构】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    福音:日本电池新技术师电池能量密度提高一倍

    宣布,已经开发出新的锂电池技术,在电池尺寸不变的情况下,新技术能让电量增加一倍。据报道,日立采用种新开发的、基于硅的材料构成电池的负极,它可以将电池能量密度提高一倍。在2016年1月13日至15日
    发表于 01-05 15:35

    利用区域划分算法降低OSPF路由表计算开销

    本文在对OSPF 协议理解的基础上,阐述了利用OSPF 构造路由表的原理,提出了一种划分区域的新算法使得在链路状态发生改变时,可减少对路由表重新计算的开销。注:关键字:
    发表于 08-06 11:50 19次下载

    一种新的基于晶体管级的电路划分算法

    随着VLSI 电路规模的不断增加,为实现电路并行仿真所做的电路划分算法的质量显得日益重要。鉴于现有算法未能同时保证均衡的分块间规模和最少的互联信号数目,该文出了一种新
    发表于 02-10 12:05 19次下载

    SPIHT算法的医学图像无失真压缩分析编码步骤

      SPIHT算法   基于分层树的集合划分算法(Set Partitioning inHierarchical Trees,SPIHT)改进了内嵌零树编码算法(EZW)。在对图像进行小波变换后,它更有效地利用了不
    发表于 11-10 14:52 647次阅读
    SPIHT<b class='flag-5'>算法</b>的医学图像无失真压缩分析编码步骤

    基于主题与连接的局部社区划分算法_蔡国永

    基于主题与连接的局部社区划分算法_蔡国永
    发表于 01-08 10:47 9次下载

    基于PSO的粗颗粒度可重构处理器时域划分算法设计刘勰

    基于PSO的粗颗粒度可重构处理器时域划分算法设计_刘勰
    发表于 03-17 08:00 0次下载

    基于层次划分的密度优化聚类算法

    针对传统的聚类算法对数据集反复聚类,且在大型数据集上计算效率欠佳的问题,提出一种基于层次划分的最佳聚类数和初始聚类中心确定算法基于层次划分密度的聚类优化( CODHD)。该
    发表于 12-17 11:27 0次下载
    基于层次<b class='flag-5'>划分</b>的密度优化聚类<b class='flag-5'>算法</b>

    基于局部模块度的社团划分算法

    针对大多复杂网络社团划分算法不能快速发现最优节点加入社团的问题,提出一种利用节点亲密度的局部社团划分算法。引入节点亲密度的概念量化社团与邻居节点的关系,按照节点亲密度由大到小选择节点加入社团,最后
    发表于 12-19 19:02 0次下载

    ARM Cortex-R8使SoC的性能提高一倍

    总部位于剑桥的IP公司推出的ARM Cortex-R8处理器有望帮助芯片设计人员将基于ARM的调制解调器和大容量存储设备SoC的性能提高一倍
    的头像 发表于 08-07 15:48 3418次阅读

    明年Q2产能提高一倍,长鑫存储每月4万片

    该报道指出,目前长鑫存储已经完成了合肥Fab 1及研发设施建设,每月生产2万片,并计划在2020年第二季度将产能提高一倍,达到每月4万片。
    的头像 发表于 12-10 14:23 2688次阅读

    LG化学计划将中国的产量提高一倍以上

    据报道,消息人士称,LG化学计划明年将其为中国产特斯拉电动汽车生产的电池产能提高一倍以上,以满足增长的需求。
    的头像 发表于 12-02 09:50 1422次阅读

    基于狄利克雷问题的动态划分算法

    传统静态的路网控制子区划分算法难以适应复杂路网中交通流动态变化的特性。为此,基于狄利克雷问题提岀一种动态划分算法。根据密度峰值理论重新定义局部密度概念,用以识别控制子区的稳定块。在此基础上
    发表于 03-16 10:34 18次下载
    基于狄利克雷问题的动态<b class='flag-5'>划分算法</b>

    基于节点多属性相似性聚类的社团划分算法SM-CD

    针对当前社团划分算法存在划分方式单一和划分结果准确度低等问题,提出一种基于节点多属性相似性聚类的社团划分算法SM-CD。根据社会网络特性定义网络节点的结构属性与自身属性,通过调整两类属
    发表于 03-30 09:47 7次下载
    基于节点多属性相似性聚类的社团<b class='flag-5'>划分算法</b>SM-CD

    基于双曲网络空间嵌入与极小值聚类的社区划分算法

    真实复杂网络节点度分布服从幂律分布,而双曲空间能够完整表现这一特性。为此,提出一种基于双曲空间嵌入与极小值聚类的社区划分算法MHE。将建模后的复杂网络嵌入庞加莱圆盘模型,保留复杂网络的全局拓扑信息
    发表于 04-01 15:18 11次下载
    基于双曲网络空间嵌入与极小值聚类的社区<b class='flag-5'>划分算法</b>

    线性划分设计陷门算法和陷门S盒综述

    中唯一的非线性部件,在加密过程中起到混淆的作用,因此在构造分组算法的陷门时主要就是研究在S盒中植入陷门。针对这个问题,文中主要研究了基于陪集对有限域进行线性划分的代数性质来构造陷门S盒的方法,这种陷门S盒的陷门信息
    发表于 04-26 14:12 5次下载
    线性<b class='flag-5'>划分</b>设计陷门<b class='flag-5'>算法</b>和陷门S盒综述