快速标签搜索算法


16

问题如下。

  • 有一组简单实体E,每个实体都有一组附加的标签T。每个实体可以具有任意数量的标签。实体总数接近1亿,标签总数约为5000。

因此,初始数据如下所示:

E1 - T1, T2, T3, ... Tn
E2 - T1, T5, T100, ... Tk
..
Ez - T10, T12, ... Tl

初始数据很少更新。

  • 我的应用以某种方式在标签上生成逻辑表达式,如下所示:

    T1&T2&T3 | (T5&!T6)

  • 我需要计算与给定表达式匹配的实体数量(注意-不是实体,而是数量)。当然,这可能并不完全准确。

我现在得到的是一个简单的内存中表查找,使我在单个线程上有5-10秒的执行时间。

我很好奇,有什么有效的方法来处理这些东西吗?您会推荐哪种方法?有一些通用的算法或数据结构吗?

更新资料

根据要求进行一些澄清。

  1. T对象实际上是较短的常量字符串。但这实际上并不重要-我们总是可以分配一些ID并对整数进行运算。
  2. 我们绝对可以对它们进行排序。

1
T1对同一个对象的参考E1E2等?
Reactgular 2013年

标签如何可比?可以对标签进行排序,以便T2 < T3始终如此吗?
Reactgular 2013年

标签是二进制的吗?即T1truefalse给定的E,而不是基于输入的变量?(即Model = "V5")还是T1变量表达式Model = <input>
Bobson

Answers:


4

我会在sql中使用自连接EntityCategoryeid引用实体和 cid引用类别之间具有链接表的方式进行此操作:

    select count(ec1.eid)
    from EntityCategory ec1 
    left join EntityCategory ec2 on ec1.eid=ec2.eid 
    left join EntityCategory ec3 on ec1.eid=ec3.eid 
    ...
    where 
      ec1.cid={categoryId1} and 
      ec2.cid={categoryId2} and
      ec3.cid={categoryId3} ...

1
+1,这是经典的数据库区域。另一个答案可能有合理的想法,如何进行手动编码,但这应该是最后的选择。
MSalters 2013年

我还将选择sql作为解决此问题的技术。大多数数据库都针对这些算法进行了优化:)
winkbrace 2013年

3

写下答案后,我可能应该将问题标记为“范围太广” –我们可以讨论各种策略已有很长时间了,最​​后必须对您的数据进行基准测试。

每个标签可以有效地由整数表示。每个实体都有一组标签。选择正确的集合实现很重要– B树和排序数组都是可能的。有了这个设置,我们将仅执行成员资格测试。由于这两种结构都是在O(log t)(每个实体带有t个标签)中执行此操作的,因此我更喜欢数组,因为它们的表示形式更密集。

现在,我们可以在O(n·log t·p)操作中筛选所有实体,其中p是谓词决策树中的平均路径长度。可以对决策树进行排序,以便可以快速做出决策。如果没有统计数据,则只能排除常见的子表达式。

搜索实体的顺序并不是很重要。在另一方面,它可能是有利的排序,它使得在指数中的实体0i都具备一定的标签,而其余的则没有。这会在搜索此特定标签时减少n(在决策树中,这应该是第一个测试)。可以将其扩展到多个级别,但这会使事情变得复杂,并且占用O(2 k内存为k水平。对于多个级别,应首先确定增益最高的标签,其中增益是不必查找的实体数乘以丢弃它们的概率。当50:50的机会或50%的实体具有此特定标签时,增益变为最大。即使访问模式未知,这也可以使您进行优化。

您还可以创建用于按使用的每个标签索引实体的集合-一组包含所有实体的T1,下一组包含的所有T2。一个明显的(时间和空间)优化是在集合包含所有元素的一半以上时停止,并保存那些没有此标签的元素–这样,为所有标签建立索引所占用的½ · n · t空间会少于空间(总共t个标签)。请注意,保存互补集会使其他优化更加困难。同样,我将对数组进行排序(排序)。

如果您还通过整数范围表示实体,则可以通过仅存储连续范围的开始和结束成员来压缩用于索引集的空间。在实现方面,这可能以高位完成,以指示条目是范围限制条目还是常规条目。

如果现在有了索引集(以及标签的统计信息),我们可以优化谓词,以便首先测试不太可能的属性(快速失败策略)。这意味着,如果T1通用且T2罕见,则谓词T1 & T2应通过遍历所有T2索引集条目并测试每个元素来评估T1

如果我们使用排序数组来实现索引集,那么许多评估步骤都可以实现为合并操作。T1 & T2表示我们采用T1T2列表,为目标数组分配较大输入的大小,并执行以下算法,直到两个输入都为空:如果为T1[0] < T2[0],则T1++(丢弃头部)。如果那样的T1[0] > T2[0]T2++。如果两个磁头相等,那么,这个数字复制到目标阵列,并递增所有三个指针(T1T2,目标)。如果谓词为T1 | T2,则不会丢弃任何元素,但会复制较小的元素。形式的谓语T1 & ¬T2也可以使用并购战略实现的,但¬T1还是T1 | ¬T2不行。

在对谓词决策树进行排序时应考虑这一点:补码应该在的RHS上发生&,或者在确定最终计数而不必查看实际元素时出现在末尾。

在不使用索引集的情况下,每个线程都可以过滤掉实体的一部分,并返回与谓词匹配的元素计数,可以将这些计数相加。使用索引集时,每个线程将在决策树中分配一个节点。它接受与有序集相对应的两个输入流,并返回合并的流。请注意,决策树中的每个节点都有一个对应的集合,该集合表示满足该子表达式的所有实体,并且由于集合的顺序,因此无需一次了解整个集合即可合并它们。

可以在一定程度上组合不同的策略,例如合并索引集或通过实体列表进行过滤。过滤具有非常可预测的性能。如果查询是非常特定的,从而使索引集的使用大大减少了搜索空间,则合并操作可能会更好。重要的是要注意,合并许多大型输入集会导致性能比暴力搜索差得多。一种非常优化的算法将根据输入大小,查询结构和统计指标选择合适的策略。

顺便说一句,如果预计将来会运行类似的查询,即使它们不会加快初始运行速度,也可以缓存部分结果。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.