为什么以pythonic方式检查字符串S
是否是回文 - S == S[::-1]
- 比以下实现更快?
i = 0
j = len(S) - 1
while i < j:
if S[i] != S[j]:
return False
i += 1
j -= 1
return True
答案 0 :(得分:7)
因为Python代码只编译为字节码,然后解释。这比在C代码中创建一个新的反向字符串要慢很多,然后将字符串与另一个字符串进行比较,使用更多的C代码。
请注意,这两种算法基本上都是O(N)复杂度; Python代码最多执行1/2 N次迭代,而字符串反转版本最多可以进行2次N次迭代,但渐渐地说,它没有任何区别。
由于两种算法都是O(N)线性方法,因此重要的是它们的常数成本,每次迭代需要多长时间。 s == s[::-1]
的固定成本大大。
答案 1 :(得分:1)
S == S[::-1]
会很快,因为在这种情况下,一切都在C中运行,与基于Python的循环相比,这将是闪电般快速的。您的版本的唯一优势是它不会等待创建反向字符串才能开始比较,因此在那些情况下 * 对于大字符串S == S[::-1]
实际上会很慢
我们可以稍微提高一点S == S[::-1]
如果不是比较整体,我们会把字符串的前半部分和后半部分分开,并将上半部分与后半部分的反转版本进行比较。
def fast_half(S):
length = len(S)
half = length/2
first, last = half + (length % 2), half
return S[:first] == S[last:][::-1]
def fast_simple(S):
return S == S[::-1]
让我们比较一下:
>>> S = 'A' * 10**5
>>> %timeit fast_simple(S)
10000 loops, best of 3: 88.7 µs per loop
>>> %timeit fast_half(S)
10000 loops, best of 3: 49.1 µs per loop
>>> S = 'A' * 10**6
>>> %timeit fast_simple(S)
1000 loops, best of 3: 1.03 ms per loop
>>> %timeit fast_half(S)
1000 loops, best of 3: 601 µs per loop
我们可以通过上半部分memoryview
略微快速fast_half
,下半部分不可能,因为内存视图不支持扩展切片。此外,它们仅适用于str
(Python 3中的bytes
)和bytearrays
。
def fast_half_memoryview(S):
length = len(S)
half = length/2
first, last = half + (length % 2), half
return memoryview(S)[:first] == S[last:][::-1]
>>> S = 'A' * 10**5
>>> %timeit fast_half_memoryview(S)
10000 loops, best of 3: 46 µs per loop
>>> S = 'A' * 10**6
>>> %timeit fast_half_memoryview(S)
1000 loops, best of 3: 523 µs per loop
>>> S = 'A' * 10**7
>>> %timeit fast_half(S)
100 loops, best of 3: 12.8 ms per loop
>>> %timeit fast_half_memoryview(S)
100 loops, best of 3: 10.8 ms per loop
>>> %timeit fast_simple(S)
100 loops, best of 3: 17.3 ms per loop
对于小字符串S == S[::-1]
是最好的:
>>> S = 'A' * 10
>>> %timeit fast_half(S)
1000000 loops, best of 3: 721 ns per loop
>>> %timeit fast_half_memoryview(S)
1000000 loops, best of 3: 1.07 µs per loop
>>> %timeit fast_simple(S)
1000000 loops, best of 3: 353 ns per loop
>>> %timeit func(S) # OP's code
1000000 loops, best of 3: 1.34 µs per loop
* 当创建反向字符串或切片时可能会减慢我们的速度:
>>> S = 'A' * 10**6 + 'a'
>>> %timeit fast_simple(S)
1000 loops, best of 3: 976 µs per loop
>>> %timeit fast_half(S)
1000 loops, best of 3: 566 µs per loop
>>> %timeit fast_half_memoryview(S)
1000 loops, best of 3: 523 µs per loop
>>> %timeit func(S)
1000000 loops, best of 3: 382 ns per loop
答案 2 :(得分:0)
以下不是一个很好的基准测试,但每次运行它时,你的功能都比s == s[::-1]
略微慢。我还试图改变执行的顺序(首先运行你的函数),它仍然提供相同的结果!
import timeit
def is_pali1(s):
s = str(s)
return s == s[::-1]
def is_pali2(S):
S = str(S)
i = 0
j = len(S) - 1
while i < j:
if S[i] != S[j]:
return False
i += 1
j -= 1
return True
res = timeit.repeat("for x in range(99999900,100000000): is_pali1(x)", "from __main__ import is_pali1", number=100000)
print "is_pali1", sum(res)/len(res)
res = timeit.repeat("for x in range(99999900,100000000): is_pali2(x)", "from __main__ import is_pali2", number=100000)
print "is_pali2", sum(res)/len(res)
<强>输出强>
is_pali1 4.37230348587
is_pali2 4.67844009399
请注意,我将数字修改为两个函数中的字符串:s = str(s)
,因此不应偏置结果。 在Python 2.7.6上测试。