寻找点的相应间隔

时间:2015-10-14 00:10:53

标签: python loops search intervals

我有5个元组A, B, C, DE代表区间。它们的交集是空的(对于它们的每一对)并且它们是排序的,例如一个间隔的上限小于下一个间隔的下限。

例如:

A = (5, 10)
B = (21, 29)
C = (134, 160)
D = (900, 1050)
E = (1080, 1100)

intervals = [A, B, C, D, E]

我还有一个按递增顺序排序的列表X

例如:

X = [6, 28, 130, 1000, 1129]

如您所见,X中的每个数字都可以属于或不属于一个区间。由于间隔交叉是空的,每个数字最多可以属于一个间隔 此外,通过施工,每个区间只有一个数字。

我试图知道X中的每个数字属于哪个区间,如果有的话 所以对于我的例子,输出应该是:

output = [(6, A), (28, B), (None, C), (1000, D), (None, E)]

表示数字6, 28, 1000分别属于区间A, B, D,并且没有数字属于区间CE

为了找到X中每个数字所属的区间,我做了以下内容:

output = []
for interval in intervals:
    for number in X:
        if interval[0] <= number and number <= interval[1]:
            found_interval = True
            output.append((number, interval))
            break

    if not found_interval:
        output.append((None, interval))

这应该有用,但我认为应该有更快的方法。我想避免为每个间隔循环X。升级后的解决方案将循环查找未找到任何间隔的剩余数字。

有更快的方法吗?

2 个答案:

答案 0 :(得分:2)

[在编辑时:我的原始代码没有正确处理x是正确端点之一的情况。修订后的代码修复了这一问题,扩展了测试示例以显示它如何处理其他边缘情况]

也许这会有所帮助:按照排序顺序创建所有端点的列表,如

ends = [5,10,21,29,134,160,900,1050,1080,1100]

并使用bisect模块查找点x在此列表中的位置。这是二进制搜索,因此比线性搜索更有效。如果它落在两个索引(i-1,i)之间,其中i是奇数,则x在相应的区间中。否则就没有了。

此外,使用元组列表intervals来加载已排序的端点列表也很容易:

from bisect import bisect

def place(x,endpoints):
    i = bisect(endpoints,x)
    if i%2 == 0:
        if x == endpoints[i-1]:
            return endpoints[i-1],endpoints[i]
        else:
            return None
    else:
        return endpoints[i-1],endpoints[i]

A = (5, 10)
B = (21, 29)
C = (134, 160)
D = (900, 1050)
E = (1080, 1100)

intervals = [A, B, C, D, E]

ends = []
for interval in intervals:
    ends.extend(interval)

xs = [3, 5, 6, 10, 28, 130, 1000, 1129]

for x in xs:
    print(str(x),':',str(place(x,ends)))

输出:

3 : None
5 : (5, 10)
6 : (5, 10)
10 : (10, 21)
28 : (21, 29)
130 : None
1000 : (900, 1050)
1129 : None

答案 1 :(得分:2)

您可以在线性时间内查找此问题的交叉点:

  • 创建两个变量来跟踪当前值和区间索引。
  • 只要您没有查看任一列表中的每个元素,请检查 如果当前值属于间隔。
  • 如果值属于当前间隔,则将当前值和当前间隔索引增加一个。 由于值和间隔都已排序且间隔不重叠,没有其他元素可以属于当前间隔,因此我们可以安全地移动。
  • 如果该值小于当前间隔的下限,请增加当前值索引,因为该值不能属于任何具有较大下限的区间。
  • 如果该值大于当前间隔的上限,请增加当前间隔索引,因为任何其他值也将大于此边界。

    def find_intersection(values, intervals):
        output = []
        value_index = 0
        interval_index = 0
    
        while value index < len(values) and interval_index < len(intervals):
            current_value = values[value_index]
            current_interval = intervals[interval_index]
            lower_bound, upper_bound = current_interval
    
            if current_value < lower_bound:
                output.append((None, current_value))
                # This value cannot belong to any greater interval.
                value_index += 1
            elif current_value > upper_bound:
                # No other value can belong to this interval either.
                interval_index += 1
            else:
                output.append((current_interval, current_value))
                # At most one value per interval and one interval per value.
                value_index += 1
                interval_index += 1
    
        # If we ran out of intervals all remaining values do not belong to any.
        for v in values[value_index:]:
            output.append((None, v))
    
        return output
    

最糟糕的情况是,没有数字属于任何区间,我们必须完全迭代每个列表(在while循环中intervals和for循环中的values),因此复杂度为{{1其中O(n + m)是值的数量,n是间隔的值。