【问题标题】:How can I remove a tuple from a list if the tuple contains two "1" next to each other in Python?如果元组在 Python 中包含两个相邻的“1”,如何从列表中删除元组?
【发布时间】:2020-03-05 22:30:04
【问题描述】:

我有一个元组列表:

from itertools import product
l1 = list((product((0,1), repeat = n))) 

对于 n=4,输出如下:

 [(0, 0, 0, 0),
 (0, 0, 0, 1),
 (0, 0, 1, 0),
 (0, 0, 1, 1),
 (0, 1, 0, 0),
 (0, 1, 0, 1),
 (0, 1, 1, 0),
 (0, 1, 1, 1),
 (1, 0, 0, 0),
 (1, 0, 0, 1),
 (1, 0, 1, 0),
 (1, 0, 1, 1),
 (1, 1, 0, 0),
 (1, 1, 0, 1),
 (1, 1, 1, 0),
 (1, 1, 1, 1)]

我想删除至少两个“1”相邻的元组,例如(0,1,1,0)

我试过这个:

for i in l1:
    for j in i:
         if j==1 and j+1 == 1:
                l1.remove(i)

我想这是行不通的,因为它需要 j+1 作为实际数字 + 1,就像如果 j=1 它需要 2 等等。

我应该做些什么不同的事情?

【问题讨论】:

标签: python for-loop tuples


【解决方案1】:

您可以使用zip(i, i[1:]) 构建邻居对并检查它们是否包含(1, 1)

>>> [i for i in l1 if (1, 1) not in zip(i, i[1:])]
[(0, 0, 0, 0), (0, 0, 0, 1), (0, 0, 1, 0), (0, 1, 0, 0),
 (0, 1, 0, 1), (1, 0, 0, 0), (1, 0, 0, 1), (1, 0, 1, 0)]

【讨论】:

    【解决方案2】:

    您的方法合乎逻辑,但不幸的是不适用于 Python(或大多数其他语言),并且实现不正确,因为 j==1 and j+1 == 1 永远不会正确;如果j 等于1,那么j+1 等于2,句号。但更大的问题是您在迭代列表时试图从列表中删除内容,which is a no-no

    首先让我们解决如何检查两个 1 是否相邻的问题。我们需要查看t[j]t[j+1],而不是查看jj+1,其中t 是元组之一。此外,j 需要迭代到长度减 1,否则t[j+1] 将在元组末尾给出IndexErrorany 函数是一种方便的方法:

    any(t[j] == t[j+1] == 1 for j in range(n - 1))
    

    请注意,t[j] == t[j+1] == 1 在 Python 中工作是因为 chained comparisons

    现在让我们解决如何从列表中删除这些元素的问题。简单地使用您想要的元素构建一个新列表,而不是删除您不想要的元素要方便得多。列表推导是一种方便的方法:

    result = [
        t for t in product((0, 1), repeat=n)
        if not any(t[j] == t[j+1] == 1 for j in range(n - 1))
    ]
    

    结果:

    [(0, 0, 0, 0),
     (0, 0, 0, 1),
     (0, 0, 1, 0),
     (0, 1, 0, 0),
     (0, 1, 0, 1),
     (1, 0, 0, 0),
     (1, 0, 0, 1),
     (1, 0, 1, 0)]
    

    综上所述,对于较大的 n,使用仅生成您想要的元素的算法会更有效,而不是遍历整个笛卡尔积(大小为 2n)拒绝你不想要的。

    【讨论】:

    • 你是对的,当然,虽然剩余元组的数量仍然是exponential,所以想必它们无论如何也不会很高。
    • 基于非拒绝的算法应该仍然是指数的,但基数较低(在本例中,1.618 而不是 2),因此改进也是指数因子。
    • 是的,没错。我觉得很有趣的是,另一个著名的东西突然出现了:-)
    【解决方案3】:

    这应该可以解决问题:

    >>> n = 4
    >>> from itertools import product
    >>> l1 = list((product((0,1), repeat = n))) 
    >>> l1
    [(0, 0, 0, 0), (0, 0, 0, 1), (0, 0, 1, 0), (0, 0, 1, 1), (0, 1, 0, 0), (0, 1, 0, 1), (0, 1, 1, 0), (0, 1, 1, 1), (1, 0, 0, 0), (1, 0, 0, 1), (1, 0, 1, 0), (1, 0, 1, 1), (1, 1, 0, 0), (1, 1, 0, 1), (1, 1, 1, 0), (1, 1, 1, 1)]
    >>> [t for t in l1 if not any(t[i] == 1 and t[i+1] == 1 for i in range(n-1))]
    [(0, 0, 0, 0), (0, 0, 0, 1), (0, 0, 1, 0), (0, 1, 0, 0), (0, 1, 0, 1), (1, 0, 0, 0), (1, 0, 0, 1), (1, 0, 1, 0)]
    

    not any(t[i] == 1 and t[i+1] == 1 for i in range(n-1)) 条件基本上检查连续元素是否等于一。注意range(n-1) 索引迭代器 - 我们不想检查第 n 个元素与第 n+1 个元素,因为在任何一个中都没有第五个元素上面的元组。

    时机

    请注意,我的函数速度大约是 that of Heap Overflow 的两倍。

    >>> from time import time as t
    >>> def f1():
    ...     t0 = t()
    ...     result = [i for i in l1 if (1, 1) not in zip(i, i[1:])]
    ...     t1 = t()
    ...     print(t1-t0)
    ... 
    >>> def f2():
    ...     t0 = t()
    ...     result = [t for t in l1 if not any(t[i] == 1 and t[i+1] == 1 for i in range(n-1))]
    ...     t1 = t()
    ...     print(t1-t0)
    ... 
    >>> l1 = list((product((0,1), repeat = n))) * 1000000
    >>> len(l1)
    16000000
    >>> f1()
    8.146391868591309
    >>> f2()
    18.645386934280396
    

    【讨论】:

      【解决方案4】:

      添加一个不那么“pythonic”但更具可读性的示例以供考虑:

      def filter_helper(t):
        for i, val in enumerate(t):
          if i != 0 and (t[i-1]) == val and val == 1:
            return False  
        return True
      
      print(list(filter(filter_helper, a)))
      

      其中a 是元组的原始列表。

      【讨论】:

      • @kaya3 很好,谢谢,添加了缺少的条件。
      【解决方案5】:

      也可以用forloop和python的any()方法来完成

      for x in l1:
          if any([(x[i]==x[i+1]==1) for i in x]) == True:
              l1.remove(x)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-10-09
        • 1970-01-01
        • 2016-09-25
        • 1970-01-01
        • 1970-01-01
        • 2017-07-10
        • 1970-01-01
        相关资源
        最近更新 更多