【问题标题】:How to create all permutations of a list of tuples depending on one boolean field keeping order in Python如何根据一个布尔字段在Python中保持顺序创建元组列表的所有排列
【发布时间】:2019-03-31 13:23:07
【问题描述】:

假设我有一个像这样的元组列表的输入:

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

每个以True 作为第二个参数的元组都被认为是可选的。

  • 列表中元组的数量是任意的
  • 第一个值的值是任意的,必须保留

现在我想通过以下方式排列这个结构:

  • 输出应该是一个元组列表
  • 每个元组列表都是唯一的
  • 列表区分是否存在的可选元组
  • False 的元组不会改变(消失)
  • 列表中元组的顺序不会改变
  • 元组的顺序没有改变
  • 元组列表可以按任意顺序排列

因此,上述示例的输出应如下所示:

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

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

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

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

任何想法如何以优雅的方式解决这个问题?我尝试了递归,但我无法成功。

【问题讨论】:

    标签: python algorithm list recursion tuples


    【解决方案1】:

    我不确定是否有特别优雅的方式。从概念上讲,您需要计算可选元素的幂集,但以满足您要求的方式将其与非可选元素合并。这是一种方法:

    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)
        ])
    

    打印

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

    循环遍历所有元组,指示是否包含可选元素:

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

    打印语句中的列表推导包括所有非可选元素,对于可选元素,请查看来自include 的下一个可用元素。

    【讨论】:

      【解决方案2】:

      您可以通过制作目前已做出选择列表的副本并将新值附加到副本中然后将其与迄今为止做出的选择列表合并来做出选择。

      很抱歉,这个可怕的解释,但我现在想不出更好的办法。随意编辑,如果你能想出更好的东西。否则我希望下面的图表和代码能更好地解释我的解决方案。

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

      基本思想是迭代所有项目,克隆所有部分解决方案并将项目附加到克隆的解决方案。

      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
      

      输出:

      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)]
      [('b', False), ('c', True), ('d', False)]
      [('a', True), ('b', False), ('d', False)]
      [('b', False), ('d', False)]

      【讨论】:

        【解决方案3】:

        其实我刚刚想到了一个不错的递归解决方案:

        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
        

        这会在所有元组列表上创建一个惰性生成器:

        >>> 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)]]
        

        【讨论】:

        • 是的,这与我同时提出的解决方案相似。不知道yield from 运算符。我也会发布我的。也许我们可以在这里讨论什么是实际上最好的解决方案,因为他们都解决了它。不过我真的很喜欢递归。
        • 我的第一个答案中的迭代解决方案是最有效的解决方案,因为它不会不必要地复制任何列表。但是性能对于您的用例可能并不重要,因此请选择您认为最易读的内容。
        • Ich komme übrigens aus der Nähe von Neumünster。 Da fragt man in die weite Welt hinein und bekommt die Antwort von jemandem aus Preetz。 :)
        • @systderr Kleine Welt :)
        • 不必要地计算 choices(tail) 两次 - 否则很好回答
        猜你喜欢
        • 1970-01-01
        • 2022-01-24
        • 1970-01-01
        • 2020-06-30
        • 1970-01-01
        • 1970-01-01
        • 2012-09-01
        • 1970-01-01
        • 2015-12-20
        相关资源
        最近更新 更多