【问题标题】:Which way is better to compare two strings in Python? I want to know which one is faster在 Python 中比较两个字符串哪种方式更好?我想知道哪个更快
【发布时间】:2022-01-14 23:04:55
【问题描述】:

假设两个变量 a 和 b 存储两个字符串,我想比较这两个字符串是否相同。在 Python 中,典型的方式应该是a == b。我相信它的时间复杂度是 O(min(m,n)),其中 m 和 n 是字符串的长度。但是如果我在 {b} 中以这种方式比较它们(我将字符串 b 添加到集合中并检查字符串 a 是否在该集合中),时间复杂度是否会为 O(1),而不管 a 中字符串的长度和b?

【问题讨论】:

  • 您可以对其进行基准测试,但== 几乎可以肯定更快。
  • 集合包含预计为 O(1),因为它不依赖于集合的大小,但您谈论的是字符串的大小,这是一个不同的变量。它仍然需要检查字符串的内容:这并不神奇。

标签: python string set


【解决方案1】:

不,因为in 运算符首先计算字符串的散列(线性复杂度),然后如果集合中存在具有相同散列的元素(再次,线性复杂度),则进行另一次比较。所以,a == b 是您最好的选择,因为它跳过了哈希的计算并避免了集合的构造。

in 运算符的恒定时间复杂度与集合的长度有关。就字符串的长度而言,它仍然是线性的。

然而,下面的结果似乎不一致:

$ python3 -m timeit "'aaa' == 'aaa'"
20000000 loops, best of 5: 18.9 nsec per loop
$ python3 -m timeit "'aaa' == 'bbb'"
10000000 loops, best of 5: 22.1 nsec per loop
$ python3 -m timeit "'aaa' in {'aaa'}"
20000000 loops, best of 5: 17 nsec per loop
$ python3 -m timeit "'aaa' in {'bbb'}"
20000000 loops, best of 5: 16 nsec per loop

稍作改动的例子似乎更有意义...

$ python3 -m timeit "a = 'aaa'; b = 'bbb'; a in {b}"
5000000 loops, best of 5: 63 nsec per loop
$ python3 -m timeit "a = 'aaa'; b = 'bbb'; a in {a}"
5000000 loops, best of 5: 55.7 nsec per loop
$ python3 -m timeit "a = 'aaa'; b = 'bbb'; a == b"
10000000 loops, best of 5: 30.1 nsec per loop
$ python3 -m timeit "a = 'aaa'; b = 'bbb'; a == a"
10000000 loops, best of 5: 27 nsec per loop

可以在反汇编代码中找到原因:

from dis import dis

def a():
    'aaa' in {'aaa'}

def b():
    x = 'aaa'
    'aaa' in {x}

print(dis(a))
print(dis(b))

产量:

  5           0 LOAD_CONST               1 ('aaa')
              2 LOAD_CONST               2 (frozenset({'aaa'}))
              4 COMPARE_OP               6 (in)
              6 POP_TOP
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE
None
 12           0 LOAD_CONST               1 ('aaa')
              2 STORE_FAST               0 (x)

 13           4 LOAD_CONST               1 ('aaa')
              6 LOAD_FAST                0 (x)
              8 BUILD_SET                1
             10 COMPARE_OP               6 (in)
             12 POP_TOP
             14 LOAD_CONST               0 (None)
             16 RETURN_VALUE
None

由于我们在第一种方法中只使用文字,python 可以进行其中一个(极其罕见的)优化并使用 LOAD_CONST 而不必在运行时构造集合。

然而,对于更真实的代码,情况不再如此,必须使用昂贵的 BUILD_SET。

【讨论】:

  • 我想你的意思是不确定一个比另一个快?也许在某些情况下,in 运算符可能会更好......
  • @SamuelWen 字符串已经高度优化。如果有一种方法可以使字符串相等更快,那么它已经被合并到常规方法中了。
  • 使用dis检查字节码可以看到a in {a}必须使用BUILD_SET,而'aaa' in {'aaa'}可以使用LOAD_CONST ... (frozenset({'aaa'}))
  • @Matthias 没错。因此,对于“野外”代码,第二系列示例更有意义,因为集合中的字符串似乎不太可能是文字。相当有趣的发现!
【解决方案2】:

没有。这只是隐藏了支票。要检查一个值是否在一个集合中,它首先需要散列它正在检查的成员。然后,如果它找到哈希的匹配项,它需要调用他们的 equals 实现来验证匹配的哈希是否只是误报。所以现在你最终得到了 O(m+n+min(m, n))。

话虽如此,由于字符串是计算机科学的核心部分,因此它们在后台进行了许多不同的优化。某些字符串可能已经提前进行了哈希处理,并且该值已被缓存。或者也许 a 和 b 指向同一个字符串,所以它可以提前退出。或者它们的长度不同,因此可以快速退出。尝试为像字符串这样重要的东西添加自己的优化可能只会使您正在努力帮助您的系统变得更加困难。

【讨论】:

    【解决方案3】:

    在时间复杂度方面,比较字符串的a is b 比比较a == b 更快,因为可以排除它们都不是NaN

    好的,我认为这很“聪明”,但请看下面的反例,为什么使用 is 是致命的 - 来自 @khelwood! @JohnColeman 建议的文章也很有趣。 (谢谢!)

    【讨论】:

    • 这不是只检查它们是否是同一个对象而不是它们包含相同的数据吗?我们指的是字符串,而不是数字。
    • @Locke Python 字符串是唯一的。所以相等的字符串是同一个对象。 (这在其他编程语言中不一定如此——但 Python 字符串就是这样工作的)。
    • 请参阅this blog post 上的字符串实习,了解两个相等的字符串未通过is 测试的示例。
    • 在 IDLE (3.9.1) 中使用 x = 'AB'y = x[:1] + x[1:] 进行了尝试。我得到了Truex == yFalsex is y
    • a='!'*10b='!'*10
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-13
    • 1970-01-01
    • 2022-11-16
    • 1970-01-01
    相关资源
    最近更新 更多