我编写了以下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 来说,这种方法非常慢。为什么会这样?
答案 0 :(得分:4)
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
。