执行字符串查找的最快方法?

Posted

技术标签:

【中文标题】执行字符串查找的最快方法?【英文标题】:Fastest way to perform string lookups? 【发布时间】:2018-07-26 19:03:47 【问题描述】:

假设我们有一定数量的可能字符串:

possible_strings_list = ['foo', 'bar', 'baz', 'qux', 'spam', 'ham', 'eggs'] 

并接收已知为其中之一的新字符串。我们想为每个新字符串分配一个整数,例如

if new_string == 'foo':
    return 0
elif new_string == 'bar':
    return 1
...

在 Python 3.6 中最快的方法是什么? 我尝试了几种方法,到目前为止使用字典是最快的:

list_index 2.7494255019701086
dictionary 0.9412809460191056
if_elif_else 2.10705983400112
lambda_function 2.6321219780365936
tupple_index 2.751029207953252
ternary 1.931659944995772
np_where 15.610908019007184

但是,我或多或少是 Python 新手,如果有其他更快的解决方案,我很感兴趣。你有什么建议吗?

我的完整测试代码:

import timeit
import random
import numpy as np

def list_index(i):
    return(possible_strings_list.index(i))

def dictionary(i):
    return possible_strings_dict[i]

def tupple_index(i):
    return possible_strings_tup.index(i)


def if_elif_else(i):
    if i == 'foo':
        return 1
    elif i == 'bar':
        return 2
    elif i == 'baz':
        return 3
    elif i == 'qux':
        return 4
    elif i == 'spam':
        return 5
    elif i == 'ham':
        return 6
    elif i == 'eggs':
        return 7

def ternary(i):
    return 0 if i == 'foo' else 1 if i == 'baz' else 2 if i == 'bar' else 3 if i == 'qux' else 4 if i == 'spam'else 5 if i == 'ham' else 6

n = lambda i: 0 if i == 'foo' else 1 if i == 'baz' else 2 if i == 'bar' else 3 if i == 'qux' else 4 if i == 'spam'else 5 if i == 'ham' else 6
def lambda_function(i):
    return n(i)

def np_where(i):
    return np.where(possible_strings_array == i)[0][0]

##
def check(function):
    for i in testlist:
        function(i)

possible_strings_list = ['foo', 'bar', 'baz', 'qux', 'spam', 'ham', 'eggs']
testlist = [random.choice(possible_strings_list) for i in range(1000)]
possible_strings_dict = 'foo':0, 'bar':1, 'baz':2, 'qux':3, 'spam':4, 'ham':5, 'eggs':6
possible_strings_tup = ('foo', 'bar', 'baz', 'qux', 'spam', 'ham', 'eggs')
allfunctions = [list_index, dictionary, if_elif_else, lambda_function, tupple_index, ternary, np_where]

for function in allfunctions:
    t = timeit.Timer(lambda: check(function))
    print(function.__name__, t.timeit(number=10000))

【问题讨论】:

据我所知,使用字典确实是最快的方法。我不希望您会找到更好的东西,因为这正是字典的预期用途。 并非如此,查找字典可能是您的最佳选择。 看起来您已经在进行性能测试了。请注意,随着列表大小的增加,您将获得不同的性能特征。一些方法将具有恒定时间、一些线性和一些指数。您是在测试特定的东西,还是只是一般情况? @elpunkt 我不是真正的 python 大师,但我希望字典具有常量查找,如哈希表,但列表具有线性查找,因为需要检查每个值。因此,列表查找成本将取决于项目的位置(第一个比最后一个快),而字典则不然。 dict = python 中的哈希表,因此平均时间复杂度为 O(1),最差的是 O(n)。大多数用例都很难被击败(除非您搜索价值) 【参考方案1】:

字典查找是执行此搜索的最快方法。在进行这样的分析时,您通常会比较每个进程的Time Complexity。

对于字典查找,时间复杂度是“恒定时间”,即 O(1)。虽然这可能意味着它通常是算法可以采取的步数的整数值,但在这种情况下它实际上是一个。

其他方法将需要迭代(或者在 if elses 遍历的情况下 - 这本质上是一种类似的方法)。这些范围从需要查看所有值 O(n) 到需要查看一些值 O(log n)。

由于 n 是检查集的大小,并且随着集变大,结果的差异也会变大,字典始终优于显示的其他选项。

没有办法比 O(1) 更快。您展示的方法的唯一缺点是随着集合的增长它可能需要更多内存,这被称为算法的空间复杂性。但是在这种情况下,由于我们只需要集合中的每个项目一个值,因此空间复杂度将为 O(n),可以忽略不计。

在一般意义上的优化中,重要的是要考虑当前解决方案中存在多少复杂性,以及改进该复杂性有多大意义。如果要进行改进,它们的目标应该是达到不同的性能层次,例如从 O(n) 到 O(log n) 或从 O(log n) 到 O(1)。

图片提供:http://bigocheatsheet.com/

微优化往往是针对同一复杂层进行优化的情况,而这些优化本身通常没有建设性。

【讨论】:

我建议如果可能的字符串集是固定的(听起来像这里的情况),那么算法的复杂性就无关紧要了。 @Jeff - 复杂性与集合的大小无关,无论是否固定。这是为了达到解决方案而需要进行操作的次数。鉴于这种设置大小为 7 的情况和 O(n!) 算法,这将是 5040 次操作。将其与 1 进行比较。 我过度概括了,意思是 Big-O 复杂性。 Big-O 表示法是渐近行为,忽略任何低阶项。 O(1) 算法和 O(n!) 算法的复杂度实际上可能分别为 1000000 和 n!,在这种情况下,O(n!) 在大小为 7 时表现更好。

以上是关于执行字符串查找的最快方法?的主要内容,如果未能解决你的问题,请参考以下文章

最快的内容查找算法-----暴雪的Hash算法

最快的 c++ / stl 算法来查找成对的字符串

如何实现十几万条数据的最快查找

在 python 中查找特征值/向量的最快方法是啥?

在 C# 中查找两个集合的补集的最快方法

用字典替换子字符串的最快方法(在大型数据集上)