如果您将数据存储在普通字典中,则无法进一步优化这一点,因为它没有提供比以某种不可预测的顺序顺序访问字典中所有元素更快的任何东西。这意味着您的解决方案并不比 O(n) 更快。
现在,数据库。数据库不是任何(足够复杂的)问题的通用解决方案。您能否可靠地估计此类数据库查找的速度/复杂性?如果您滚动到此回复的底部,您会发现对于大型数据集,数据库性能可能比智能数据结构差得多。
您需要的是手工制作的数据结构。有很多选择,这在很大程度上取决于您对这些数据所做的其他事情。例如:您可以保留 N 键的排序列表集,每个列表按 n-th 元组元素排序。然后你可以快速选择N的已排序元素集合,只匹配位置n处的一个元组元素,并找到它们的交集得到结果。这将给出O(log n)*O(m) 的平均性能,其中 m 是一个子集中的平均元素数。
或者您可以将您的项目存储在 k-d 树中,这意味着您必须支付O(log n) 插入价格,但您可以在O(log n) 时间进行上述查询。下面是一个 Python 示例,使用 SciPy 中的 k-d 树实现:
from scipy.spatial import kdtree
import itertools
import random
random.seed(1)
data = list(itertools.permutations(range(10), 4))
random.shuffle(data)
data = data[:(len(data)/2)]
tree = kdtree.KDTree(data)
def match(a, b):
assert len(a) == len(b)
for i, v in enumerate(a):
if v != b[i] and (v is not None) and (b[i] is not None):
return False
return True
def find_like(kdtree, needle):
assert len(needle) == kdtree.m
def do_find(tree, needle):
if hasattr(tree, 'idx'):
return list(itertools.ifilter(lambda x: match(needle, x),
kdtree.data[tree.idx]))
if needle[tree.split_dim] is None:
return do_find(tree.less, needle) + do_find(tree.greater, needle)
if needle[tree.split_dim] <= tree.split:
return do_find(tree.less, needle)
else:
return do_find(tree.greater, needle)
return do_find(kdtree.tree, needle)
def find_like_bf(kdtree, needle):
assert len(needle) == kdtree.m
return list(itertools.ifilter(lambda x: match(needle, x),
kdtree.data))
import timeit
print "k-d tree:"
print "%.2f sec" % timeit.timeit("find_like(tree, (1, None, 2, None))",
"from __main__ import find_like, tree",
number=1000)
print "brute force:"
print "%.2f sec" % timeit.timeit("find_like_bf(tree, (1, None, 2, None))",
"from __main__ import find_like_bf, tree",
number=1000)
以及试运行结果:
$ python lookup.py
k-d tree:
0.89 sec
brute force:
6.92 sec
只是为了好玩,还添加了基于数据库的解决方案基准。初始化代码由上面改为:
random.seed(1)
data = list(itertools.permutations(range(30), 4))
random.shuffle(data)
现在,“数据库”实现:
import sqlite3
db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE a (x1 INTEGER, x2 INTEGER, x3 INTEGER, x4 INTEGER)")
db.execute("CREATE INDEX x1 ON a(x1)")
db.execute("CREATE INDEX x2 ON a(x2)")
db.execute("CREATE INDEX x3 ON a(x3)")
db.execute("CREATE INDEX x4 ON a(x4)")
db.executemany("INSERT INTO a VALUES (?, ?, ?, ?)",
[[int(x) for x in value] for value in tree.data])
def db_test():
cur = db.cursor()
cur.execute("SELECT * FROM a WHERE x1=? AND x3=?", (1, 2))
return cur.fetchall()
print "sqlite db:"
print "%.2f sec" % timeit.timeit("db_test()",
"from __main__ import db_test",
number=100)
以及测试结果,每个基准测试减少了 100 次运行(针对生成的 657720 元素键集):
$ python lookup.py
building tree
done in 6.97 sec
building db
done in 11.59 sec
k-d tree:
1.90 sec
sqlite db:
2.31 sec
还值得一提的是,构建树所花费的时间几乎比将测试数据集插入数据库的时间少了两倍。
完整来源:https://gist.github.com/1261449