Python中 set.intersection 的相反操作是什么?

105
在Python中,您可以使用a.intersection(b)来查找两个集合中共同的项。
有没有一种方法可以执行此操作的disjoint相反版本呢?即找到既不是a又不是b的项;将a中独特的项与b中独特的项合并。
5个回答

196
你正在寻找的是“对称差集”;即只出现在集合a或集合b中,但不同时出现在两个集合中的所有元素。
a.symmetric_difference(b)

来自set.symmetric_difference()方法文档

返回一个新的集合,其中包含集合或其他中的元素,但不包含两者都有的元素。

如果ab都是集合,则也可以使用^运算符:

a ^ b

set.symmetric_difference() 接受任何可迭代的 other 参数。

输出等同于 (a | b) - (a & b),即两个集合的并集减去交集。

生成输出需要 O(M+N) 的时间,其中 M 和 N 分别是集合 ab 的长度;需要 M 步来复制集合 a,然后需要 N 步来根据 b 中的每个值修改该集合:

def symmetric_difference(a, b):
    result = set(a)
    for elem in b:
        try:
            result.remove(elem)
        except KeyError:
            result.add(elem)
    return result

也有原地变体,其中集合a直接被修改;使用a.symmetric_difference_update(b)a ^= b。原地变体需要O(N)时间,因此只取决于集合b的大小:

def symmetric_difference_update(a, b):
    for elem in b:
        try:
            a.remove(elem)
        except KeyError:
            a.add(elem)
    # no return, a has been updated in-place

通常^是XOR运算符吗? - user4847061
1
@user4847061:确实如此,但是集合已经重载了多个这样的运算符。 |& 通常是按位或和按位与,但是在集合上它们为您提供并集和交集。比较运算符 <<=>>= 也已被重载。 - Martijn Pieters
鉴于问题已经表述为“a中唯一项与b中唯一项的并集”,你却将其描述为“两个集合的并集减去两个集合的交集”,这似乎有些奇怪。这让人觉得他们的方法是错误的。 - Kelly Bundy
@KellyBundy:为什么这很奇怪?这就是对称差集的定义;它是两个集合的并集去掉它们共有的元素。他们的措辞有点不太灵活,语法上也不太对,因为集合本身就是唯一的项。需要重新审视一下才能看到“唯一”指的是a和b的非对称差集。 - Martijn Pieters
因为通常当有人说“X是Y”,然后有人回复“X是Z”,那意味着这是一种纠正,也就是说,“X是Y”是错误的。至少“仅限于a的项目”是正确的,这也是我立刻读到他们的方式。 - Kelly Bundy

7
a={1,2,4,5,6}
b={5,6,4,9}
c=(a^b)&b
print(c) # you got {9}

你为什么在这里添加了一个交集操作?这不是交集的反运算,而是ba之间的(非对称)差异。这可以用更简单的表达式b - a来表示!a ^ b才是ab的对称差异和真正的交集的反运算,结果应该是{1, 2, 9} - Martijn Pieters

2
最佳方法是使用列表推导式。
a = [ 1,2,3,4]
b = [ 8,7,9,2,1]
c = [ element for element in a if element not in b] 
d = [ element for element in b if element not in a] 
print(c) 
# output is [ 3,4]
print(d) 
# output is  [8,7,9]

您可以将这两个列表合并


3
与上述操作相比,列表推导式的性能要慢得多,特别是对于大型操作而言,可能需要数小时甚至数天的时间。虽然小型列表还能承受,但对于大型操作,不太适合使用列表推导式。 - Joe B
谢谢您指出这一点,我之前不知道,因为我的列表通常不是很长。 - Apeasant
这个问题是关于集合而不是列表的。这是有原因的:对称差可以在O(M+N)时间内产生,其中M和N是两个集合的长度。你的循环需要O(MxN)的时间,对于长度为M和N的列表。尝试使用长度为100和1000的列表,然后将这些列表增加10倍并再次尝试。现在比较一下如果你从集合开始需要多长时间。 - Martijn Pieters
请注意,您还要产生两个不同的(非对称的)差异结果;这并不完全是交集的逆运算。您需要将这两个列表连接起来才能得到那个结果。 - Martijn Pieters
如果你一定需要一个列表,那么至少先将较长的列表转换为集合,遍历较短的列表并选择所有不在该集合中的元素,同时将那些未缺失的元素添加到第二个集合中。然后在较长的列表上循环,并选择所有那些在第二个集合中缺失的元素输出。这也是一个O(N+M)操作。 - Martijn Pieters

-1

e,f是两个你想要检查不相交的列表

a = [1,2,3,4]
b = [8,7,9,2,1]

c = []
def loop_to_check(e,f):
    for i in range(len(e)):
        if e[i] not in f:
            c.append(e[i])


loop_to_check(a,b)
loop_to_check(b,a)
print(c)

## output is [3,4,8,7,9]

这个循环回到列表并返回不相交的列表


这种方法的性能很差,因为value in list需要检查列表中的每个元素是否相等;对于长度为N和M的输入,这将花费O(MxN)的时间。另一方面,集合操作只需要O(M+N)的时间,其中K是两个集合中最短的长度。尝试在两个具有1000个元素的列表上使用它。再试试有10000个元素的列表。问题之所以问到集合,是有原因的。 - Martijn Pieters

-2
尝试使用以下代码:(set(a) - intersection(a&b))
a = [1,2,3,4,5,6]
b = [2,3]

for i in b:
   if i in a:
      a.remove(i)

print(a)

输出结果为[1,4,5,6],希望它能正常工作。


2
在迭代列表时改变它们通常是不好的(在这种情况下,除非我只关心返回一个新列表而不修改 a,否则没有真正的后果)。此外,check = i in a 是多余的,因为你总是可以使用 if i in a: - cowbert
@cowbert 谢谢你的建议。我已经修复了它。我会更多地学习关于那个的知识。 - Muhammad Ammar Fauzan
你可以尝试使用这个一行代码的解决方案:print(sorted(set(a)-set(b))) - ravibeli
我相信这被称为两个集合的相对补集差集 - ingyhere
使用NumPy,这个问题变得更容易了:np.setdiff1d(a, b)。 - Muhammad Ammar Fauzan
该问题涉及集合而非列表,并输出a和b的(非对称)差集。这不是交集的反集,而是一个对称差,即a-bb-a的并集。试着将7添加到b中,它将被包含在对称差中。 - Martijn Pieters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接