【问题标题】:Count all sequences in a list计算列表中的所有序列
【发布时间】:2017-09-10 22:37:49
【问题描述】:

我的自学任务是找出列表中有多少个序列。序列是一组数字,其中每个数字都比前一个数字大 1。所以,在列表中:

[1,2,3,5,8,10,12,13,14,15,17,19,21,23,24,25,26]

有3个序列:

  • 1,2,3
  • 12,13,14,15
  • 23,24,25,26

我花了几个小时得到了一个解决方案,我认为这是一种解决方法,而不是真正的解决方案。

我的解决方案是有一个单独的列表来添加序列并计算更新此列表的尝试。我计算了第一个附加,以及除了已经存在的序列之外的每个新附加。

我相信有一个无需额外列表的解决方案,它允许对序列本身进行计数,而不是对列表操作尝试进行计数。

numbers = [1,2,3,5,8,10,12,13,14,15,17,19,21,23,24,25,26]

goods = []
count = 0

for i in range(len(numbers)-1):
    if numbers[i] + 1 == numbers[i+1]:

        if goods == []:
            goods.append(numbers[i])
            count = count + 1

        elif numbers[i] != goods[-1]:
            goods.append(numbers[i])
            count = count + 1

        if numbers[i+1] != goods[-1]:
            goods.append(numbers[i+1])

我的调试输出:

Number 1 added to: [1]
First count change: 1

Number 12 added to: [1, 2, 3, 12]
Normal count change: 2

Number 23 added to: [1, 2, 3, 12, 13, 14, 15, 23]
Normal count change: 3

【问题讨论】:

  • 提示:为什么不尝试让goods(这个变量名是什么?)包含一个列表列表。例如。对于[1,2,3,5,8,10,12,13,14,15,17,19,21,23,24,25,26] 的输入,它最终将包含[[1,2,3,5], [8], [10], [12,13,14,15], 17, 19, 21, [23,24,25,26]]。从那里,您可以过滤所有大小为 1 的列表,以获取 [[1,2,3,5], [12,13,14,15], [23,24,25,26]],其 len 是您正在寻找的 3
  • 感谢您的建议。我仍然不确定如何识别何时开始新列表。如果我只知道怎么做,我可能会找到完全避免“商品”清单的方法。附言当我试图计算序列中的中断时,“Goods”是一个遗留词,而不是“Bads”。
  • 假设你有一个result 列表,它的元素是子列表,我们可以称之为run。您可能有一个活动运行(您当前正在处理的那个),当您注意到下一个元素不属于它时,您会终止它(例如,它不比活动运行的最后一个元素多一个)。此时,您将此运行添加到您的结果中,并将活动运行重置为一个新的空列表,并继续构建它,直到找到下一个不连续性

标签: python dynamic-programming


【解决方案1】:

感谢大家的帮助!

Legman 建议了我未能实施的原始解决方案,然后我最终在这篇文章中提出了另一个解决方案。

MSeifert 帮助找到了使用列表的正确方法:

numbers = [1,2,3,5,8,10,12,13,14,15,17,19,21,23,24,25,26]
print("Numbers:", numbers)

goods = []
count = 0

for i in range(len(numbers)-1):
    if numbers[i] + 1 == numbers[i+1]:
        if goods == []:
            goods.append([numbers[i]])
            count = count + 1
        elif numbers[i] != goods[-1][-1]:
            goods.append([numbers[i]])
            count = count + 1
        if numbers[i+1] != goods[-1]:
            goods[-1].extend([numbers[i+1]])

print("Sequences:", goods)
print("Number of sequences:", len(goods))

【讨论】:

    【解决方案2】:

    一种方法是迭代成对元素:

    l = [1,2,3,5,8,10,12,13,14,15,17,19,21,23,24,25,26]
    
    res = [[]]
    for item1, item2 in zip(l, l[1:]):  # pairwise iteration
        if item2 - item1 == 1:
            # The difference is 1, if we're at the beginning of a sequence add both
            # to the result, otherwise just the second one (the first one is already
            # included because of the previous iteration).
            if not res[-1]:  # index -1 means "last element".
                res[-1].extend((item1, item2))
            else:
                res[-1].append(item2)
        elif res[-1]: 
            # The difference isn't 1 so add a new empty list in case it just ended a sequence.
            res.append([])
    
    # In case "l" doesn't end with a "sequence" one needs to remove the trailing empty list.
    if not res[-1]:
        del res[-1]
    
    >>> res
    [[1, 2, 3], [12, 13, 14, 15], [23, 24, 25, 26]]
    
    >>> len(res)  # the amount of these sequences
    3
    

    与上述方法相比,没有zip 的解决方案只需要进行小的更改(循环和循环的开头):

    l = [1,2,3,5,8,10,12,13,14,15,17,19,21,23,24,25,26]
    
    res = [[]]
    for idx in range(1, len(l)):
        item1 = l[idx-1]
        item2 = l[idx]
        if item2 - item1 == 1:
            if not res[-1]:
                res[-1].extend((item1, item2))
            else:
                res[-1].append(item2)
        elif res[-1]: 
            res.append([])
    if not res[-1]:
        del res[-1]
    

    【讨论】:

    • 我想避免使用 zip() 并自己编写更多代码以供学习。感谢您使用扩展与附加的想法。在原始代码中,我通过扩展替换了最后一个附加,得到的结果是 [[1], 2, 3, [12], 13, 14, 15, [23]]。如何让它看起来像你的输出?
    • @Sam 你需要append 到最后一个子列表goods[-1].append(...)(注意-1)才能使它工作。 :(
    • @Sam zip 在这里很方便。如果您遍历索引for idx in range(1, len(l))(而不是zip)和循环内的索引item1 = l[idx-1]item2 = l[idx],它也应该可以工作。 :)
    • 我很高兴在你写之前几秒钟自己找到了解决方案!我还需要调整语法并添加一个列表而不是元素并避免重复。我会更新我的帖子,并将您的答案也标记为解决方案。
    • 我已经更新了我的帖子,你能看一下吗?我得到了我需要的结果,但也许我错过了一些我看不到的检查或条件。
    【解决方案3】:

    取自 python itertools 文档,如 here 所示,您可以使用 itemgettergroupby 仅使用一个列表来执行此操作,如下所示:

    >>> from itertools import groupby
    >>> from operator import itemgetter
    >>>
    >>> l = [1, 2, 3, 5, 8, 10, 12, 13, 14, 15, 17, 19, 21, 23, 24, 25, 26]
    >>>
    >>> counter = 0
    >>> for k, g in groupby(enumerate(l), lambda (i,x):i-x):
    ...     seq = map(itemgetter(1), g)
    ...     if len(seq)>1:
    ...         print seq
    ...         counter+=1
    ...
    [1, 2, 3]
    [12, 13, 14, 15]
    [23, 24, 25, 26]
    >>> counter
    3
    

    注意:正如@MSeifert 正确提到的,签名中的元组解包只能在 Python 2 中进行,而在 Python 3 中会失败 - 所以这是 python 2.x 解决方案。

    【讨论】:

    • 只有在 Python 2 中才能在签名中解包元组。在 Python 3 上会失败 (SyntaxError: invalid syntax)。
    • 虽然我会在现实世界中使用这样的解决方案,但它绕过了这样的练习的大部分要点
    • 谢谢!学习 itertools 很好。正如亚历山大所说,对于这项任务,我想要一个更“动手”的解决方案。
    【解决方案4】:

    这可以通过动态规划来解决。如果您只想知道序列的数量并且实际上不需要知道序列是什么,那么您应该能够只用几个变量来做到这一点。实际上,当您浏览列表时,您只需要知道您当前是否在一个序列中,如果不是,下一个是否增加 1 使其成为序列的开头,如果是,下一个大于1 使其成为序列的出口。之后,您只需要确保在列表末尾之前的一个单元格结束循环,因为最后一个单元格不能自行形成序列,因此在执行检查时不会导致错误。下面是示例代码

    isSeq=false
    
    for i in range(len(numbers)-1):
        if isSeq==false:
           if numbers[i]+1==numbers[i+1]:
               isSeq=true  
               count=count+1
        elif 
           if numbers[i]+1!=numbers[i+1]:
               isSeq=false              
    

    这里是动态编程教程的链接。

    https://www.codechef.com/wiki/tutorial-dynamic-programming

    【讨论】:

    • 这正是我一开始的想法!我不知道如何实现它,所以我用那个额外的列表遵循了错误的方向......虽然,我还不确定如何用这些列表来解决任务,但我对你的建议很满意。跨度>
    猜你喜欢
    • 1970-01-01
    • 2019-08-04
    • 2018-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 1970-01-01
    相关资源
    最近更新 更多