简体   繁体   English

如何根据Python中的一个布尔字段保持顺序创建元组列表的所有排列

[英]How to create all permutations of a list of tuples depending on one boolean field keeping order in Python

Let's say i have an input as a list of tuples like this: 假设我有一个输入作为这样的元组列表:

[('a', True),
 ('b', False),
 ('c', True),
 ('d', False)] 

Every tuple which has True as second parameter is considered optional. 每个具有True作为第二个参数的元组都被认为是可选的。

  • amount of tuples in the list is arbitrary 列表中的元组数量是任意的
  • the value of the first value is arbitrary and must be preserved 第一个值的值是任意的,必须保留

Now I want to permutate this struct the following way: 现在,我想通过以下方式置换此结构:

  • the output should be a list of list of tuples 输出应为元组列表的列表
  • every list of tuples is unique 每个元组列表都是唯一的
  • the lists differenciates on the optional tuples which are either there or not 列表在存在或不存在的可选元组上有所区别
  • tuples which are False don't change (disappear) False元组不变(消失)
  • ordering of the tuples inside the lists does not change 列表内元组的顺序不变
  • ordering of the tuples does not change 元组的顺序不变
  • lists of tuples can be in any order 元组列表可以按任何顺序排列

The output of the example above should therefore look like this: 因此,以上示例的输出应如下所示:

[[('a', True),
  ('b', False),
  ('c', True),
  ('d', False)],

 [('b', False),
  ('c', True),
  ('d', False)],

 [('a', True),
  ('b', False),
  ('d', False)],

 [('b', False),
  ('d', False)]]

Any thoughts how to solve this one in an elegant way? 有什么想法如何以一种优雅的方式解决这个问题? I tried with recursion but I could not pull it off. 我尝试递归,但无法完成。

I'm not sure there is a particularly elegant way. 我不确定是否有特别优雅的方法。 Conceptually, you need to compute the power set of the optional elements, but merge it with the non-optional elements in a way that fulfils your requirements. 从概念上讲,您需要计算可选元素的功效集,然后以满足您要求的方式将其与非可选元素合并。 Here is one way: 这是一种方法:

import itertools
a = [('a', True), ('b', False), ('c', True), ('d', False)]
optional_count = sum(optional for x, optional in a)
for include in itertools.product([True, False], repeat=optional_count):
    include_iter = iter(include)
    print([
        (x, optional)
        for x, optional in a
        if not optional or next(include_iter)
    ])

printing 印花

[('a', True), ('b', False), ('c', True), ('d', False)]
[('a', True), ('b', False), ('d', False)]
[('b', False), ('c', True), ('d', False)]
[('b', False), ('d', False)]

The loop iterates over all tuples indicating whether to include the optional elements: 该循环遍历所有元组,指示是否包括可选元素:

True, True
True, False
False, True
False, False

The list comprehension in the print statement includes all non-optional elements, and for the optional ones looks at the next available element from include . 打印语句中的列表理解包括所有非可选元素,对于可选元素,请查看include中的下一个可用元素。

There is actually a nice recursive solution I just thought of: 实际上,我刚刚想到了一个不错的递归解决方案:

def choices(a):
    if not a:
        yield []
        return
    head, *tail = a
    if head[1]:
        yield from choices(tail)
    for tail_choice in choices(tail):
        yield [head] + tail_choice

This creates a lazy generator over all lists of tuples: 这会在所有元组列表上创建一个惰性生成器:

>>> list(choices(a))
[[('b', False), ('d', False)],
 [('b', False), ('c', True), ('d', False)],
 [('a', True), ('b', False), ('d', False)],
 [('a', True), ('b', False), ('c', True), ('d', False)]]

You could make choices by making a copy of the list of so far made choices and append the new value to the copy and afterwards merging it with the list of so far made choices. 您可以通过复制到目前为止的选择列表来做出选择,然后将新值附加到副本中,然后将其与到目前为止的选择列表合并。

Sorry for that horrible explanation, but I can't come up with anything better right now. 很抱歉得到这个可怕的解释,但是我现在无法提出更好的建议。 Feel free to edit, if you can come up with anything better. 如果可以提出更好的建议,请随时进行编辑。 Otherwise I hope, below diagram and code do a better job at explaining my solution. 否则,我希望下面的图表和代码可以更好地解释我的解决方案。

                                        []
                    -------------------------------------------
                   []                                        [0]
       ----------------------------                ---------------------------
       []                       [1]                [0]                  [0, 1]
       ...                      ...                ...                  ...

The basic idea is to iterate over ever item, clone all partial solutions and append the item to the cloned solutions. 基本思想是遍历所有项目,克隆所有部分解决方案,然后将项目追加到克隆的解决方案中。

def choose(arr):
    res = [[]]

    for t in arr:
        if t[1]:
            # make a copy of all found solutions
            clone = [list(c) for c in res]

            # append the new value to the original solutions
            for l in res:
                l.append(t)

            # merge solution-list with the list of copies
            res += clone
        else:
            # non-optional element => just add the element to all solutions
            for l in res:
                l.append(t)

    return res

Output: 输出:

print('\n'.join(str(l) for l in choose([('a', True), ('b', False), ('c', True), ('d', False)])

[('a', True), ('b', False), ('c', True), ('d', False)] [('a',True),('b',False),('c',True),('d',False)]
[('b', False), ('c', True), ('d', False)] [('b',False),('c',True),('d',False)]
[('a', True), ('b', False), ('d', False)] [('a',True),('b',False),('d',False)]
[('b', False), ('d', False)] [('b',False),('d',False)]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM