有效地测试一个项目是否在一个排序的字符串列表中

Efficiently test if an item is in a sorted list of strings

假设我有一个短小写 [a-z] 字符串列表(最大长度 8):

L = ['cat', 'cod', 'dog', 'cab', ...]

如何有效地确定字符串 s 是否在此列表中?

我知道我可以 if s in L: 但我可以预排序 L 和二叉树搜索。

我什至可以一个字母一个字母地建造自己的树。所以设置s='cat': 所以 T[ ord(s[0])-ord('a') ] 给出了通往 'cat''cab' 等的子树。但是哎呀,乱七八糟的!

我也可以制作自己的 hashfunc,因为 L 是静态的。

def hash_(item):
    w = [127**i * (ord(j)-ord('0')) for i,j in enumerate(item)]
    return sum(w) % 123456

... 并且只是 fiddle 数字,直到我没有得到重复的数字。又丑了。

是否有任何开箱即用的东西我可以使用,还是我必须自己动手?

沿着 complexity/optimisation 曲线当然会有解决方案,所以如果这个问题过于开放,我提前道歉。

我正在寻找能够以低 LoC 成本换取体面性能增益的东西。

内置 Python set 几乎肯定是您可以使用的最高效的设备。 (当然,你可以推出可爱的东西,比如你的“词汇”的 DAG,但这会慢很多)。

因此,将您的 list 转换为 set(如果要进行多项测试,最好构建一次)并测试成员资格:

s in set(L)

或者,对于多个测试:

set_values = set(L)

# ...
if s in set_values:
    # ...

这里有一个简单的例子来说明性能:

from string import ascii_lowercase
import random

n = 1_000_000
L = [''.join(random.choices(ascii_lowercase, k=6)) for _ in range(n)]

搭建场景的时间:

%timeit set(L)
# 99.9 ms ± 49.5 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

查询集的时间:

set_values = set(L)

# non-existent string
%timeit 'foo' in set_values
# 45.1 ns ± 0.0418 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

# existing value
s = L[-1]

a = %timeit -o s in set_values
# 45 ns ± 0.0286 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

对比直接针对 list 进行测试:

b = %timeit -o s in L
# 16.5 ms ± 24.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

b.average / a.average
# 359141.74

您上次进行 350,000 倍加速是什么时候 ;-)?