Python3快速查找集合中的任何元素是不是是字符串的子字符串的方法
Posted
技术标签:
【中文标题】Python3快速查找集合中的任何元素是不是是字符串的子字符串的方法【英文标题】:Python3 Fast Way To Find If Any Elements In Collections Are Substring Of StringPython3快速查找集合中的任何元素是否是字符串的子字符串的方法 【发布时间】:2016-06-18 14:23:09 【问题描述】:如果我有 collection of strings
,是否有数据结构或函数可以提高检查集合的任何元素是否在我的主字符串上为 substrings
的速度?
现在我正在循环遍历我的字符串数组并使用in
运算符。有更快的方法吗?
import timing
## string match in first do_not_scan
## 0:00:00.029332
## string not in do_not_scan
## 0:00:00.035179
def check_if_substring():
for x in do_not_scan:
if x in string:
return True
return False
## string match in first do_not_scan
## 0:00:00.046530
## string not in do_not_scan
## 0:00:00.067439
def index_of():
for x in do_not_scan:
try:
string.index(x)
return True
except:
return False
## string match in first do_not_scan
## 0:00:00.047654
## string not in do_not_scan
## 0:00:00.070596
def find_def():
for x in do_not_scan:
if string.find(x) != -1:
return True
return False
string = '/usr/documents/apps/components/login'
do_not_scan = ['node_modules','bower_components']
for x in range(100000):
find_def()
index_of()
check_if_substring()
【问题讨论】:
有没有可能你在这里粘贴了错误的东西。或者string = 'a'
只是一个示例。因为node_modules
永远不会在string
中。话虽如此,您可以使用地图吗?其中键是do_not_scan
的项目。那么搜索就是O(1)
只是一个示例来演示string
可能不包含do_not_scan
的任何元素。我以前从未使用过地图,你会怎么做呢?
您想要grep -l -Ff collections_of_strings main_string
的模拟吗?其中collections_of_strings
文件包含一组字符串(每行一个),main_string
文件包含主字符串(原样)。
我将编辑,意味着是否有更好的数据结构,例如。将事物放在一组而不是数组中,这样可以加快搜索速度
【参考方案1】:
不,没有更快的内置方法。
如果您有大量字符串要测试,那么您最好使用第三方Aho-Corasick 包,如J.F. Sebastian's answer 所示。
使用内置方法,最坏的情况是:没有匹配项,这意味着您已经测试了列表中的每个项目以及每个项目中的几乎每个偏移量。
幸运的是,in
运算符非常快(至少在 CPython 中)并且在我的测试中快了近三倍:
0.3364804992452264 # substring()
0.867534976452589 # any_substring()
0.8401796016842127 # find_def()
0.9342398950830102 # index_of()
2.7920695478096604 # re implementation
这是我用于测试的脚本:
from timeit import timeit
import re
def substring():
for x in do_not_scan:
if x in string:
return True
return False
def any_substring():
return any(x in string for x in do_not_scan)
def find_def():
for x in do_not_scan:
if string.find(x) != -1:
return True
return False
def index_of():
for x in do_not_scan:
try:
string.index(x)
return True
except:
return False
def re_match():
for x in do_not_scan:
if re.search(string, x):
return True
return False
string = 'a'
do_not_scan = ['node_modules','bower_components']
print(timeit('substring()', setup='from __main__ import substring'))
print(timeit('any_substring()', setup='from __main__ import any_substring'))
print(timeit('find_def()', setup='from __main__ import find_def'))
print(timeit('index_of()', setup='from __main__ import index_of'))
print(timeit('re_match()', setup='from __main__ import re_match'))
【讨论】:
不正确。您可以做得比O(n*m)
更好,例如,Aho–Corasick algorithm 及时是O(n + m)
。 grep
may use it for fixed strings【参考方案2】:
是的,有一种更快的方法来执行found = any(s in main_string for s in collection_of_strings)
,例如,Aho-Corasick_algorithm 允许将基于any()
的O(n*m*k)
算法改进为O(n + m*k)
,其中n
是len(main_string)
,m
是 len(collections_of_strings)
,k
表示集合中字符串的各个长度。
#!/usr/bin/env python
import noaho # $ pip install noaho
trie = noaho.NoAho()
for s in collection_of_strings:
trie.add(s)
found = trie.find_short(main_string)[0] is not None
注意:如果您对 Big-O 行为感兴趣,则没有必要测量诸如 string = 'a'
之类的小字符串的时间性能。要么为基准测试使用更具代表性的样本,要么在您的案例中不需要更快(渐近)的算法。
【讨论】:
您能否提供任何关于在in
上使用 Aho-Corasick 算法的截止点的指导?
只有您的分析器知道。常数因素取决于实施的质量,例如str.translate()
in Python 3.5+ on ASCII-only input may be 50 times faster than the same code on previous Python 3 versions。【参考方案3】:
def check():
if any(w in string for w in do_not_scan):
return True
else:
return False
或者更简单:
def check():
return any(w in string for w in do_not_scan)
如@Two-Bit Alchemist 所述
【讨论】:
第一个 do_not_scan 中的字符串匹配 = 0:00:00.085493 |字符串不在 do_not_scan = 0:00:00.074540 更简单:return any(w in string for w in do_not_scan)
any
与 find_def
和 index_of
一样慢。【参考方案4】:
我没有大型数据集可以尝试:
但也许这样的事情会起作用?
python3
from builtins import any
import timeit
do_not_scan = ['node_modules', 'bower_components']
string = 'a'
def check_if_substring():
return any(string in x for x in do_not_scan)
result = timeit.Timer("check_if_substring()", "from __main__ import check_if_substring")
count = 10000
print(result.timeit(count)/count)
或者反过来:
def check_if_substring():
return any(x in string for x in do_not_scan)
我的结果:6.48119201650843e-07
【讨论】:
只是好奇——你为什么要重命名任何一个,为什么要这样? 这是旧代码的副本和过去。在这种情况下没有意义。我会修复它any
与 find_def
和 index_of
一样慢。以上是关于Python3快速查找集合中的任何元素是不是是字符串的子字符串的方法的主要内容,如果未能解决你的问题,请参考以下文章