【问题标题】:Finding symmetric difference of 2 sets separating them by origin找到按原点分隔它们的 2 个集合的对称差异
【发布时间】:2014-11-26 17:01:44
【问题描述】:

我有两套:

>>> a = {1,2,3}
>>> b = {2,3,4,5,6}

我想获得 两个 具有 共同元素的新集合,第一个集合包含来自 a 的元素,第二个包含来自 b 的元素,例如 ({1}, {4,5,6}) ,或类似:

>>> c = a&b # Common elements
>>> d = a^b # Symmetric difference
>>> (a-b, b-a)
({1}, {4, 5, 6})
>>> (a-c, b-c)
({1}, {4, 5, 6})
>>> (a&d, b&d)
({1}, {4, 5, 6})

我的问题是我要在大量 sha1 哈希上使用它,我担心性能。 什么是有效地做到这一点的正确方法

注意:ab 将有大约 95% 的共同元素,1% 将在 a 和 4% 在 b

【问题讨论】:

  • a-b, b-a 是我过去这样做的方式,但它是为了可读性而不是性能。为什么不尝试一些方法并通过timeit 分别运行它们?
  • 我认为你无法击败a-b, b-a。 Python 所要做的就是评估名称ab,然后用- 进行二进制减法(你可以用dis.dis 看到确切的细分)。然而,创建新集合的实际函数是用 C 编写的,这将优于用 Python 编写的任何东西。
  • 我认为 a-b, b-a 将是 O(n) 并且无论如何我无法想象有什么比 O(n) 更好的方法。使用一次迭代而不是两次迭代不会改变复杂性。
  • 无论如何这是一个有趣的问题,当我在关系数据库上更新多对多时找出要添加/修改/删除哪些元素时,我经常需要这样做

标签: python performance python-3.x set


【解决方案1】:

我在问题中提到的方法具有以下性能:

>>> timeit.timeit('a-b; b-a', 'a=set(range(0,1500000)); b=set(range(1000000, 2000000))', number=1000)
135.45828826893307
>>> timeit.timeit('c=a&b; a-c; b-c', 'a=set(range(0,1500000)); b=set(range(1000000, 2000000))', number=1000)
189.98522938665735
>>> timeit.timeit('d=a^b; a&d; b&d', 'a=set(range(0,1500000)); b=set(range(1000000, 2000000))', number=1000)
238.35084129583106

所以最有效的方法似乎是使用(a-b, b-a) 方法。

我将此作为参考发布,因此其他答案会添加新方法,而不是比较我找到的方法。


Python实现函数

出于好奇,我尝试实现自己的 python 函数来执行此操作(适用于预排序的迭代器):

def symmetric_diff(l1,l2):
    # l1 and l2 has to be sorted and contain comparable elements
    r1 = []
    r2 = []
    i1 = iter(l1)
    i2 = iter(l2)

    try:
        e1 = next(i1)
    except StopIteration: return ([], list(i2))
    try:
        e2 = next(i2)
    except StopIteration: return ([e1] + list(i1), [])

    try:
        while True: 
            if e1 == e2:
                e1 = next(i1)
                e2 = next(i2)
            elif e1 > e2:
                r2.append(e2)
                e2 = next(i2)
            else:
                r1.append(e1)
                e1 = next(i1)

    except StopIteration:
        if e1==e2:
            return (r1+list(i1), r2+list(i2))
        elif e1 > e2:
            return (r1+[e1]+list(i1), r2+list(i2))
        else:   
            return (r1+list(i1), r2+[e2]+list(i2))

与其他方法相比,这种方法的性能相当低:

t = timeit.Timer(lambda: symmetric_diff(a,b))
>>> t.timeit(1000)
542.3225249653769

因此,除非在某处实现了其他方法(一些用于处理集合的库),否则我认为使用两组差异最有效的方法。

【讨论】:

    猜你喜欢
    • 2023-03-27
    • 1970-01-01
    • 2014-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-16
    • 2016-07-31
    相关资源
    最近更新 更多