• 词典(词汇表)


    [词典搜索的数据结构]

      本篇描述的倒排索引对普通倒排索引中的词典部分再进行了一层索引, 通过本章的倒排索引结构可以找到词项, 然后通过普通倒排索引最终定位到文档。词汇表的查找操作往往采用一种称为词典(dictionary)的经典数据结构,并且主要有两大类解决方案:哈希表方式和搜索树方式。在数据结构相关的文献中,词汇表中的每个条目(这里是词项)常常称为关键字或键(key)。

      哈希表方式已在某些搜索引擎中用于词典查找。这种方式下,每个词项通过哈希函数映射成一个整数,映射函数的目标空间需要足够大,以减少哈希结果冲突的可能性。当然,这种方式很难避免冲突的发生,此时需要精心维护一个辅助结构来解决冲突问题。查询时,对于每个查询项分别进行哈希操作,并解决存在的冲突,最后返回每个查询词项对应的倒排记录表的指针。由于查询词项稍有变化后哈希函数会生成截然不同的结果,哈希表方式难以处理查询词项存在轻微变形的情况(如单词resume的重音符和非重音符版本)。特别地,哈希表方式很难处理 前缀式查询,如查找以automat开始的词项所在的文档。最后需要指出的是,在词汇表不断增长的环境(如Web)中,为当前需求所设计的哈希函数可能会在几年内很快失效。

      搜索树方式能够解决上面提到的大部分问题。比如,它可以支持以automat开始的前缀式查询。为了减轻重新平衡化处理的开销,有一种方法允许内部节点的子树数目在某个固定区间内变化。词典搜索中普遍使用B-树就是这类方法的一个实例。B-树可以看成是将二叉树的多层“ 压平” 到一层而得到的树结构,在内存空间不足以存下全部词典而必须要将部分词典常驻磁盘时,这样做非常有效,因为在这种情况下“ 压平”可以在将词典调入内存时实现后续二值测试的预读取。此时,B-树中的整数 a 和 b 取决于磁盘块的大小。

    [通配符查询]

      通配符查询往往适用于如下任何一种场景:

      (1) 用户对查询的拼写不太确定(比如,如果不能确定是 Sydney 还是 Sidney,就采用通配;符查询 S*dney)

      (2) 用户知道某个查询词项可能有不同的拼写版本,并且要把包含这些版本的文档都找出来(比如 color 和 colour);

      (3) 用户查找某个查询词项的所有变形,这些变形可能还做了词干还原,但是用户并不知道;搜索引擎是否进行了词干还原(比如 judicial 和 judiciary,可采用通配符查询 judicia* )

      (4) 用户不确定一个外来词或者短语的正确拼写形式(如查询 Universit* Stuttgart)

      为通配符*在查询字符串末尾仅出现一次,所以一个诸如 mon* 的查询称为尾通配符查询(trailing wildcard query)。基于搜索树的词典结构对于处理尾通配符查询来说非常方便,比如对于查询 mon*,我们可以依次按照字符 m、o、n 从上到下遍历搜索树,直到能列举词典中所有以 mon 开头的词项集合 W 时为止。最后,在普通倒排索引中进行|W|次查找后取出 W 中所有词。

      上述做法中存在的一个明显的问题就是,如果通配符不出现在查询尾部应该如何处理?在首先对尾通配符查询做一个小小的推广。考虑首通配符查询(leading处理这种更一般的情况之前, wildcard query)或者说*mon 形式的查询。此时可以引入词典的反向 B-树(reverse B-tree)结构,在该结构中,原来 B-树中的每个从根到叶子路径所代表的词项全部反过来写。因此,词项 lemon在反向B—树中的路径就是 root-n-o-m-e-l。所以对反向 B-树遍历后可以返回所有包含同一后缀的词项。

      同时使用B-树和反向B-树,我们可以处理更一般的单通配符查询,比如se*mon。具体处理时,可以通过B-树来返回所有前缀为se且后缀非空的词项子集W,再通过反向B-树来返回所有后缀为mon且前缀非空的词项子集R。然后, 对W和R求交集W ∩ R,之后对交集进行扫描,当前缀和后缀相同时去掉它们对应的词项 1 (比如,查询ba*ba对应的集合R和W都包含词项ba,这种情况下应该过滤掉ba),得到结果集合。最后,通过普通倒排索引来获取结果集合中所有词项对应的文档。这样,我们就可以通过同时引入B-树和逆B-树的方式来处理只包含单个*号的查询。

    [轮排索引]

      一种专门用于一般通配符查询的索引是轮排索引(permuterm index), 它是倒排索引的一种特殊形式。首先,我们在字符集中引入一个新的符号$,用于标识词项结束。因此,词项hello在这里表示成扩展的词项hello$。然后,构建一个轮排索引,其中对扩展词项的每个旋转结果都构造一个指针来指向原始词项。图 3-3 给出了词项hello对应的轮排索引的例子。

      

      我们将词项旋转后得到的集合称为轮排词汇表(permuterm vocabulary)。那么,如何利用轮排索引来处理通配符查询呢?考虑通配符查询 m*n,这里的关键是将查询进行旋转让*号出现在字符串末尾,即得到 n$m* 。下一步,在轮排索引中查找该字符串(可通过搜索树方式查找),实际上等价于查找某些词项(如 man 和 moron)的旋转结果。既然我们可以使用轮排索引查找到匹配通配符的原始词典中的词项,那么我们就可以在普通倒排索引中查找这些词项,从而检索出匹配的文档。这样,我们就能够处理所有包含单个*号的通配符查询。
      
    如果查询中存在多个通配符(如 fi*mo*er),那么我们应该如何处理?在这种情况下,首先返回轮排索引中 er$fi*对应的词项集合,然后通过穷举法检查该集合中的每个元素,过滤掉其中不包含 mo 的词项。上例中,最终会选出 fishmonger,而过滤掉 filibuster。最后,再利用剩下的词项去查普通倒排索引,从而得到最后的结果。轮排索引的一个最大缺点是其词典会变得非常大,因为它保存了每个词项的所有旋转结果。我们注意到,B-树和轮排索引之间存在着密切的关联。实际上,有人建议上述轮排索引结构可以看成是一棵轮排 B-树。但是,为了突出轮排索引和 B-树的不同,这里使用了传统的术语。给定前缀时,利用轮排索引能够选择不同的旋转结果。

    [k-gram 索引]

       一个 k-gram 代表由k 个字符组成的序列。对于词项 castle 来说,cas、ast、stl 都是 3-gram。我们用一个特殊的字符$来标识词项的开始或者结束,因此对于 castle 来说,所有的 3-gram 包括$ca、cas、ast、stl、tle 及 le$。

      

      考虑查询 re*ve我们构造布尔查询 $re AND ve$,这个查询可以在 3-gram 索引中进行查找处理,返回诸如 relive、remove 及 retrieve 的词项,然后我们可以在普通倒排索引中查找这些返回的词项,从而得到与查询匹配的文档。使用k-gram索引时往往还需要进行进一步的处理。考虑 3-gram索引结构的情况,对于查询red*,按照上面的处理步骤我们就会将原始查询转换为布尔查询$re AND red,这时可能会返回诸如retired的词项,因为它同时包含$re和red,但这个结果显然并不满足原始的查询red*,也就是说采用k-gram索引会导致非预期的结果。为了解决这个问题,我们引入一个称为后过滤(postfiltering)的步骤,即利用原始的查询red*对上述布尔查询产生的结果进行逐一过滤。过滤时只需要做简单的字符串匹配操作即可。和前面一样,我们会在普通倒排索引中查找上述过滤得到的结果词项,从而得到最后的文档集合。

      即使没有通配符查询的布尔组合,单个通配符查询的处理也是非常耗时的,除了最后要在普通倒排索引中查找之外,还要在特定索引(如轮排索引或 k-gram 索引)中进行查找、在结果中进行过滤等操作。搜索引擎可以支持这些丰富的功能,但是搜索引擎通常将这些功能隐藏在一个大部分用户从不访问的界面(如“ 高级搜索” 界面)下。如果把这些功能暴露在一般搜索界面上,用户常常会受鼓励而使用这些功能,即便在他们不是特别需要的时候(比如,输入以a*开始的查询的前缀),这样就会大大增加搜索引擎的负担。

       

  • 相关阅读:
    WPF入门(一):简单的演示
    代码的演化DI(理解依赖注入di,控制反转ioc)
    WPF入门(三):简单绑定 绑定到页面元素
    WPF入门(四):简单绑定 静态资源绑定
    WPF入门(六)样式Style
    WPF入门(八)布局(layout) port 2
    js select onchange
    js this指向
    js 两个日期之间有多少个星期几
    js table的所有td 按行合并
  • 原文地址:https://www.cnblogs.com/tekkaman/p/3573858.html
Copyright © 2020-2023  润新知