为什么我的迭代器实施效率很低?

时间:2019-03-01 22:19:35

标签: python itertools

我编写了以下python脚本,以计算无限字符串的前 n 个字符中字符(a)出现的次数。

from itertools import cycle
def count_a(str_, n):
    count = 0
    str_ = cycle(str_)
    for i in range(n):
        if next(str_) == 'a':
            count += 1
    return count

我对迭代器的理解是,它们应该是有效的,但是对于很大的 n 来说,这种方法非常慢。为什么会这样?

2 个答案:

答案 0 :(得分:4)

documentation说,

cycle迭代器的效率可能不如您想象的那样。
  

制作一个迭代器,从迭代器返回元素并保存一个   每个副本。

     

当迭代器用尽时,从保存的副本中返回元素。   无限重复

     

...请注意,该工具箱成员可能需要大量辅助工具   存储(取决于迭代对象的长度)。

为什么不简化而根本不使用迭代器?它增加了不必要的开销,并且没有任何好处。您可以使用简单的str_[:n].count('a')

轻松计算发生次数

答案 1 :(得分:0)

这里的第一个问题是,尽管使用了itertools,您仍在进行显式的python级for循环。为了在使用itertools时获得C级速度提升,您希望将所有迭代都保留在高速itertools中。

因此,让我们逐步进行此操作,首先,我们要获取有限字符串中的字符数。为此,可以使用itertools.islice方法获取字符串中的前n个字符:

str_first_n_chars = islice(cycle(str_), n)

接下来,您要计算字母(a)的出现次数,您可以对这两个字母中的任何一个进行一些变体(您可能想尝试哪种变体更快):

count_a = sum(1 for c in str_first_n_chars if c == 'a')
count_a = len(tuple(filter('a'.__eq__, str_first_n_chars))

这很好,但是对于很大的n来说仍然很慢,因为对于很大的str_,您需要多次遍历n,例如{ {1}}。换句话说,此算法为n = 10**10000


我们可以做的最后一个改进。请注意,O(n)中(a)的数目如何在每次迭代中都不会真正改变。与其对大型str_遍历str_多次,我们还可以通过一点数学来做一点更聪明,所以我们只需要遍历n两次。首先,我们计算str_的单个拉伸段中(a)的数量:

str_

然后,我们需要使用divmod函数来遍历count_a_single = str_.count('a') 以获得长度str_的次数:

n

然后我们可以将iter_count与count_a_single相乘,并在剩余长度中加上(a)的数量。我们这里不需要循环或等号,因为iter_count, remainder = divmod(n, len(str_))

remainder < len(str_)

使用此方法,算法的运行时性能仅在str_的单个周期的长度上增加,而不是count_a = iter_count * count_a_single + str_[:remainder].count('a') 。换句话说,此算法为n