子线性时间内二次查找的数据结构或算法?

时间:2009-01-09 01:28:46

标签: database data-structures indexing

有没有办法在不到O(n)的时间内根据属性或谓词从大型集合中选择子集?

举个简单的例子,说我有很多作者。每个作者与一套书有一对多的关系,与出生城市有一对一的关系。

有没有办法有效地进行查询,例如“获得出生在芝加哥的作者的所有书籍”?我能想到的唯一方法是首先从城市中选择所有作者(快速获得良好的索引),然后迭代它们并累积所有书籍(O(n)其中n是作者的数量来自芝加哥)。

我知道数据库在某些连接中做了类似的事情,并且Endeca声称能够使用他们称之为“记录关系导航”的“快速”做到这一点,但我无法找到任何有关实际算法的信息。使用甚至他们的计算复杂性。

我并不特别关心确切的数据结构......我会在RDBMS或键/值存储库或任何事情中了解如何执行此操作。< / p>

另外,这种性质的三度或四度请求呢? (给我生活在移民人数超过10,000的城市的作者写的所有书籍。)是否有广义的n度算法,它的性能特征是什么?

修改

我可能只是非常密集,但我不知道倒排索引建议如何帮助。例如,假设我有以下数据:

DATA
1.  Milton        England
2.  Shakespeare   England
3.  Twain         USA

4.  Milton        Paridise Lost
5.  Shakespeare   Hamlet
6.  Shakespeare   Othello
7.  Twain         Tom Sawyer
8.  Twain         Huck Finn

INDEX
"Milton"         (1, 4)
"Shakespeare"    (2, 5, 6)
"Twain"          (3, 7, 8)
"Paridise Lost"  (4)
"Hamlet"         (5)
"Othello"        (6)
"Tom Sawyer"     (7)
"Huck Finn"      (8)
"England"        (1, 2)
"USA"            (3)

说我对“英国作家的书籍”进行了查询。很快,在O(1)时间通过哈希表,我可以从英格兰获得我的作者列表:(1, 2)。但是,为了下一步,为了检索书籍,我必须,对于集合{1, 2}的每一个,进行另一个O(1)查找:1 -> {4}, 2 -> {5, 6}然后进行联合结果{4, 5, 6}

或者我错过了什么?也许你的意思是我应该明确地存储一个链接Book to Country的索引条目。这适用于非常小的数据集。但对于大型数据集,匹配任何可能的查询组合所需的索引数将使索引呈指数级增长。

4 个答案:

答案 0 :(得分:2)

对于大型数据集上的这样的连接,现代RDBMS通常会使用称为 list merge 的算法。使用您的示例:

  1. 准备一份居住在芝加哥的所有作者的名单,并按作者在O(Nlog(N))时间内对其进行排序。*
  2. 准备所有(作者,书名)对的列表B,并按作者在O(Mlog(M))时间内对其进行排序。*
  3. 将这两个列表“并排”放置,并比较每一堆中“顶部”(按字典编码最小)元素的作者。
    • 他们是一样的吗?如果是这样:
      • top(B)
      • 输出(作者,书名)对
      • 移除B桩的顶部元素
      • 转到3。
    • 否则,是top(A).author&lt; top(B).author?如果是这样:
      • 删除A桩的顶部元素
      • 转到3。
    • 否则,必须是top(A).author&gt; top(B).author
      • 移除B桩的顶部元素
      • 转到3。
  4. *(如果表已按作者排序,或者索引具有索引,则为O(0)时间。)

    循环继续一次移除一个项目,直到两个堆都是空的,因此采用O(N + M)步,其中N和M分别是桩A和B的大小。由于两个“桩”按作者排序,因此该算法将发现每个匹配对。它不需要索引(尽管索引的存在可能会在开始时消除对一个或两个排序操作的需要。)

    请注意,如果RDBMS估计它会更快,那么它可能会选择不同的算法(例如您提到的简单算法)。 RDBMS的查询分析器通常会估算数千种不同方法的磁盘访问和CPU时间成本,可能会考虑相关表中值的统计分布等信息,并选择最佳方法。

答案 1 :(得分:1)

Inverted Index

由于这有一个循环,我确信它没有通过 O (n)测试。但是,当结果集具有 n 行时,无法避免迭代结果集。但是,查询是两个哈希查找。

from collections import defaultdict

country = [ "England", "USA" ]

author=  [ ("Milton", "England"), ("Shakespeare","England"), ("Twain","USA") ]

title = [ ("Milton", "Paradise Lost"), 
    ("Shakespeare", "Hamlet"),
    ("Shakespeare", "Othello"),
    ("Twain","Tom Sawyer"),
    ("Twain","Huck Finn"),
]

inv_country = {}
for id,c in enumerate(country):
    inv_country.setdefault(c,defaultdict(list))
    inv_country[c]['country'].append( id )

inv_author= {}
for id,row in enumerate(author):
    a,c = row
    inv_author.setdefault(a,defaultdict(list))
    inv_author[a]['author'].append( id )
    inv_country[c]['author'].append( id )

inv_title= {}
for id,row in enumerate(title):
    a,t = row
    inv_title.setdefault(t,defaultdict(list))
    inv_title[t]['title'].append( id )
    inv_author[a]['author'].append( id )

#Books by authors from England
for t in inv_country['England']['author']:
    print title[t]

答案 2 :(得分:1)

SELECT a.*, b.*
   FROM Authors AS a, Books AS b
   WHERE a.author_id = b.author_id
     AND a.birth_city = "Chicago"
     AND a.birth_state = "IL";

一个优秀的优化器将在不到读取整个作者列表和整个书籍列表所需的时间内处理,因此这是次线性时间。 (如果您对次线性的含义有另一种定义,请说出来。)

请注意,优化器应该能够选择处理最有利的表的顺序。这适用于N级查询集。

答案 3 :(得分:1)

一般来说,RDBMS可以很好地处理这些类型的查询。商业和开源数据库引擎都使用了所有合理的计算算法,经过几十年的发展,尽可能快地完成这项任务。

如果你的数据是专门组织的并且需要特定的算法,那么我猜想你在速度上击败RDBMS的唯一方法就是这样。一些RDBS允许您指定可用于操作数据的基础算法,而对于开源数据,您可以随时重写或实现新算法。

然而,除非你的情况非常特殊,否则我认为这可能是一种严重的矫枉过正。对于大多数情况,我会说将数据放在RDBMS中并通过SQL操作它应该能够很好地工作,这样你就不必担心基本的算法了。