繁体   English   中英

这是在Python中搜索一组“范围”对象中的值的最快方法

[英]Which is the fastest way to search a value within a set of many “range” objects in Python

我有很多这样的Python对象的列表:

class RangeClass(object):

    def __init__(self,address,size):
        self.address=address
        self.size=size
        #other attributes and methods...

然后,我有一个RangeClass对象的列表(rangelist)。

我需要找到给定值在哪个范围内。

我可以使用如下代码:

for r in ragelist:
    if(value>=r.address and value<(r.address+r.size)):
        return r
return None

但是我认为有一种更快的方法。 范围具有任意大小,但是我们可以假定它们没有重叠。

谢谢。

如果要测试的值很多,则可以使用bisect模块更快地找到值在哪个范围内。

如果

  • m =要测试的值的数量,以及
  • n = len(rangelist)

然后按照您的建议遍历值和范围列表,将花费O(m*n)时间。

如果使用二等分,则必须首先对起始地址O(nlogn)排序,然后在范围列表O(m*logn)找到每个值的位置。 因此,如果

O(nlogn + m*logn) < O(m*n)

然后二等分获胜。 对于大n ,与O(m*n)相比, O(m*logn)很小。 因此,如果

O(nlogn) < O(m*n)

或等效地,当

C log(n) < m

对于一些常数C


因此,当n大且C log(n) < m ,您可以尝试类似

import bisect

class RangeClass(object):

    def __init__(self,address,size):
        self.address=address
        self.size=size
    def __str__(self):
        return '[{0},{1})'.format(self.address,self.address+self.size)
    def __lt__(self,other):
        return self.address<other.address

rangelist=sorted([RangeClass(i,1) for i in (1,3,4,5,7.5)])
starts=[r.address for r in rangelist]

def find_range(value):
    start_idx=bisect.bisect(starts,value)-1
    try:
        r=rangelist[start_idx]
    except IndexError:
        return None
    start=r.address
    end=r.address+r.size
    if start<=value<end:
        return rangelist[start_idx]
    return None    

print(','.join(str(r) for r in rangelist))

for value in (0,1,1.5,2,3,4,5,6,7,8,9,10):
    r=find_range(value)
    if r:
        print('{v} in {r}'.format(v=value,r=str(r)))
    else:
        print('{v} not in any range'.format(v=value))

并不是的。 您所能做的就是利用Python的关系运算符链接。

if r.address <= value < (r.address + r.size):

您还可以在RangeClass上定义__contains__ ,以允许您使用in来查找它。

class RangeClass(object):
   ...
  def __contains__(self, val):
    return self.address <= val < (self.address + self.size)

 ...
  if value in r:

在Range中实现比较运算符,对范围列表进行排序,并使用bisect搜索值所属的范围:

import bisect
def find_range(value):
    index = bisect.bisect(rangelist, value)
    if index not in (0, len(rangelist)):
        index -= 1
    return rangelist[index]

谢谢大家

我实际上正在使用unutbu提出的方法。

此外,我添加了另一个优化:

if(value <= first_range_value or value >= last_range_value):
    return None

其中first_range_value和last_range_value之前已经计算过,它们是最小的r.address值和最大的r.address + r.size值。

这在我的应用程序中值得,但实际上取决于范围和值的分布。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM