from random import randint
from marisa_trie import RecordTrie
sa = [(str(randint(1000000,9999999)),str(randint(1000000,9999999))) for i in range(100000)]
# make length of string in packed format big enough!
fmt = ">10p10p"
sa_tries = (RecordTrie(fmt, zip((unicode(first) for first, _ in sa), sa)),
RecordTrie(fmt, zip((unicode(second) for _, second in sa), sa)))
def lookup_set(sa_tries, x_appr, y_appr):
"""lookup prefix in the appropriate trie and intersect the result"""
return (set(item[1] for item in sa_tries[0].items(unicode(x_appr))) &
set(item[1] for item in sa_tries[1].items(unicode(y_appr))))
lookup_set(sa_tries, "2", "4")
from random import randint
from operator import itemgetter
first = itemgetter(0)
second = itemgetter(1)
sa = [(str(randint(0, 1000000)), str(randint(0, 1000000))) for _ in range(300000)]
f_sorted = sorted(sa, key=first)
s_sorted = sa
s_sorted.sort(key=second)
max_length = max(len(s) for _,s in sa)
# See: bisect module from stdlib
def bisect_right(seq, element, key):
lo = 0
hi = len(seq)
element = element.ljust(max_length, '9')
while lo < hi:
mid = (lo+hi)//2
if element < key(seq[mid]):
hi = mid
else:
lo = mid + 1
return lo
def bisect_left(seq, element, key):
lo = 0
hi = len(seq)
while lo < hi:
mid = (lo+hi)//2
if key(seq[mid]) < element:
lo = mid + 1
else:
hi = mid
return lo
def lookup_set(x_appr, y_appr):
x_left = bisect_left(f_sorted, x_appr, key=first)
x_right = bisect_right(f_sorted, x_appr, key=first)
x_candidates = f_sorted[x_left:x_right + 1]
y_left = bisect_left(s_sorted, y_appr, key=second)
y_right = bisect_right(s_sorted, y_appr, key=second)
y_candidates = s_sorted[y_left:y_right + 1]
return set(x_candidates).intersection(y_candidates)
以及与初始解决方案的比较:
^{pr2}$
如您所见,此解决方案比您的天真方法快240-1400倍(在这些示例中)。在
如果你有一大堆火柴:
In [19]: %timeit lookup_set('1', '2')
10 loops, best of 3: 27.1 ms per loop
In [20]: %timeit lookup_set2('1', '2')
10 loops, best of 3: 152 ms per loop
In [21]: len(lookup_set('1', '2'))
Out[21]: 3587
In [23]: %timeit lookup_set('', '2')
10 loops, best of 3: 182 ms per loop
In [24]: %timeit lookup_set2('', '2')
1 loops, best of 3: 212 ms per loop
In [25]: len(lookup_set2('', '2'))
Out[25]: 33053
如您所见,即使匹配的数量约为总大小的10%,此解决方案也会更快。但是,如果您试图匹配所有数据:
In [26]: %timeit lookup_set('', '')
1 loops, best of 3: 360 ms per loop
In [27]: %timeit lookup_set2('', '')
1 loops, best of 3: 221 ms per loop
它变慢了(不是那么慢),尽管这是一个非常特殊的情况,我怀疑你会经常匹配几乎所有的元素。在
请注意,sort所花的时间非常小:
In [13]: from random import randint
...: from operator import itemgetter
...:
...: first = itemgetter(0)
...: second = itemgetter(1)
...:
...: sa2 = [(str(randint(0, 1000000)), str(randint(0, 1000000))) for _ in range(300000)]
In [14]: %%timeit
...: f_sorted = sorted(sa2, key=first)
...: s_sorted = sorted(sa2, key=second)
...: max_length = max(len(s) for _,s in sa2)
...:
1 loops, best of 3: 881 ms per loop
我仔细研究并实施了4个建议的解决方案,以比较它们的效率。我用不同的前缀长度运行测试,看看输入如何影响性能。trie和sorted list的性能对输入的长度非常敏感,随着输入的增加,两者都会变得更快(我认为这实际上对输出的大小很敏感,因为输出随着前缀的增加而变小)。然而,排序集解决方案在所有情况下都绝对更快。在
在这些计时测试中,
sa
中有200000个元组,每个方法运行10次:代码如下:
^{pr2}$您可以使用trie data structure。可以用dict对象树(参见How to create a TRIE in Python)构建一个,但是有一个包marisa-trie通过绑定到c++库来实现一个内存高效的版本
我以前没有用过这个库,但是在玩它的时候,我得到了这个功能:
你可以在
O(log(n) + m)
时间内完成,其中n
是元组的数目,m
是匹配元组的数目,如果你可以保留两个元组的排序副本。 排序本身将花费O(nlog(n))
,也就是说,它将是渐进的慢于而不是您的天真方法,但是如果您必须执行一定数量的查询(超过log(n)
,这几乎肯定是相当小的),它将得到回报。在其思想是可以使用对分来找到第一个值和第二个值正确的候选值,然后与这些集相交。在
但是请注意,您需要一种奇怪的比较:您关心以给定参数开头的所有字符串。这仅仅意味着在搜索最正确的匹配项时,应该用
9
s填充该键一个完整的工作代码(虽然没有经过很多测试):
以及与初始解决方案的比较:
^{pr2}$如您所见,此解决方案比您的天真方法快
240-1400
倍(在这些示例中)。在如果你有一大堆火柴:
如您所见,即使匹配的数量约为总大小的10%,此解决方案也会更快。但是,如果您试图匹配所有数据:
它变慢了(不是那么慢),尽管这是一个非常特殊的情况,我怀疑你会经常匹配几乎所有的元素。在
请注意,
sort
所花的时间非常小:如您所见,完成两个已排序的副本不到一秒钟。实际上,由于上面的代码对第二个副本进行“就地”排序(尽管tim sort可能仍然需要
O(n)
内存),所以它的速度会稍微快一些。在这意味着,如果您必须执行超过6-8个查询,此解决方案将更快。在
注意:python的标准库提供了一个
bisect
模块。但是它不允许key
参数(尽管我记得读过Guido想要它,所以将来可能会添加它)。因此,如果您想直接使用它,就必须使用“decorate-sort-undecorate”这个成语。在而不是:
您应该:
也就是说,显式地插入键作为元组的第一个元素。然后,可以使用
('123', '')
作为元素传递给bisect_*
函数,它应该会找到正确的索引。在我决定避免这个。我复制粘贴了来自模块源代码的代码,并对其稍作修改,为您的用例提供了一个更简单的接口。在
最后一点:如果可以将元组元素转换为整数,那么比较会更快。然而,大部分时间仍然需要执行集合的交集,所以我不知道它到底能提高多少性能。在
相关问题 更多 >
编程相关推荐