Python 如何唯一地组合两个列表

Python 如何唯一地组合两个列表,python,list,Python,List,我正在处理非常长的列表,并试图提出一个迭代解决方案,以独特的方式组合这两个列表 例如,我有列表 a = [TF1,Tar1] b = [Tar1, TF1] 我希望以下迭代器(如果可能)包含元组: (TF1,Tar1) (TF1,TF1) (Tar1,Tar1) 这不包括(Tar1,TF1),因为已经添加了相反的顺序 我目前的方法是循环浏览每个列表,并使用字典跟踪添加的内容。这将占用大量RAM,因为列表a的长度为12000,列表b的长度为15000。使生成的字典包含大约a*b

我正在处理非常长的列表,并试图提出一个迭代解决方案,以独特的方式组合这两个列表

例如,我有列表

a = [TF1,Tar1]
b = [Tar1, TF1]
我希望以下迭代器(如果可能)包含元组:

(TF1,Tar1)    
(TF1,TF1)  
(Tar1,Tar1)  
这不包括(Tar1,TF1),因为已经添加了相反的顺序

我目前的方法是循环浏览每个列表,并使用字典跟踪添加的内容。这将占用大量RAM,因为列表a的长度为12000,列表b的长度为15000。使生成的字典包含大约a*b/2个条目,在本例中为90M个条目


如有任何建议,我们将不胜感激。感谢

基本上,两个列表之间的公共元素会出现问题。如果您可以将组合公共元素和唯一元素的情况分开,您将解决您的问题

i、 e.您需要创建以下笛卡尔积

a_unique X b_unique
a_unique X b_common
a_common X b_unique
a_common X b_common 
在这四种情况中,最后一种情况会造成问题,因为它会创建非唯一对。再想一想,最后一个具有唯一对的笛卡尔坐标系是从一个公共点中简单选择2个元素

最后,可以通过创建两个列表的集合和,然后在比较时进行迭代来分离元素

>>> #Sample Lists
>>> a = ['C0','C1','C2','A0','A1','A2']
>>> b = ['C0','C1','C2','B0','B1','B2']
>>> from itertools import product, combinations, chain
>>> # Create sets for O(1) lookup
>>> a_key = set(a)
>>> b_key = set(b)
>>> # Segerate elements to unique and common for both lists
>>> a = {'common':a_key & b_key,
         'unique':a_key - common}
>>> b = {'common':a_key & b_key,
         'unique':b_key - common}
>>> # Create cartesian products forall the cases
>>> list(chain.from_iterable([product(a['unique'], b['unique']),
                      product(a['unique'], b['common']),
                      product(a['common'], b['unique']),
                      combinations(a['common'], 2)]))
[('A0', 'B0'), ('A0', 'B1'), ('A0', 'B2'), ('A1', 'B0'), ('A1', 'B1'), ('A1', 'B2'), ('A2', 'B0'), ('A2', 'B1'), ('A2', 'B2'), ('A0', 'C0'), ('A0', 'C1'), ('A0', 'C2'), ('A1', 'C0'), ('A1', 'C1'), ('A1', 'C2'), ('A2', 'C0'), ('A2', 'C1'), ('A2', 'C2'), ('C0', 'B0'), ('C0', 'B1'), ('C0', 'B2'), ('C1', 'B0'), ('C1', 'B1'), ('C1', 'B2'), ('C2', 'B0'), ('C2', 'B1'), ('C2', 'B2'), ('C0', 'C1'), ('C0', 'C2'), ('C1', 'C2')]

要以迭代方式生成对,您需要查看函数:

>>> l1 = [1, 2, 3]
>>> l2 = [1, 3, 7]
>>> import itertools
>>> list(itertools.product(l1, l2))
[(1, 1), (1, 3), (1, 7), (2, 1), (2, 3), (2, 7), (3, 1), (3, 3), (3, 7)]
然而,我不认为在不跟踪您已经看到的重复对的情况下删除重复对是可能的

要删除内存中的重复项,我将对元组进行排序并将其设置为一个集合:

>>> pairs = list(itertools.product(l1, l2))
>>> set(map(tuple, map(sorted, pairs)))
set([(1, 2), (2, 7), (1, 3), (3, 3), (2, 3), (1, 7), (3, 7), (1, 1)])

如果您想保持内存低,并且可以使用磁盘,我建议使用一种由磁盘文件支持的合并排序,类似于。在遍历
itertools.product
的结果时,对该对进行排序并将其写入磁盘。然后使用合并排序并读取排序列表,删除重复项(因为它们将是相邻的)。

我认为您可以避免重复项,而无需存储到目前为止生成的所有值。相反,您希望检查生成的值稍后将以相反的方式生成,并且只跟踪这些项。如果没有大量的冲突,这将需要更少的内存(尽管在最坏的情况下仍然是
O(M*N)

我是这样做的:

import itertools

def product_without_reversed_duplicates(a, b):
    a_set = set(a)
    b_set = set(b)
    dupes = set()

    for x, y in itertools.product(a, b):
        if (x, y) not in dupes: # take (x, y) only if it is not a dupe of a previous item
            yield x, y
            if x in b_set and y in a_set:  # test if (y, x) will be generated later
                dupes.add((y, x))          # if so, add it to the set to be skipped

请注意,这假设
a
b
没有任何内部副本,并且您希望尽可能保留产品的顺序(只跳过反向对)。如果
a
b
中可能存在重复,您将希望迭代
itertools.product(a_集,b_集)
,而不是我上面提到的。但是,这将以任意顺序提供结果。你可以通过额外的步骤来解决这一问题,在保持顺序的同时消除
a
b
,但如果你需要,我会让你自己来找出代码。

相当棘手,但这里有一种方法,使用
O(n)
额外的内存

xs = ['a', 'b', 'd']
ys = ['b', 'a', 'c']

def unique(seq):
    seen = set()
    seen_add = seen.add
    return [ x for x in seq if not (x in seen or seen_add(x))]

xs = unique(xs)
ys = unique(ys)

x_added = set()
for x in xs:
    for y in ys:
        if y in x_added and x in set(ys):
            continue
        print(x, y)
    x_added.add(x)
输出:

a b
a a
a c
b b
b c
d b
d a
d c

基本上,我们知道一对已经产生,如果
y
已经在迄今为止产生的
x
s中的一个中,并且
x
ys
中的一个,因为我们已经对之前的
x
s迭代了所有
y
s。独特的要求只会使处理特殊情况变得更容易。

列表是否可能有重复的元素?e、 g.a=[TF1,Tar1,TF1]@Gargamel看他的例子。我做了,但它没有回答我的问题,除非我遗漏了什么?结果的顺序重要吗?你想要2元组(成对)还是更长的元组的输出?您说过您的列表有12000和15000长您可以使用集合操作更轻松地找到您的公共和唯一集合:
common=a_键&b_键;a_unique=a_键-公共;b_unique=b_键-通用
。除此之外,这是一个很好的答案,因为即使
a
b
是同一个列表,它也不会使用超过
O(M+N)
的存储空间(因此,
itertools.product
中产生的每个值也会出现反转).@Totem:我认为标题说明了所有问题
如何将两个列表唯一地组合起来
我认为您在这里使用dicts有点奇怪。你不需要
a['common']
b['common']
;只需使用
common
。此外,您应该编写
common=a_键和b_键
;目前还没有定义。也没有理由使用
。from_iterable
;只需使用
chain
,不要列出清单。写出来:
a_key=set(a);b_键=设置(b);公共=a_键和b_键;仅a_=a_键-通用;仅b_=b_键-通用;列表(链(产品(仅a_,仅b_)、产品(仅a_,普通)、产品(仅普通,仅b_)、组合(普通,2))
。好主意,但我没想到。