【问题标题】:Find all possible sublists of a list查找列表的所有可能子列表
【发布时间】:2013-06-12 09:08:56
【问题描述】:

假设我有以下列表

[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]

我想找到一定长度的所有可能的子列表,其中它们不包含某个特定数字并且不会丢失数字的顺序。

例如,长度为 6 而没有长度为 12 的所有可能的子列表是:

[1,2,3,4,5,6]
[2,3,4,5,6,7]
[3,4,5,6,7,8]
[4,5,6,7,8,9]
[5,6,7,8,9,10]
[6,7,8,9,10,11]
[13,14,15,16,17,18]

问题是我想在一个非常大的列表中完成它并且我想要最快的方法。

用我的方法更新:

oldlist = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]
newlist = []
length = 6
exclude = 12
for i in oldlist:
   if length+i>len(oldlist):
       break
   else:
       mylist.append(oldlist[i:(i+length)]
for i in newlist:
    if exclude in i:
       newlist.remove(i)

我知道这不是最好的方法,这就是为什么我需要更好的方法。

【问题讨论】:

标签: python


【解决方案1】:

一个简单的、未优化的解决方案是

result = [sublist for sublist in 
        (lst[x:x+size] for x in range(len(lst) - size + 1))
        if item not in sublist
    ]

优化版本:

result = []
start = 0
while start < len(lst):
    try:
        end = lst.index(item, start + 1)
    except ValueError:
        end = len(lst)
    result.extend(lst[x+start:x+start+size] for x in range(end - start - size + 1))
    start = end + 1

【讨论】:

  • 无论如何你还能优化多少:) “滑动窗口”解决方案正是这里所需要的,恕我直言。 +1。
  • 这里的item 是什么?第二个版本对我不起作用:NameError: name 'item' is not defined
  • 第一个版本也不起作用,因为 item 未定义。
  • 这两种解决方案中的size 是什么。这也没有定义。
【解决方案2】:

使用itertools.combinations:

import itertools
mylist = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]
def contains_sublist(lst, sublst):
    n = len(sublst)
    return any((sublst == lst[i:i+n]) for i in xrange(len(lst)-n+1))
print [i for i in itertools.combinations(mylist,6) if 12 not in i and contains_sublist(mylist, list(i))]

打印:

[(1, 2, 3, 4, 5, 6), (2, 3, 4, 5, 6, 7), (3, 4, 5, 6, 7, 8), (4, 5, 6, 7, 8, 9), (5, 6, 7, 8, 9, 10), (6, 7, 8, 9, 10, 11), (13, 14, 15, 16, 17, 18)]

【讨论】:

  • 这是一个不错的答案,但是转换为字符串使得无法使用列表中没有__str____repr__ 方法的任何对象。
  • 我不想失去我的号码的顺序。我想成为一个继续子列表。例如 (1, 2, 13, 14, 15, 16) 不是我需要的。我在 cmets 上添加了我的方法,但我认为这不是最好的方法
  • 我认为生成大量未使用的组合(全部包含 12 个)并过滤掉它们并不是最快的方法。相反,应该有一个没有 12 的列表的副本被处理,或者从一个少一个元素的列表到所需元素的某种映射(例如,通过将 1 添加到所有结果数字 >= 12)
  • @StoryTeller 糟糕,我刚从 [stackoverflow.com/questions/3313590/… 得到它。我现在拥有的(也来自问题)应该可以工作:)。
【解决方案3】:

我能想到的最简单的方法是从列表中删除排除的数字,然后使用itertools.combinations() 生成所需的子列表,这具有额外的优势,它将迭代地生成子列表。

from  itertools import combinations

def combos_with_exclusion(lst, exclude, length):
    for combo in combinations((e for e in lst if e != exclude), length):
        yield list(combo)

mylist = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]

for sublist in combos_with_exclusion(mylist, 12, 6):
    print sublist

输出:

[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 7]
[1, 2, 3, 4, 5, 8]
[1, 2, 3, 4, 5, 9]
[1, 2, 3, 4, 5, 10]
[1, 2, 3, 4, 5, 11]
[1, 2, 3, 4, 5, 13]
        ...
[11, 14, 15, 16, 17, 18]
[13, 14, 15, 16, 17, 18]

【讨论】:

    【解决方案4】:

    我喜欢用可组合的小部件来构建解决方案。几年编写 Haskell 对你来说是这样的。所以我会这样做......

    首先,这将返回一个遍历所有子列表的迭代器,按长度升序排列,从空列表开始:

    from itertools import chain, combinations
    
    def all_sublists(l):
        return chain(*(combinations(l, i) for i in range(len(l) + 1)))
    

    通常我们不鼓励使用单字母变量名,但我认为在高度抽象的代码的短时间内,这是完全合理的做法。

    (顺便说一句,要省略空列表,请改用range(1, len(l) + 1)。)

    然后我们可以通过添加您的标准来解决您的问题:

    def filtered_sublists(input_list, length, exclude):
        return (
            l for l in all_sublists(input_list)
            if len(l) == length and exclude not in l
        )
    

    例如:

    oldlist = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]
    length = 6
    exclude = 12
    newlist = filtered_sublists(old_list, length, exclude)
    

    【讨论】:

      【解决方案5】:

      我尝试递归创建所有可能的列表列表。 depth 参数只需要从每个列表中删除的项目数。这不是滑动窗口。

      代码:

      def sublists(input, depth):
          output= []
          if depth > 0:
              for i in range(0, len(input)):
                  sub= input[0:i] + input[i+1:]
                  output += [sub]
                  output.extend(sublists(sub, depth-1))
          return output
      

      示例(以交互方式输入 python3):

      sublists([1,2,3,4],1)

      [[2, 3, 4], [1, 3, 4], [1, 2, 4], [1, 2, 3]]

      sublists([1,2,3,4],2)

      [[2, 3, 4], [3, 4], [2, 4], [2, 3], [1, 3, 4], [3, 4], [1, 4], [1, 3], [1, 2, 4], [2, 4], [1, 4], [1, 2], [1, 2, 3], [2, 3], [1, 3 ], [1, 2]]

      sublists([1,2,3,4],3)

      [[2, 3, 4], [3, 4], [4], [3], [2, 4], [4], [2], [2, 3], [3], [2], [1, 3, 4], [3, 4], [4], [3], [1, 4], [4], [1], [1, 3], [3], [1], [1, 2, 4], [2, 4], [4], [2], [1, 4], [4], [1], [1, 2], [2], [1], [1, 2, 3], [2, 3], [3], [2], [1, 3], [3], [1], [1, 2], [2], [1]]

      一些极端情况:

      sublists([1,2,3,4],100)

      [[2, 3, 4], [3, 4], [4], [3], [2, 4], [4], [2], [2, 3], [3], [2], [1, 3, 4], [3, 4], [4], [3], [1, 4], [4], [1], [1, 3], [3], [1], [1, 2, 4], [2, 4], [4], [2], [1, 4], [4], [1], [1, 2], [2], [1], [1, 2, 3], [2, 3], [3], [2], [1, 3], [3], [1], [1, 2], [2], [1]]

      sublists([], 1)

      []

      注意:列表的输出列表包含重复项。

      【讨论】:

        【解决方案6】:

        我有一个答案,但我认为这不是最好的:

        oldlist = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]
        result = []
        def sub_list(lst):
            if len(lst) <= 1:
                result.append(tuple(lst))
                return
            else:
                result.append(tuple(lst))
            for i in lst:
                new_lst = lst[:]
                new_lst.remove(i)
                sub_list(new_lst)
        sub_list(oldlist)
        newlist = set(result)    # because it have very very very many the same
                                 # sublist so we need use set to remove these also 
                                 # use tuple above is also the reason 
        print newlist
        

        它会得到结果,但是因为它会有很多相同的子列表,所以它需要很多内存和很多时间。我认为这不是一个好方法。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-01
          • 2017-08-10
          • 1970-01-01
          • 1970-01-01
          • 2018-12-03
          • 2020-07-18
          相关资源
          最近更新 更多