【问题标题】:Fast search of lists of tuples元组列表的快速搜索
【发布时间】:2018-07-11 17:31:05
【问题描述】:

所以我有许多从数据库中提取的元组列表,格式如下:

dataA = [('A', 'B', 'C', D, E, F), ('A', 'B', 'C', D, E, F), ...]

然后我有一个元组列表,格式为:

dataB = [('X', 'A', 'B', 'C', Y), ('X', 'A', 'B', 'C', Y), ...]

我需要通过匹配 'A' = 'A'、'B' = 'B'、'C' = 'C' 和其中 D > Y > E 来匹配从 dataA 到 dataB 的元组。元组匹配的位置,我将使用值“X”和“F”。

'A'、'B'、'C'、'X' 是最多 16 个字符的字符串。 D、E、F、Y 是整数。

我尝试了很多方法来做到这一点(字符串比较、嵌套 for 循环、设置交集),但它们都很慢。实现这一目标的最佳方法是什么?非常感谢

【问题讨论】:

  • 您应该包含您尝试过的内容,以便我们可以尝试更有效地解决您的问题。
  • 你的 D、E、F 和 Y 变量也是单字符串吗?
  • 欢迎来到 StackOverflow。请按照您创建此帐户时的建议阅读并遵循帮助文档中的发布指南。 Minimal, complete, verifiable example 适用于此。在您发布 MCVE 代码并准确描述问题之前,我们无法有效地帮助您。我们应该能够将您发布的代码粘贴到文本文件中并重现您描述的问题。 StackOverflow 不是编码或教程服务。
  • @PM2Ring 'A'、'B'、'C' 等是最多 16 个字符的字符串。 D、E、F、Y 是整数。我应该早点指定的。
  • 我不认为 MCVE 在这里真的有帮助。 OP 在实现算法时没有遇到任何问题——他已经实现了一大堆算法,而且它们显然都有效,而且他大概可以对它们进行基准测试以判断它们不够快,等等。他的麻烦在于首先提出要使用的正确算法。

标签: python performance list sorting tuples


【解决方案1】:

这里的关键是选择正确的数据结构。 tl;dr 是集合的区间树的字典是正确的数据结构,但这对您来说可能意义不大,并且绝对不能解释如何到达那里。

在开始之前……您可以将工作推送到关系数据库吗?毕竟,这些值首先是“从数据库中提取的”,而优化这些查找正是 RDBMS 的全部内容。使用适当的索引,SELECT f FROM mytable WHERE a=?, b=?, c=?, ? BETWEEN e and d 应该以对数时间运行。另外,您无需获取两个表的所有行,只需获取匹配的行即可。


首先,您只希望前三个值完全匹配的元组。因此,您需要一个 dict,以前三个成员为键,并具有匹配的值:

dictA = {}
for a, b, c, *d in dataA:
    dictA.setdefault((a, b, c), set()).add(tuple(d))

这已经将每次比较缩小为仅对具有正确 A, B, C 的元组进行线性搜索,而不是对所有元组进行线性搜索。这可能已经足够了。


如果没有,你如何减少线性搜索?

如果不是A, B, C 的一组元组,而是一个按D 值排序的排序列表(或二叉搜索树等),该怎么办?然后你可以找到第一个D > Y 的对数时间而不是线性时间。但是,不幸的是,您仍然需要从那里扫描到最后的列表的其余部分,因为它们都有D > Y,并且您不知道哪些也有Y > E,直到您全部测试它们。所以你只是将总时间减少了 50%,而不是对数。

如果您有两个排序列表,一个按D 排序,另一个按E 排序怎么办?起初这似乎很有希望,但是您如何将它们结合起来呢?


您实际需要做的是将元组分解为不重叠的区间,每个区间包含一个或多个元组。例如,如果元组 A 有 D=4, E=1,元组 B 有 D=6, B=3,则区间 (1, 3) 和元组 {A},区间 (3, 4) 和元组 {A, B},区间 (4, 6){B}.

然后您可以将这些不相交的间隔存储在二叉搜索树(或其他对数数据结构)中。由于它们是不相交的,您可以按字典顺序按 (begin, end, value) 或仅按 begin 排序,以您使用的树 API 更容易为准。


所以现在,搜索是在(a, b, c) 上进行哈希查找,以查找由d 排序的不相交的(d, e) 间隔树,然后使用y 在该树上进行对数搜索,然后检查是否@987654345 @ 表示该区间,答案是该区间内元组的对应集合 {f}


Python 中显然没有用于区间脱节的代码,但自己构建并不难。

事实上,PyPI 上有包含整个区间树结构的库。但这不像字典,即使你不知道散列是什么意思,使用它也很容易。一旦您牢牢记住了抽象,使用区间树就很容易,但在此之前很难做到正确。例如,您需要考虑您的开放范围(您正在测试D < Y < E,而不是通常的Python 半开放D < Y <= E)应该如何处理边缘。因此,可能值得先手动构建它。


所以把它们放在一起,你可以像这样构建你的数据结构:

dictA = {}
for a, b, c, d, e, f in dataA:
    dictA.setdefault((a, b, c), set()).add((d, e, f))
for key, values in dictA.items():
    tree = IntervalTree()
    for d, e, f in values:
        tree.add(low=d, high=e, value=f)
    dictA[key] = tree

然后你像这样使用它:

for x, a, b, c, y in dataB:
    tree = dictA.get((a, b, c))
    if tree:
        d, e, fs = tree.search(y)
        if f:
            for f in fs:
                yield x, f

这可能不是按原样可运行的代码,尤其是因为我怀疑任何区间树库都有这个 API,但这几乎就是您的代码的结构。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-06
    • 2020-09-23
    • 2019-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-20
    • 2010-11-28
    相关资源
    最近更新 更多