合并列表中相同元素的列表

时间:2018-09-28 09:44:33

标签: python

我需要合并列表并具有可以实现的功能,但是当合并的数量非常缓慢且难以忍受时,我想知道是否有更有效的方法

合并条件:子列表包含相同的数字,谢谢

简单关联:

[7,8,9] = [7,8]+[8,9]   #The same number 8 

级联包含:

[1,2,3]   = [1,2,3]+[3,4] #The same number 3 
[3,4,5,6] = [3,4],[4,5,6] #The same number 4 
[1,2,3,4,5,6] = [1,2,3]+[3,4,5,6] #The same number 3 

功能:

a =  [ [1,2,3],[4,5,6],[3,4],[7,8],[8,9],[6,12,13] ]
b = len(a)
for i in range(b):
    for j in range(b):
        x = list(set(a[i]+a[j]))
        y = len(a[j])+len(a[i])
        if i == j or a[i] == 0 or a[j] == 0:
            break
        elif len(x) < y:
            a[i] = x
            a[j] = [0]
            print a
print [i for i in a if i!= [0]]

结果:

[[8, 9, 7], [1, 2, 3, 4, 5, 6, 10, 11]]

上面是一个示例,其中实际计算中的每个子列表的长度只有2,

a =  [[1,3],[5,6],[3,4],[7,8],[8,9],[12,13]]

我想错过更多数据,这是模拟数据。

a = np.random.rand(150,150)>0.99 
a[np.tril_indices(a.shape[1], -1)] = 0     
a[np.diag_indices(a.shape[1])]     = 0     
a = [list(x) for x in np.c_[np.where(a)]]

consolidate(a)

2 个答案:

答案 0 :(得分:1)

我认为您的算法已接近最佳,但由于相交操作是对称的,因此可以缩短内部循环,也就是说,如果您检查(A, B)相交,则无需检查{{1} }。 这样,您将从(B, A)转到O(n²)

但是,我将更清楚地重写代码段,并且还避免修改输入。 还要注意,由于O(n * (n / 2))不能保证排序,因此在列出之前进行一些排序是个好主意。

这是我建议的代码(已编辑,以减少强制转换和排序的次数)

set

将代码封装在一个函数中,我会得到:

def consolidate(items):
    items = [set(item.copy()) for item in items]
    for i, x in enumerate(items):
        for j, y in enumerate(items[i + 1:]):
            if x & y:
                items[i + j + 1] = x | y
                items[i] = None
    return [sorted(x) for x in items if x]

这将使我们能够进行一些干净的微基准测试(为完整性起见,我还包括@zipa的def consolidate_orig(a): a = [x.copy() for x in a] b = len(a) for i in range(b): for j in range(b): x = list(set(a[i]+a[j])) y = len(a[j])+len(a[i]) if i == j or a[i] == 0 or a[j] == 0: break elif len(x) < y: a[i] = x a[j] = [0] return [i for i in a if i!= [0]] ):


编辑:

@zipa的代码未正确封装,这是具有正确封装的等效版本:

merge()

和更新的时间:

def merge(iterable, base=None):
    if base is None:
        base = iterable
    merged = set([tuple(set(i).union(
        *[j for j in base if set(i).intersection(j)])) for i in iterable])
    if merged == iterable:
        return merged
    else:
        return merge(merged, base)

表明,至少对于此输入,建议的解决方案始终更快。 由于生成大量有意义的输入不是太简单,因此我将让您检查一下,确定该方法是否比考虑到较大输入的方法更有效。


编辑

使用较大但可能无意义的输入,对于建议的版本来说,时间仍然是有利的:

in_list = [[1,2,3], [4,5,6], [3,4], [7,8], [8,9], [6,12,13]]
%timeit consolidate_orig(in_list)
# 17.9 µs ± 368 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit consolidate(in_list)
# 6.15 µs ± 30 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit merge(in_list)
# 53.6 µs ± 718 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

in_list = [[1, 3], [5, 6], [3, 4], [7, 8], [8, 9], [12, 13]]
%timeit consolidate_orig(in_list)
# 16.1 µs ± 159 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit consolidate(in_list)
# 5.87 µs ± 71.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit merge(in_list)
# 27 µs ± 701 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

答案 1 :(得分:0)

这种方法应该在较大的嵌套列表上执行得更快:

def merge(iterable):
    merged = set([tuple(set(i).union(*[j for j in a if set(i).intersection(j)])) for i in iterable])
    if merged == iterable:
        return merged
    else:
        return merge(merged)
merged(a)
#set([(1, 2, 3, 4, 5, 6, 12, 13), (8, 9, 7)])

它递归地组合列表,直到用尽所有组合。