我是怎么用跳表优化搜索引擎的?

前言

对于跳表,我想大家都不陌生吧,这里不多解释,感兴趣的小伙伴可以看我的这篇文章:http://www.cnblogs.com/haolujun/archive/2012/12/24/2830683.html。 这段时间在做我们拍搜的优化,今天我就讲讲我是如何用跳表优化检索系统的。

搜索引擎的夹角余弦计算

都知道,搜索引擎利用夹角余弦计算query与文档的相似度,感兴趣的小伙伴可以看我的这篇文章:http://www.cnblogs.com/haolujun/archive/2013/01/08/2847503.html, 这里面需要计算两个向量的余弦值。

假设查询向量为:$ Q = [q_{1},q_{2},......,q_{n}] $
假设文档向量为:$ D = [d_{1},d_{2},......,d_{n}] $

query与文档的相似度为:$ sim(Q,D) = \frac{QD}{|Q||D|} $ 。这里面需要Q和D模长相乘做分母,对应分量相乘之和做分子。

模长的计算

对于query可以在每次查询之前做统一的预处理,在预处理过程中计算模长;对于文档,不能每次查询都计算一遍模长,这样效率很低,可以事先在建立索引的时候计算模长并保存。

向量相乘

在搜索引擎检索过程中,首先需要对query进行分词并得到查询向量,之后我们用分出的词从倒排表中拉取文档,不熟悉倒排表的小伙伴可以看这篇文章:http://www.cnblogs.com/haolujun/archive/2013/01/06/2847510.html。 通过倒排拉取出来的只是文档的ID,而文档本身包含的内容其实是以正排的方式存储的:即key=文档ID,$value=(word_{1}, word_{2}, ....word_{n}) $,实际上为了节省存储空间,正排只存储该文档中出现过的词。而今天的向量相乘就是利用正排与query进行两个集合的求交计算(向量相乘只对同时出现在query以及文档中的词进行计算,其它的都计为0),那么这就引出一个新问题,如何求两个集合的交集呢?聪明的小伙伴肯定想到了解决办法:对Q和D分别按照字典序排序,之后求两个有序列表的交集可以在线性时间内完成,示例代码如下:

int i = 0, j = 0;
double sum = 0.0;
while(i < len_q && j < len_doc) {
    if(q[i] < d[j]) {
        i++;
    } else if(q[i] > d[j]) {
        j++;
    } else {
        sum += sim(q[i], d[j]);
    }
}

但是,还能再优化这个代码么?答案是肯定的,那就是利用跳表。

对于搜索引擎来说,通常query较短,而文档较长,我们估计排完序的文档序列中会有一大段一大段的词都不在query中,可以直接跳过这些段而不用一一遍历,这就启发我们可以用跳表进行加速,优化代码如下:

double sum = 0.0;
int step = ceil(sqrt(len_doc)), i = 0, j = 0;
while(i < len_q && j < len_doc) {
   if(q[i] < d[j]) {
      int k = (j - step + 1) < 0 ? 0 : j - step + 1;
      while(k <= j && d[k] < q[i]) k++;
      if(d[k] == q[i]) {
        sum += sim(i, j); j = k + 1;  i++;
      } else {
        j = k; i++;
      }
    } else if(q[i] > d[j]) {
      j = j + step < len_doc ? j + step : j + 1;
    } else {
      sum += sim(i, j);
      ++i;
      j = j + step < len_doc ? j + step : j + 1;
    }
  }
}

我们在这里选择步长为 $ \sqrt[]{len_{doc}} $只是表示一种理论指导,实际中还需要不断测试不同的步长从而找到实际最优解。

当然,优化这个问题并不一定用跳表,如果内存足够大,我们可以为每个文档建立哈希或者map,这样只需用O(1)或者 O(log(len_doc))时间判断文档中是否包含一个词。

总结

利用简单的跳表,加起来不过几十行代码,直接使检索的效率提高一倍,优化了50%的硬件成本,可以高高兴兴领取年终奖回家过大年了。 5年前我就研究了跳表,没想到5年后的今天我竟然用到了它,所以没事多看看感兴趣的技术、研究一下感兴趣的问题对你的将来只有利没有弊。

时间: 2024-11-08 01:29:25

我是怎么用跳表优化搜索引擎的?的相关文章

【搜索引擎(二)】索引、倒排索引、哈希表、跳表

索引 其实在计算机中我们早已接触过跟索引有关的东西,比如数据库里的索引(index),还有硬盘文件系统中其实也有类似的东西,简而言之,索引是一种为了方便找到自己需要的东西而设计出来的条目,你可以通过找索引找到自己想要内容的位置.索引过程是: 关键字->索引->文档.在图书馆内的书分门别类,就是一种按类别来分的索引.当然索引还有很多其他的实现. 仅仅有索引的概念是不够的.虽然分门别类是一种方法,但是我们在拥有一堆文档的时候必须要有从文档到索引的规范过程,并且索引的结构要满足能够让人(或者计算机)

倒排索引优化 - 跳表

在前面一篇介绍 倒排索引 的文章中我们知道, 两个关键字的合并操作的时候复杂度是 θ(N), 如果在合并操作时遇到最极端的情况, 所扫描和比较的次数是两个列表集合的所有元素个数之和, 即是线性增长的, 这在数据量特别大的时候是很低效的. 我们还是看一下两个集合的合并操作代码示例: a = [1, 2, 3, 6, 9, 11, 45, 67] b = [4, 6, 13, 45, 69, 98] i = j = 0 result = [] while i < len(a) and j < le

SkipList 跳表

为什么选择跳表 目前经常使用的平衡数据结构有:B树,红黑树,AVL树,Splay Tree, Treep等. 想象一下,给你一张草稿纸,一只笔,一个编辑器,你能立即实现一颗红黑树,或者AVL树 出来吗? 很难吧,这需要时间,要考虑很多细节,要参考一堆算法与数据结构之类的树, 还要参考网上的代码,相当麻烦. 用跳表吧,跳表是一种随机化的数据结构,目前开源软件 Redis 和 LevelDB 都有用到它, 它的效率和红黑树以及 AVL 树不相上下,但跳表的原理相当简单,只要你能熟练操作链表, 就能轻

红黑树、B(+)树、跳表、AVL等数据结构,应用场景及分析,以及一些英文缩写

在网上学习了一些材料. 这一篇:https://www.zhihu.com/question/30527705 AVL树:最早的平衡二叉树之一.应用相对其他数据结构比较少.windows对进程地址空间的管理用到了AVL树 红黑树:平衡二叉树,广泛用在C++的STL中.map和set都是用红黑树实现的.我们熟悉的STL的map容器底层是RBtree,当然指的不是unordered_map,后者是hash. B/B+树用在磁盘文件组织 数据索引和数据库索引 Trie树 字典树,用在统计和排序大量字符

C语言跳表(skiplist)实现

一.简介 跳表(skiplist)是一个非常优秀的数据结构,实现简单,插入.删除.查找的复杂度均为O(logN).LevelDB的核心数据结构是用跳表实现的,redis的sorted set数据结构也是有跳表实现的.代码在这里:http://flyingsnail.blog.51cto.com/5341669/1020034 二.跳表图解 考虑一个有序表: 从该有序表中搜索元素 < 23, 43, 59 > ,需要比较的次数分别为 < 2, 4, 6 >,总共比较的次数 为 2 +

HBase内存结构之跳表数据结构浅析

最近学习HBase源码时发现HRegion在sotre管理上用到了跳表数据结构ConcurrentSkipListMap: ConcurrentSkipListMap有几个ConcurrentHashMap不能比拟优点: 1.ConcurrentSkipListMap的key是有序的. 2.ConcurrentSkipListMap支持更高的并发. ConcurrentSkipListMap的存取时间是log(N),和线程数几乎无关. 也就是说在数据量一定的情况下,并发的线程越多,Concurr

K:跳表

??跳表(SkipList)是一种随机化的数据结构,目前在redis和leveldb中都有用到它,它的效率和红黑树以及 AVL 树不相上下,但跳表的原理相当简单,只要你能熟练操作链表, 就能轻松实现一个 SkipList. 考虑一个有序表: 从该有序表中搜索元素 < 23, 43, 59 >,需要比较的次数分别为 < 2, 4, 6 >,总共比较的次数为 2 + 4 + 6 = 12 次. 有没有优化的算法吗?链表是有序的,但不能使用二分查找.类似二叉搜索树,我们把一些节点提取出来

SkipList跳表基本原理

为什么选择跳表 目前经常使用的平衡数据结构有:B树,红黑树,AVL树,Splay Tree, Treep等. 想象一下,给你一张草稿纸,一只笔,一个编辑器,你能立即实现一颗红黑树,或者AVL树 出来吗? 很难吧,这需要时间,要考虑很多细节,要参考一堆算法与数据结构之类的树, 还要参考网上的代码,相当麻烦. 用跳表吧,跳表是一种随机化的数据结构,目前开源软件 Redis 和 LevelDB 都有用到它, 它的效率和红黑树以及 AVL 树不相上下,但跳表的原理相当简单,只要你能熟练操作链表, 就能轻

skiplist(跳表)的原理及JAVA实现

前记 最近在看Redis,之间就尝试用sortedSet用在实现排行榜的项目,那么sortedSet底层是什么结构呢? "Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单.”   那么什么是SkipList跳表呢?下面我们从理解它的思想到实现及应用去做一个大致