快速合并算法(补充):List Intersection

在之前的《Information Retrieval(信息检索)笔记01:Boolean Retrieval(布尔检索)》 中,我们已经对使用额外数据结构的快速合并算法:跳表指针 (Skip Pointer) 进行了介绍,这里我们会对这部分的内容进行一个补充。

在这里插入图片描述

我们已经知道,跳表指针是一个需要额外数据结构的算法,但这不仅仅意味着它需要额外的空间进行存储(主要是存储 Skip Pointer List)。当数据有更新的时候,比如添加了新的文档,那么 Inverted Index 也会需要更新,进而使得存储 Skip Pointers 的列表也需要进行更新。 所以很自然地我们会想,有没有一种不需要使用额外数据结构,也能够实现这种 SkipTo() 效果的方法呢?

首先,我们需要再清楚地理解一下,为何我们需要 SkipTo()。我们可以回顾一下,在之前的笔记中介绍的基于合并的取交集方式 (merge-based intersection),它的主要思想是不断使用 next() 去访问下一个对象,所以两个列表中的所有对象对会被访问,因此复杂度为 O(m + n)

在这里插入图片描述
显然,使用这种方法的时候,我们在第一个 posting list 中从 1 直到 53 前一个元素的所有访问都是可以省略的开销,我们希望能够直接 SkipTo(≥52) ,这样能够访问最少的元素。

一个比较朴素的想法是使用二分法搜索 (Binary Search),因为所有的 Posting List 都是排好序的,因此很适合使用这种方式,该算法的复杂度为 ⎡log2(Nremainder) ⎤(Nremainder用来表示 Posting List 中剩余的元素数量)。但这里仍存在一个问题,二分法搜索对于 目标位置 (Landing Position) 距离当前位置很远的时候非常实用,单若是目标距离当前点很近,使用顺序搜索的代价可能会更小。 比如现在指针指向 1,我们的目标可能是 4,而整个 Nremainder 有 1 Million,我们使用顺序访问的时候只需要访问 3 次,而用二分法搜索可能要用接近 20 次。所以我们希望能结合这两种方法。

Galloping Search (Gambler’s Strategy)

我们希望能够先确定目标位置 (Landing Position) 距离当前位置的距离,根据远近选择搜索方式。Galloping Search 算法分为两个阶段:

  • Stage 1:每次搜索时,让搜索范围翻倍,直到超过目标 (Overshoot)(注:这里不一定是 2 倍,1.5倍、3倍都无所谓
  • Stage 2:在最后一段范围内进行二分法搜索 (Binary Search)

在这里插入图片描述
该例子中,我们的目标是 52,我们首先从 1 开始,第一次搜索范围为 1,第二次为 2,第三次为 4,此时,我们已经走到了 79,大于目标 52 (Overshoot),因此我们在最后一段范围 (7, 79) 之内使用二分法搜索 。

*如果对于这个例子,我们只使用二分法搜索,那么搜索范围就是 (1, 79)

这个算法的性能,我们可以在以醉话的情况进行考虑,我们用 n 表示目标位置与起始位置的位置差距。

  • Stage 1 中找到 Overshoot 范围需要 log2(n) 次探测
  • Stage 2 中进行二分法搜索需要 log2(n) 次探测

因此总共需要 2⎡ log2 (n) ⎤= O(log2(n))

我们也可以用数学方法更具体的进行一个计算。我们需要对 a 和 b 两个 Posting List 进行 merge,并且我们假设 |a| < |b|。

for each doc d in a:
   b.SkipTo(d)
   if b.currpos() == d:
      output(d)
   else:
      -

在这里插入图片描述

cost = cost(d1) + cost(d2) + … + cost(d4)
   = Σ cost(di)
   = Σ log(ni)

∵ Σ ni ≤ n
∵ log(n1) + log(n2) = log(n1 * n2)
∴ Σ log(ni) = log ∏ ni

3,5
amean = (3 + 5) ÷ 2 = 4
gmean = (3 * 5) 1/2 = 3.9
amean > gmean

∴ Σ ni / k ≥ (∏ ki)1/k
∴ ∏ ki ≤ (Σ ni / k)k
∴ cost = log ∏ ni ≤ k * log(Σ ni / k) ≤ k * log(n / k) = m * log(n / m)

所以,如果 m = Θ(n) = c * n,则 cost = m * log( c ).

多词项联合查询(Multiple Term Conjunctive Queries)

形如 K1 AND K2 AND … AND Kn 的查询被称为多词项联合查询。我们在之前的笔记中已经说过,可以根据 Posting List 尺寸进行顺序操作的方法 (SvS方法) 。但这个方法对于所有 Posting List 都很长的情况不适用。

但是,SvS 方法本身是优秀的,它包含一个很重要的思想,那就是最小尺寸的 Posting List 会决定最后结果的大小。比如 K1 的 Posting List 为 10,其他词项的 Posting List 尺寸皆在 100 甚至 10000,但是,最后的结果最多只可能有 10 个。因为我们取的是包含所有这些的词项的文档,所以随着 Conjunction 越来越多,最后结果的数量只会越来越少。

秉持这个思想,我们可以得到一些新的方法:

在这里插入图片描述

在这个例子中,我们先只看 K1 和 K2 两个 Posting List。因为 K2 的最小元素为 2,所以 1 不可能是答案,在 K1 中直接使用 SkipTo(2),跳至 3,已经大于 2,所以 2 也不是结果,此时已至 K1 的结尾,停止,无答案


再同时看 K1, K2和 K3。因为 1 不在 K2 中,2 不在 K3 中,因此 1 和 2 都不可能是答案,在 K1 和 K2 中使用 SkipTp(3),K1 中能得到 3,但 K2 中跳至 4,因此 3 也不是答案,K1 已至结尾,结束,无答案

在这里插入图片描述
我们可以对这个方法进行一些优化。

在这里插入图片描述
在使用以上方法时,我们会下访问 K1,知道其第一个 Posting 为 1,之后访问 K2,知道其第一个 Posting 为 2,此时我们有两个选择:

  1. 继续访问接下来的所有词项的 Posting List,并取其第一个元素,之后的操作跟我们之前介绍的相同
  2. 我们已经发现 1 和 2 是不匹配的,那我们先解决 K1 和 K2 之间的争端 (使用 MAX 算法)。也就是在访问下一个 Posting List 之前,先把现有的矛盾解决。

第二个方法具备明显的优势,因为如果我们能确定 K1 和 K2 没有共有元素,那么我们已经可以得出结论,该查询无解,无需再进行后面的操作。

MAX 算法的伪代码:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值