以多种方式合并 2 个列表 - Python

Merging 2 Lists In Multiple Ways - Python

我一直在试验多种技术,但我确信有一些简单的方法可以完成这项工作。

假设我有两个列表,其中包含相同数量的项目(每个 4 个):

a = ['a', 'b', 'c', 'd']    
b = [1, 2, 3, 4]

我想以所有可能的方式合并这些列表,同时保持顺序。示例输出:

a, b, c, d, 1, 2, 3, 4    
1, 2, 3, 4, a, b, c, d    
a, b, 1, 2, c, 3, 4, d

要点是每个列表都必须保留其顺序,因此考虑到它在列表中的位置,一个项目不能在输出中排在另一个项目之前。所以例如输出不能是:

a, b, **d**, c, 1...   > d precedes c whereas c is before d in the original list
1, **4**, a, b, 3....  > 4 precedes 3 whereas 3 is before 4 in the original list

我想这个想法是将第二个列表以所有可能的方式合并到第一个列表中。一个完整的例子是这样的:

a = [a, b]    
b = [1, 2]

期望的输出:

ab12                                                                      
a1b2                                                                             
a12b                                                                         
1ab2                                                                             
1a2b                                                                          
12ab

我该怎么做? itertools 是否有能力以某种方式做到这一点?还是有另一种方法来完成这项工作?请帮忙!

在 2x4 的情况下,您希望在不打乱每个四边形内的顺序的情况下获取所有 8 个元素。这些例子:

a, b, c, d, 1, 2, 3, 4    
1, 2, 3, 4, a, b, c, d    
a, b, 1, 2, c, 3, 4, d

可以转换为 "instructions" 的序列,这些序列是要从中获取的列表,0 或 1:

0 0 0 0 1 1 1 1
1 1 1 1 0 0 0 0
0 0 1 1 0 1 1 0

当你意识到这一点后,你可能会注意到我们需要生成的序列都是四个零和四个一的排列。实现这一飞跃后,我们可以使用 itertools:

itertools.permutations([0,0,0,0,1,1,1,1])

对于 2x4 的情况,这给出了 40320 个结果,但只有 70 个不同的结果(因为 itertools.permutations 认为 1,1,1 不同于 1,1,1 如果重新排序数字)。您可以从此处的答案中获得独特的排列: 或仅使用 set().


综上所述,这是一个完整的解决方案:

import itertools

def combos(*seqs):
    counts = map(len, seqs)
    base = []
    for ii, count in enumerate(counts):
        base.extend([ii]*count)
    for take in set(itertools.permutations(base)):
        result = []
        where = [0] * len(seqs)
        for elem in take:
            result.append(seqs[elem][where[elem]])
            where[elem] += 1
        yield result

您可以这样测试(给出 70 个结果):

a = ['a', 'b', 'c', 'd']
b = [1, 2, 3, 4]

for res in combos(a, b):
    print res

另一种选择是使用递归。

伪代码:

result[] SortedMergePermutations(listA,listB)
{
  result.append([FirstElementOfListA, 
    SortedMergePermutations(listAWithoutFirstElement,listB))]
  result.append([FirstElementOfListB,
    SortedMergePermutations(listA,listBWithoutFirstElement))]
  ])
  return result
}

python的递归方法:

def f( s , l1 , l2 ):
        if( l1 == [] and l2 == [] ):
                print s
                return
        if( l1 != [] ):
                f( s + l1[0] , l1[1:] , l2 )
        if( l2 != [] ):
                f( s + str(l2[0]) , l1 , l2[1:] )

一个选项是使用一个计数器,其中设置位对应于 a 上的项目并且取消设置为 b 上的项目。对于计数器中的每个值,检查是否设置了 len(a) 位并生成排列:

def ordered_permutations(l1, l2):
    length = len(l1) + len(l2)
    fmt = '{{0:0{0}b}}'.format(length)

    for i in xrange(2 ** length):
        seq = fmt.format(i)

        if seq.count('1') == len(l1):
            iters = [iter(l1), iter(l2)]
            yield [iters[int(c)].next() for c in seq]

用法:

for p in ordered_permutations(['a','b'], [1,2]):
    print p

输出:

['a', 'b', 1, 2]
['a', 1, 'b', 2]
['a', 1, 2, 'b']
[1, 'a', 'b', 2]
[1, 'a', 2, 'b']
[1, 2, 'a', 'b']

可以通过使用 HAKMEM 175 生成下一个序列而不是使用计数器并检查是否设置了正确的位数来改进实现。

我找到了一个仅适用于两个序列的解决方案,但使用 itertools.combinations() 来找到放置(按顺序...)第一个序列的元素的可能位置序列

from __future__ import print_function

def print_merged(a, b):
    from itertools import combinations, cycle

    l = len(a) + len(b)
    ab = [cycle(a), cycle(b)]
    rng = range(l)
    print([a, b])

    for index in combinations(rng, len(a)):
        li = []
        for i in rng:
            n = 0 if i in index else 1
            li.append(next(ab[n]))
        print(li)

# testing

print_merged([1,2,3], [4,5,6])
print('-'*72)
print_merged([1,2], [4,5,6])
print('-'*72)
print_merged([1,2,3], [5,6])

我模糊地理解可以处理更多的列表,将第三个列表与第一个和第二个列表生成的每个列表合并,等等,这个想法指向递归实现的方向但我很高兴把这样的成就留给别人...

编辑 1

我已经移除了计数器机器,因为 itertools.cycle() 对象正是我们所需要的。

测试输出

[[1, 2, 3], [4, 5, 6]]
[1, 2, 3, 4, 5, 6]
[1, 2, 4, 3, 5, 6]
[1, 2, 4, 5, 3, 6]
[1, 2, 4, 5, 6, 3]
[1, 4, 2, 3, 5, 6]
[1, 4, 2, 5, 3, 6]
[1, 4, 2, 5, 6, 3]
[1, 4, 5, 2, 3, 6]
[1, 4, 5, 2, 6, 3]
[1, 4, 5, 6, 2, 3]
[4, 1, 2, 3, 5, 6]
[4, 1, 2, 5, 3, 6]
[4, 1, 2, 5, 6, 3]
[4, 1, 5, 2, 3, 6]
[4, 1, 5, 2, 6, 3]
[4, 1, 5, 6, 2, 3]
[4, 5, 1, 2, 3, 6]
[4, 5, 1, 2, 6, 3]
[4, 5, 1, 6, 2, 3]
[4, 5, 6, 1, 2, 3]
------------------------------------------------------------------------
[[1, 2], [4, 5, 6]]
[1, 2, 4, 5, 6]
[1, 4, 2, 5, 6]
[1, 4, 5, 2, 6]
[1, 4, 5, 6, 2]
[4, 1, 2, 5, 6]
[4, 1, 5, 2, 6]
[4, 1, 5, 6, 2]
[4, 5, 1, 2, 6]
[4, 5, 1, 6, 2]
[4, 5, 6, 1, 2]
------------------------------------------------------------------------
[[1, 2, 3], [5, 6]]
[1, 2, 3, 5, 6]
[1, 2, 5, 3, 6]
[1, 2, 5, 6, 3]
[1, 5, 2, 3, 6]
[1, 5, 2, 6, 3]
[1, 5, 6, 2, 3]
[5, 1, 2, 3, 6]
[5, 1, 2, 6, 3]
[5, 1, 6, 2, 3]
[5, 6, 1, 2, 3]