Python 满足特定条件的列表列表的所有组合

Python 满足特定条件的列表列表的所有组合,python,Python,我希望得到列表的所有可能组合,但有一些特定条件。 必须满足的条件: 我假设输入列表中的所有子列表都具有相同的长度N 我只能选择每列的特定金额 输出应仅包含大小N 每行只能选择一个值(列) 例如: # Columns 0 1 2 lst = [[1,2,3], [4,5,6], [7,8,9]] choose = [2,1,0] # here choose only two from first column and one from middle col

我希望得到列表的所有可能组合,但有一些特定条件。 必须满足的条件:

  • 我假设输入列表中的所有子列表都具有相同的长度
    N
  • 我只能选择每列的特定金额
  • 输出应仅包含大小
    N
  • 每行只能选择一个值(列)
  • 例如:

    # Columns 0 1 2
    lst  =  [[1,2,3],
             [4,5,6],
             [7,8,9]]
    
    choose = [2,1,0] # here choose only two from first column and one from middle column
    
    为了帮助跟踪列,我修改了输入列表并将每个项封装在一个对象中(值、列索引)

    我有一个适用于较小列表的解决方案,但我的目标是在20x12矩阵上运行它。有什么可行的方法来解决这个问题吗

    我当前的解决方案:

    class Encapsulation:
        def __init__(self, value, column_index):
            self.value = value
            self.column_index = column_index
    
        def __repr__(self):
            return "(%d, %d)" % (self.value, self.column_index)
    
    
    def combine(L, choose):
        combinations = []
    
        choose_n = len(choose)
    
        def _combine(terms, accum):
            last = (len(terms) == 1)
            n = len(terms[0])
            for i in range(n):
                item = accum + [terms[0][i]]
                if last:
                    if can_choose(item, choose, choose_n):
                        combinations.append(item)
                else:
                    if can_choose(item, choose, choose_n):
                        _combine(terms[1:], item)
    
        _combine(L, [])
        return combinations
    
    def encapsulate(lst):
        outlist = []
        for j in range(0, len(lst)):
            new_l = []
            for i in range(0, len(lst[j])):
                new_l.append(Encapsulation(lst[j][i], i))
            outlist.append(new_l)
        return outlist
    
    def can_choose(l, _choose, n):
        counts = [0]*n
    
        for _item in l:
            counts[_item.column_index] += 1
            if counts[_item.column_index] > _choose[_item.column_index]:
                return False
    
        return True
    
    
    if __name__ == "__main__":
        lst = [[1,2,3],
               [4,5,6],
               [7,8,9]]
    
        choose = [2,1,0]
    
        lst = encapsulate(lst)
        assert(sum(choose) == len(lst) and len(choose) == len(lst[0]))
        combinations = combine(lst, choose)
        print(combinations)
    
    编辑1: 我设法使实现稍微快一点。但对于大型矩阵仍然不可行

    def product(*args, **kwds):
        "Alternative fast implementation of product for python < 2.6"
        choose = kwds.get('choose', [])
        choose_n = len(choose)
    
        def cycle(values, uplevel):
            for prefix in uplevel:       # cycle through all upper levels
                counts = [0]*choose_n
    
                for _item in prefix:
                    counts[_item.column_index] += 1
    
                for current in values:   # restart iteration of current level
                    i = current.column_index
                    if counts[i]+1 <= choose[i]:
                        yield prefix + (current,)
    
        stack = iter(((),))
        for level in tuple(map(tuple, args)):
            stack = cycle(level, stack)  # build stack of iterators
        return stack
    

    这里是一个使用
    itertools
    的解决方案,它将生成可能的输出,而不需要每个解决方案的可能排列。如果您也需要排列,只需生成每个输出的所有排列即可

    我首先构建这些列,然后构建一个生成器,其中包含来自每个列的请求数量的值的所有可能组合。最后,我们制作这些组合的产品:

    from itertools import combinations, product, chain
    
    lst  =  [[1,2,3],
             [4,5,6],
             [7,8,9]]
    
    choose = [2,1,0]
    
    
    columns = list(zip(*lst))
    # [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
    
    if sum(choose) != len(columns[0]):
        raise ValueError(f'You must choose {len(columns[0])} values')
    
    combinations = [combinations(columns[index], r=number) for index, number in enumerate(choose)]
    
    for comb in product(*combinations):
        out = list(chain.from_iterable(comb))
        print(out)
    
    输出:

    [1, 4, 2]
    [1, 4, 5]
    [1, 4, 8]
    [1, 7, 2]
    [1, 7, 5]
    [1, 7, 8]
    [4, 7, 2]
    [4, 7, 5]
    [4, 7, 8]
    

    在第一列中从3个元素中选择2个元素的逻辑是什么?也就是说,你似乎不是随机选择的?如果你能描述一下,那会很有帮助。我把它归结为一个简单的例子,但最初的问题是我有一个有技能的人(行)的列表(列)。每个人在每项技能中都有一个技能等级。例如,我想选择两个人,第一个技能,一个中等技能。最终,如果我为每项技能选择一组预先确定的人员,我希望找到最佳组合,以最大限度地提高技能水平。我忘了提到,每行只能选择一个值。例如,你的第一个结果是无效的,因为它包含第一行的1和2。是的,我意识到了。到现在为止,我已经花了相当长的时间在这个问题上,我没有注意到它不够具体。谢谢你的帮助。即使您的解决方案返回的输出列表要大得多,但它似乎比我的解决方案运行得快得多。如果我可以减少它只考虑每行一列,那么也许可以处理(20行×12列)矩阵。否则,组合的数量就太多了。
    from itertools import combinations, product, chain
    
    lst  =  [[1,2,3],
             [4,5,6],
             [7,8,9]]
    
    choose = [2,1,0]
    
    
    columns = list(zip(*lst))
    # [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
    
    if sum(choose) != len(columns[0]):
        raise ValueError(f'You must choose {len(columns[0])} values')
    
    combinations = [combinations(columns[index], r=number) for index, number in enumerate(choose)]
    
    for comb in product(*combinations):
        out = list(chain.from_iterable(comb))
        print(out)
    
    [1, 4, 2]
    [1, 4, 5]
    [1, 4, 8]
    [1, 7, 2]
    [1, 7, 5]
    [1, 7, 8]
    [4, 7, 2]
    [4, 7, 5]
    [4, 7, 8]