在不考虑相邻元素的情况下从列表创建组合

时间:2019-05-21 09:20:39

标签: python python-3.x itertools

我想从列表中生成组合而不考虑相邻元素。

我尝试过一种代码,该代码提供了不考虑相邻元素的组合,并且可以处理列表中的唯一元素。 但是,它不适用于列表Eg中的重复元素。 [4,5,4,3]

代码:

import itertools

b = []    
stuff = [4,5,4,3]

for L in range(2, len(stuff)+1):

    for subset in itertools.combinations(stuff, L):
        a =list(subset)       

        for i in range(1,len(a)):  

            if stuff.index(a[i-1]) == stuff.index(a[i])-1:

                a.clear()

                break

            else:

                b.append(a)

print('b = ',b)   

预期结果= [[4,4],[4,3],[5,3]]

实际结果= [[4, 4], [4, 3], [5, 4], [5, 3], [4, 3], [4, 4, 3], [4, 4, 3], [5, 4, 3], [5, 4, 3]]

我可以举一个例子来解释:假设列表是[1,2,3,4,5],那么可能的非相邻组合是[[1,3],[1,4],[1,5], [2,4],[2,5],[3,5],[1,3,5]。我想要这些组合。我正在尝试的代码可以与唯一集一起很好地工作,但是当给定列表中的数字重复出现时,例如[1,3,2,3,2,5],那么在获取索引时,它总是取前3个而不是其他一。那么如何从这个集合中获得组合

1 个答案:

答案 0 :(得分:1)

与其生成所有的itertools.combinations,然后使用index过滤掉有效的{a},这(a)效率很低,(b)不适用于重复的元素,而是应该实现自己的combinations算法,这一点也不难,可能看起来像这样:

def comb(lst, num):
    if num == 0:
        yield []
    if 0 < num <= len(lst):
        first, *rest = lst
        for c in comb(rest, num-1):
            yield [first] + c
        for c in comb(rest, num):
            yield c

要添加“无相邻元素”约束,只需跟踪您是否获取了最后一个元素,并且仅在情况并非如此的情况下才添加下一个元素:

def comb_no_adj(lst, num, last=False):
    if num == 0:
        yield []
    if 0 < num <= len(lst):
        first, *rest = lst
        if not last:
            for c in comb_no_adj(rest, num-1, True):
                yield [first] + c
        for c in comb_no_adj(rest, num, False):
            yield c

comb_no_adj([1,2,3,4,5,6], 3)的示例组合为[1, 3, 5], [1, 3, 6], [1, 4, 6], [2, 4, 6](此示例包含重复项,只是为了易于理解;因为该算法未使用{{ 1}},重复元素不是问题。)


更新:实际上,首先生成 all 个组合,然后过滤无效的不起作用。请考虑以下示例:index。具有两个元素的所有组合均为[1,1,1](第一和第二,第一和第三,第二和第三[1,1], [1,1], [1,1])。您将如何决定保留哪些和丢弃哪些?对于1,情况变得更糟。 (虽然您可以生成所有元素-索引对组合,然后对其进行过滤,但是由于大量组合将被过滤掉,所以效率仍然较低。)