列表python中的计数列表出现次数

列表python中的计数列表出现次数,python,Python,我想数一数一个大列表中按特定顺序包含元素的次数。例如,如果我有元素[1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5],我想知道有多少次[1,2,3]彼此相邻(在这种情况下,答案是4) 我正在考虑检查数字“3”的索引(因此当前它将返回[2,7,12,17]。然后我会迭代该列表,在列表中描述的位置获取元素,并检查前面的两个位置。如果它们与“1”和“2”匹配,则添加1到计数器并继续查找。我相信此解决方案效率不高,也不好看,是否有更好的解决方案?对于int列表,您可以转换

我想数一数一个大列表中按特定顺序包含元素的次数。例如,如果我有元素
[1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5]
,我想知道有多少次
[1,2,3]
彼此相邻(在这种情况下,答案是
4


我正在考虑检查数字“3”的索引(因此当前它将返回
[2,7,12,17]
。然后我会迭代该列表,在列表中描述的位置获取元素,并检查前面的两个位置。如果它们与“1”和“2”匹配,则添加1到计数器并继续查找。我相信此解决方案效率不高,也不好看,是否有更好的解决方案?

对于int列表,您可以转换创建字符串,然后使用计数方法:

>>> x = [1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5]
>>> y = [1,2,3]
>>> s = ',' + ','.join(str(i) for i in x) + ','
>>> t = ',' + ','.join(str(i) for i in y) + ','
>>> s.count(t)
4
如果列表中的项包含包含逗号的字符串,则此方法可能会失败(正如@schwobasegll在注释中指出的)。您需要选择一个已知不会出现在任何字符串中的分隔符,或者采用一种完全不同的方法,该方法不会减少到string
count
方法


编辑时:我添加了@Rawing建议的修复程序,以解决@tobias_k指出的一个错误。这是一个比最初看起来更微妙的问题。

您可以迭代列表并比较子列表:

In [1]: lst = [1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5]
In [2]: sub = [1,2,3]
In [3]: [i for i, _ in enumerate(lst) if lst[i:i+len(sub)] == sub]
Out[3]: [0, 5, 10, 15]
但是,请注意,在一个非常大的列表和子列表上,这是非常浪费的,因为它会创建许多原始列表的切片来与子列表进行比较。在稍长的版本中,您可以使用
all
将列表的每个相关位置与子列表的位置进行比较:

In [5]: [i for i, _ in enumerate(lst) if all(lst[i+k] == e for k, e in enumerate(sub))]
Out[5]: [0, 5, 10, 15]

这是一个通用的解决方案,适用于任何大小的子序列和任何类型的元素。它也非常节省空间,因为它只在迭代器上运行

from itertools import islice

def count(lst, seq):
    it = zip(*(islice(lst, i, None) for i in range(len(seq))))
    seq = tuple(seq)
    return sum(x == seq for x in it)
其思想是在
lst
上创建宽度
len(seq)
的滑动窗口迭代器,并计算等于
tuple(seq)
的元组数。这意味着
count
也计算重叠匹配:

In [5]: count('aaa', 'aa')
Out[5]: 2

我觉得这是最长的常见子序列问题,每次都会重复,直到返回的序列是一个空列表

我认为在这种情况下,对于一个有效的算法,你能做的最好的事情是O(n*m),其中n是你的大列表中的元素数,m是你的小列表中的元素数。当然,你必须有一个额外的步骤,从大序列中删除小序列并重复这个过程

以下是算法:

  • 查找lcs(大列表、小列表)
  • 从大列表中删除第一个出现的小列表
  • 重复此操作,直到lcs为空列表
  • 返回迭代次数
  • 下面是我用python编写的lcs的一个实现:

    def lcs(first, second):
        results = dict()
        return lcs_mem(first, second, results)
    
    def lcs_mem(first, second, results):
        key = ""
        if first > second:
            key = first + "," + second
        else:
            key = second + "," + first
    
        if len(first) == 0 or len(second) == 0:
            return ''
        elif key in results:
            return results[key]
        elif first[-1] == second[-1]:
            result = lcs(first[:-1], second[:-1]) + first[-1]
            results[key] = result
            return result
        else:
            lcsLeft = lcs(first[:-1], second)
            lcsRight = lcs(first, second[:-1])
    
            if len(lcsLeft) > len(lcsRight):
                return lcsLeft
            else:
                return lcsRight
    
    def main():
        pass
    
    
    if __name__ == '__main__':
        main()
    

    请随意修改以上算法。

    可以从复杂性定义解决方案的效率。在谷歌搜索更多关于算法复杂性的信息

    x = [1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5]
    y = [1,2,3]
    
    count = 0
    for i in range(len(x)-len(y)):
        if x[i:i+len(y)] == y:
            count += 1
    print(count)
    
    在你们的例子中,复杂性是2n,其中n是元素的数量

    这是复杂度为n的解决方案,因为它只遍历列表一次,即n次

    def IsSameError(x,y):
        if (len(x) != len(y)):
            return False
    
        i = 0
        while (i < len(y)):
            if(x[i] != y[i]):
                return False
            i += 1
    
        return True
    
    x = [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
    y = [1, 2, 3]
    
    xLength = len(x)
    yLength = len(y)
    cnt = 0
    answer = []
    while (cnt+3 < xLength):
        if(IsSameError([x[cnt], x[cnt+1], x[cnt+2]], y)):
            answer.append(x[cnt])
            answer.append(x[cnt+1])
            answer.append(x[cnt + 2])
            cnt = cnt + 3
        else:
            cnt = cnt + 1
    
    
    print answer
    
    def ISSAMERROR(x,y):
    如果(len(x)!=len(y)):
    返回错误
    i=0
    而(i
    旋转
    [12,3]
    [1,23]
    进入同一个实体。如果我的列表有约600万个元素,它会在正常时间运行吗?@schwobasegll我从未声称我的解决方案是计算任意子列表的通用方法。这是一种完全足够的快速和肮脏的方法,可以覆盖大多数情况,包括问题中的示例。不过,你提出了一个很好的解决方案要点。在字符串列表的情况下,必须注意分隔符的选择。即使对于
    int
    列表,如果搜索模式为
    1,2,3
    ,并且列表包含例如
    11,2,33
    @JohnColeman,这也不是一个致命的问题。您只需要在st的开头和结尾加逗号即可戒指。由于OP的列表似乎有约600万个元素,使用
    islice
    @Rawing可能是一个好主意。是的,同意。它可能有较低的复杂性,但很难说没有基准测试它会更好。我的推理是在Python中,循环以c级速度运行,而循环则不以c级速度运行。这意味着使用Python中的for循环速度更快。同样,您正在使用附加程序创建内存分配,这也是一个繁重的过程。因此,在这种情况下,复杂度的折衷可能不值得。我不知道for循环和while循环之间的区别。请您给出慢的原因。如果您将我的答案与另一个答案是,我可以跳过附加部分,我们也可以用for循环替换while循环。但是,是的,知识非常丰富。你也需要计算重叠匹配吗?例如,
    [1,1]
    包含在
    [1,1,1]
    中多少次?不,在我的情况下没有重叠匹配。
    def IsSameError(x,y):
        if (len(x) != len(y)):
            return False
    
        i = 0
        while (i < len(y)):
            if(x[i] != y[i]):
                return False
            i += 1
    
        return True
    
    x = [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
    y = [1, 2, 3]
    
    xLength = len(x)
    yLength = len(y)
    cnt = 0
    answer = []
    while (cnt+3 < xLength):
        if(IsSameError([x[cnt], x[cnt+1], x[cnt+2]], y)):
            answer.append(x[cnt])
            answer.append(x[cnt+1])
            answer.append(x[cnt + 2])
            cnt = cnt + 3
        else:
            cnt = cnt + 1
    
    
    print answer