在Python中,在根据属性排序的列表中搜索的最有效方法是什么?请参阅下面的更准确的问题。
示例:
class Any (object):
def __init__(self, attr_a, attr_b):
self.attr_a = attr_a
self.attr_b = attr_b
L = [Any(-3, 4), Any(-2, 1), Any(0, 2), Any(2, 1), Any(5, 6), Any(6, 3), Any(8, 2), Any(10, 1), Any(13, 5), Any(14, 3)]
L
根据属性attr_a
排序。列表Any
的所有L
个实例都具有不同的attr_a
值。搜索attr_b
等于attr_a
的对象x
的值的最有效方法是什么?
答案 0 :(得分:3)
您将采用二进制搜索为Any
值找到正确的attr_a
对象。 bisect
module提供了一个起点:
def bisect_left(a, x, lo=0, hi=None, key=None):
if key is None: key = lambda v: v
if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)
while lo < hi:
mid = (lo+hi)//2
if key(a[mid]) < x: lo = mid+1
else: hi = mid
return lo
我唯一做的就是在这里为签名添加key
功能。 key
使用一个可调用的函数来返回值,而不是我们正在进行二等分。
现在您可以使用二分法来查找Any
索引:
from operator import attrgetter
index = bisect_left(L, x, key=attrgetter('attr_a'))
这将返回匹配的Any
的索引,或Any
值高于attr_a
的下一个 x
对象的索引}。您可能需要针对这些情况测试和/或调整算法。例如,您可以验证attr_a
确实与所需的值匹配:
def find_by_x(L, x, key):
index = bisect_left(L, x, key=key)
if key(L[index]) != x:
raise IndexError('{} not found'.format(x))
return L[index]
演示:
>>> from operator import attrgetter
>>> L = [Any(-3, 4), Any(-2, 1), Any(0, 2), Any(2, 1), Any(5, 6), Any(6, 3), Any(8, 2), Any(10, 1), Any(13, 5), Any(14, 3)]
>>> x = 6
>>> bisect_left(L, x, key=attrgetter('attr_a'))
5
>>> L[bisect_left(L, x, key=attrgetter('attr_a'))].attr_b
3
>>> find_by_x(L, x, key=attrgetter('attr_a')).attr_b
3
>>> x = 12
>>> find_by_x(L, x, key=attrgetter('attr_a')).attr_b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in find_by_x
IndexError: 12 not found
答案 1 :(得分:0)
您想要使用模块bisect及其bisect_left
功能。在您的情况下,您需要在使用之前提取密钥列表,有关详细说明,请参阅Other examples部分。如果这是不可接受的,那么你可以实现自己的二进制搜索版本,这是一个简单的算法。