【发布时间】:2019-04-06 06:03:58
【问题描述】:
我遇到了用于子字符串搜索的 KMP 算法,并在 python 中实现了它。后来,我发现in 运算符也可以用来解决这个问题,我决定比较它们的性能。令我惊讶的是,in 比 KMP 算法快得多,我决定仔细研究一下in。
我发现in 在一个字符串中实现了__contains__ 方法,该方法用于Datamodel doc 中建议的包含检查。但无法进一步了解为什么它更快。
这是我的 KMP 算法实现:
def lps(pattern):
start_ind = 0
lps_list = [0]
for j in pattern[1:]:
if(j == pattern[start_ind]):
lps_list.append(start_ind)
start_ind += 1
else:
start_ind = 0
lps_list.append(start_ind)
return lps_list
def kmp(search, pattern):
lps_list = lps(pattern)
pat_ind = 0
for j in search:
if(pat_ind == len(pattern)):
return True
if(j == pattern[pat_ind]):
pat_ind += 1
continue
while(j != pattern[pat_ind] and pat_ind != 0):
pat_ind = lps_list[pat_ind - 1]
if(pat_ind == 0 and j == pattern[pat_ind]):
pat_ind += 1
else:
if(pat_ind == len(pattern)):
return True
return False
驱动程序块:
start = timeit.default_timer()
print('Found!!') if(kmp(search, pattern)) else print('Nope')
print(f'KMP algorithm: {(timeit.default_timer() - start):0.8e}')
start = timeit.default_timer()
print('Found!!') if(pattern in search) else print('Nope')
print(f'in operator: {(timeit.default_timer() - start):0.8e}')
测试用例和结果:
search = ''.join(['a' for _ in range(10000)] + ['b'])
pattern = ''.join(['a' for _ in range(1000)] + ['b'])
Found!!
KMP algorithm: 4.42536000e-03
Found!!
in operator: 3.72060003e-05
我预计 KMP 算法不会像结果显示的那么慢,因为它是一个不错的子字符串搜索算法。我不明白是不是,我的测试用例遗漏了一些东西,或者由于 Python 存储字符串的方式。
【问题讨论】:
-
这是内置的一些历史背景:effbot.org/zone/stringlib.htm
-
还请记住,内置函数是用 C 编写的,而 KMP 是用纯 Python 编写的(因为它被解释了,所以会产生很大的乘法性能损失)。
-
你认为 Python 使用什么算法?见the source。以下是来自实施者的notes:“这排除了大多数标准算法(Knuth-Morris-Pratt 不是次线性的,Boyer-Moore 需要依赖于字母大小和模式大小的表格,大多数 Boyer-Moore 变体需要取决于图案大小等的表格。经过一些调整,我想出了 Boyer-Moore 的简化,并结合了 Horspool 和 Sunday 的想法。”
-
如果您想从 Python 的
str.__contains__中查看最坏情况的行为,请尝试搜索'a'*1000+'ba'。您的测试会将最简单的天真字符串搜索置于最坏情况下的性能,但str.__contains__需要稍微困难的情况。 -
@NPE,没想到。这是有道理的。