【问题标题】:Time complexity of python string iteration with for inpython字符串迭代的时间复杂度与for in
【发布时间】:2020-04-28 16:19:07
【问题描述】:

Python的for in用字符串迭代构造的时间复杂度是多少?

例如,

for s in 'test':
   ...  # s = 't', 'e', 's', 't'

循环的总运行时间是多少?

编辑:我发现我将 Python 的字符串切片查找与字符串迭代混淆了。它的索引查找是 O(1) 并且它在 O(1) 处迭代,所以总循环应该是 O(n),与列表相同。

【问题讨论】:

  • "单个索引在字符串中的查找时间为O(n)"。字符串索引should be O(1)
  • 我明白了,谢谢。然后循环是O(n)。对 Python 字符串的不变性感到困惑。
  • @NickParsons 您只是在评论那句话,还是您认为这与问题相关?我不认为它是。这不是索引循环。即使索引不是 O(1),因为例如 Python 使用可变大小的字符或字符的链接列表,iterator 仍然可以在 O(n) 中遍历字符串,因为它记得在哪里它在。
  • @HeapOverflow 这只是一般的观察。我不是 python 专家,所以我不知道字符串的迭代器是如何实现的。但是,如果迭代器的下一个方法是使用索引来获取下一个值并且索引循环需要 O(n),那么我会想象 t.c 是 O(n^2)。
  • @NickParsons 如果您对迭代器的实现感兴趣,请参阅我的答案的新部分“返回字符串迭代的时间复杂度”。总结:它确实使用了索引,但如果不是 O(1),我敢肯定迭代器会使用其他东西而不是 O(1),因为它具有保持其状态的优势。所以整个迭代仍然是O(n)。但是,是的,if 索引查找需要 O(n) 并且 迭代器使用它,迭代将需要 O(n^2)。尽管要做到这一点,Python 的实现者需要完全是菜鸟:-)

标签: python string loops iterator time-complexity


【解决方案1】:

它是 O(n),但索引查找参数是一个红鲱鱼。

迭代的工作原理

如果您这样做,索引查找速度会很重要:

for index in range(len(mystring)):
    char = mystring[index]
    ...

但是您没有使用索引。您正在使用一个 iterator,更准确地说是一个 string 迭代器:

>>> iter('test')
<str_iterator object at 0x03569820>

那个迭代器记住它在字符串中的位置(不管它喜欢什么,不需要是一个“索引”)。并且可以反复询问下一个字符:

>>> it = iter('test')
>>> next(it)
't'
>>> next(it)
'e'
>>> next(it)
's'
>>> next(it)
't'
>>> next(it)
Traceback (most recent call last):
  File "<pyshell#200>", line 1, in <module>
    next(it)
StopIteration

这就是for-loop 的作用。它创建该迭代器,然后反复询问它的下一个值,直到迭代器告诉它停止。它从迭代器中获得的每个值,都会在您命名的变量中提供给您的代码。换句话说,for-loop 实际上只是迭代器和循环体中的代码之间的中间人。

与字符串相比,想象一个简单的链表。链表中的索引查找需要 O(n),因为每次查找都需要从链表的开头步行到所需的节点。但是您仍然可以轻松地在 O(n) 中进行完整的迭代,对吧?并且迭代器对象将保留对下一个节点的引用,因此它将在 O(1) 时间内提供它(然后将其引用向前移动)。因此,对于链表,使用索引的for-loop 将花费 O(n2) 但普通的 pythonic for-loop(隐式使用链表迭代器)将是 O(n )。

您甚至可以使用while-loop 和您自己处理的显式迭代器来模仿for-loop,而不是让for-loop 为您处理它。而不是

for char in 'test':
    print(char)

这样做:

it = iter('test')
while True:
    try:
        char = next(it)
    except StopIteration:
        break
    print(char)

打印这个:

t
e
s
t

回到字符串迭代的时间复杂度

让我们看看源代码。我对它不是很熟悉,但我会描述我所相信的。还记得它是str_iterator 吗? Python 3 中的 str 在 Python 2 中称为 unicode,这仍然是 Python 3 的 C 源代码中的名称。所以在 unicodeobject.c 中,我们找到字符串 "str_iterator",它位于“Unicode 迭代器”中“ 部分。摘录:

/********************* Unicode Iterator **************************/

typedef struct {
    ...
    Py_ssize_t it_index;
    PyObject *it_seq;    /* Set to NULL when iterator is exhausted */
} unicodeiterobject;
...
unicodeiter_next(unicodeiterobject *it)
{
    ...
    seq = it->it_seq;
      ...
        void *data = PyUnicode_DATA(seq);
        Py_UCS4 chr = PyUnicode_READ(kind, data, it->it_index);
        item = PyUnicode_FromOrdinal(chr);
        if (item != NULL)
            ++it->it_index;
        return item;
    ...
}
...
PyTypeObject PyUnicodeIter_Type = {
    ...
    "str_iterator",         /* tp_name */
   ...
};

所以它是一个unicodeiterobject,带有一个指向它迭代的字符串的指针it_seq 和一个索引it_index。而它的next 函数使用它们来获取下一个字符,增加索引并返回该字符。好的,结果迭代器确实在内部使用了索引。但是这种索引比你在 Python 中使用unicode_getitem 函数的索引在更低、更直接的内部级别:

static PyObject *
unicode_getitem(PyObject *self, Py_ssize_t index)
{
    void *data;
    enum PyUnicode_Kind kind;
    Py_UCS4 ch;
    ...
    if (index < 0 || index >= PyUnicode_GET_LENGTH(self)) {
        PyErr_SetString(PyExc_IndexError, "string index out of range");
        return NULL;
    }
    kind = PyUnicode_KIND(self);
    data = PyUnicode_DATA(self);
    ch = PyUnicode_READ(kind, data, index);
    return unicode_char(ch);
}

两者看起来相似,最终使用PyUnicode_READ(kind, data, index)。我找不到那个,但它应该相当简单并且 O(1),使得整个迭代 O(n)。

还有一件事:@NickParsons 上面指出的answer/question 解决了 Python 使用可变大小多字节字符表示的担忧,这可能使索引查找 O(n) 而不是 O(1)。即使 的情况,它也只会影响unicode_getitem 函数。不是 str_iterator 迭代器。因为迭代器绝对不会使用幼稚的“字符串索引”,而是使用指向下一个字符的第一个 byte 的指针,以便它可以在 O(1) 中读取和前进。所以它的整个迭代仍然是 O(n)。

【讨论】:

  • 希望我能接受这个。有没有办法揭示字符串迭代器在幕后的作用?我今天会尝试一些实验。
  • @Learningstatsbyexample 完成,请参阅添加的“返回字符串迭代的时间复杂度”部分。
  • 太棒了。随意支持这个问题..这可能会被删除,您的回复将消失。这很有帮助。
【解决方案2】:

O(n) 就是答案。 查看此网站以了解有关 timeComplexity 的更多信息 python time complexity

【讨论】:

  • 这实际上没有字符串迭代。但@Nick Parsons 的评论显示你的解决方案是正确的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多