在保持订单的同时组合两个列表

时间:2016-07-26 21:50:11

标签: python combinations permutation itertools

我尝试加入两个列表并输出合并列表的所有可能组合,以维护原始两个列表的排序。例如:

DirectoryEntry dnc = new DirectoryEntry("LDAP://corp.domain.tld");
SecurityIdentifier domainSID = new SecurityIdentifier(dnc.Properties["objectSid"].Value);

string administratorsGroupSID = String.Format("{0}-544", domainSID);
string administratorsGroupSIDPath = String.Format("LDAP://<SID={0}>", administratorsGroupSID);

DirecoryEntry AdministratorsGroupDE = new DirectoryEntry(administratorsGroupSIDPath);

列表中的每个元素&#34;组合&#34;, 2 始终位于 1 之前, 9 始终位于之前的 8

到目前为止,我已经使用了来自itertools的排列来循环所有可能的排列,但它还不够快。

这是我得到的:

list_1 = [9,8]
list_2 = [2,1]

#output
combo= [9821,9281,2981,2918,2198,9218]

谢谢!

5 个答案:

答案 0 :(得分:4)

尝试一下:

import itertools

lst1 = ['a', 'b']
lst2 = [1, 2]

for locations in itertools.combinations(range(len(lst1) + len(lst2)), len(lst2)):
    result = lst1[:]
    for location, element in zip(locations, lst2):
        result.insert(location, element)
    print(''.join(map(str, result)))

# Output:
# 12ab
# 1a2b
# 1ab2
# a12b
# a1b2
# ab12

我想到问题的方式,你从第一个序列(在这种情况下为ab)开始,然后查找可以插入第二个序列的元素的所有可能位置(在这种情况下,一个1,然后一个2)。

itertools.combinations来电为您提供了这些组合。在上面的示例中,它遍历位置(0, 1)(0, 2)(0, 3)(1, 2)(1, 3)(2, 3)

对于每组坐标,我们只需将第二个列表中的元素插入指定的索引。

<强>更新

这是一个处理任意数量列表的递归解决方案,基于@ĐặngXuânThành在他的回答中提出的建议:

import itertools

def in_order_combinations(*lists):
    lists = list(filter(len, lists))

    if len(lists) == 0:
        yield []

    for lst in lists:
        element = lst.pop()
        for combination in in_order_combinations(*lists):
            yield combination + [element]
        lst.append(element)

for combo in in_order_combinations(['a', 'b'], [1, 2]):
    print(''.join(map(str, combo)))

基本思路是,从ab12开始,您知道所有可能的解决方案都将以b2结束。以b结尾的那些都将以(a12)的解决方案开头。以2结尾的内容都将以(ab1)的解决方案开头。

递归的基本情况就是没有列表时。 (我们去的时候会清空空列表。)

答案 1 :(得分:3)

如果您的输出是列表而不是连接数字,那将会更清晰,但这并不重要。这是python3中的一个简单的递归解决方案(但你可以简单地将它转换为python2)。

def combine(xs, ys):
    if xs == []: return [ys]
    if ys == []: return [xs]
    x, *xs_tail = xs
    y, *ys_tail = ys
    return [ [x] + l for l in combine(xs_tail, ys) ] + \
           [ [y] + l for l in combine(ys_tail, xs) ]

这将返回一个列表列表:

>>> combine([9, 8], [2, 1])
[[9, 8, 2, 1], [9, 2, 1, 8], [9, 2, 8, 1], [2, 1, 9, 8], [2, 9, 8, 1], [2, 9, 1, 8]]

以下是将其转换为所需输出的方法:

def list_to_int(digits):
    return int(''.join(map(str, digits)))

def combine_flat(xs, ys):
    return [list_to_int(l) for l in combine(xs, ys)]

答案 2 :(得分:2)

使用递归生成器的解决方案(yield from ...需要Python 3):

def f(a,b,p=[]):
    if len(a)==0 or len(b)==0:
        yield p+a+b
    else:
        yield from f(a[1:],b,p+[a[0]])
        yield from f(a,b[1:],p+[b[0]])

在每一步,您可以选择a的第一个字符或b的第一个字符,并递归地构建列表的其余部分。如果其中一个变空,则没有更多的选择点。

>>> list(f([9,8],[2,1]))
[[9, 8, 2, 1], [9, 2, 8, 1], [9, 2, 1, 8], [2, 9, 8, 1], [2, 9, 1, 8], [2, 1, 9, 8]]

更新:从上面的解决方案开始,这是一个处理任意数量列表的实现:

def f(*args,p=[]):
    if any(len(arg)==0 for arg in args):
        yield p+[el for arg in args for el in arg]
    else:
        for i,arg in enumerate(args):
            args1=list(args)
            args1[i]=arg[1:]
            yield from f(*args1,p=p+[arg[0]])

答案 3 :(得分:1)

我不太了解python,但我有一个想法可能会有所帮助 这个想法是使用递归:
 加入两个名单n&amp; m项,我们有两种情况:

  • 加入两个名单n-1&amp; m项,然后将第n项放在最后。
  • 加入两个名单n&amp; m-1项,然后将第m项放在最后。

使用此递归,您只需处理最简单的情况:连接两个列表,其中一个列表为空。 它会快速使用排列。 希望它有所帮助。

答案 4 :(得分:1)

长(ish)单线

from itertools import *
from copy import deepcopy

list({''.join(str(l.pop(0)) for l in deepcopy(p)) for p in permutations(chain(repeat(list_1, len(list_1)), repeat(list_2, len(list_2))))})

有关解释,请参阅我对类似问题All possible ways to interleave two strings的回答。