我有5个元组A, B, C, D
和E
代表区间。它们的交集是空的(对于它们的每一对)并且它们是排序的,例如一个间隔的上限小于下一个间隔的下限。
例如:
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
,并且没有数字属于区间C
和E
。
为了找到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
。升级后的解决方案将循环查找未找到任何间隔的剩余数字。
有更快的方法吗?
答案 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
是间隔的值。