问题如下:我有一个列表intervals
,该列表由(start, end
)形式的元组组成[带有start <= end
]。每个元组代表一个间隔(实线)。我们假设intervals
中的间隔彼此不重叠。给定一个新的间隔(s,e)
,我想编写一个Python函数来检查(s, e)
是否与intervals
中的任何间隔重叠。如果(s, e)
与intervals
中的间隔至少有一个非空交集,则函数应返回列表intervals
中这些间隔的索引。
说该函数称为find_intersections
。然后,根据intervals = [(1, 3.5), (5.5, 8.7), (10.2, 22.6), (22.7, 23.1)]
,预期输出将为:
find_intersection(intervals, (3.2, 5.))
返回array([0])
find_intersection(intervals, (6.1, 7.3))
返回array([1])
find_intersection(intervals, (9.1, 10.2))
返回No intersection.
find_intersection(intervals, (5.8, 22.9))
返回array([1, 2, 3])
。我为find_intersection
编写的代码是:
import itertools
def find_intersection(intervals, new_interval):
_times = sorted(list(itertools.chain.from_iterable(intervals)))
ind = np.searchsorted(_times, np.asarray(new_interval))
parity = np.mod(ind, 2)
if (not np.any(parity)) and ind[1] == ind[0]:
print('No intersection.')
elif parity[0] == 1:
ub = ind[1] if parity[1] == 1 else ind[1] - 1
return np.arange((ind[0] - 1) / 2, (ub - 1) / 2 + 1)
elif parity[1] == 1:
lb = ind[0] if parity[0] == 1 else ind[0] + 1
return np.arange((lb - 1) / 2, (ind[1] - 1) / 2 + 1)
else:
lb = ind[0] if parity[0] == 1 else ind[0] + 1
ub = ind[1] if parity[1] == 1 else ind[1] - 1
return np.arange((lb - 1) / 2, (ub - 1) / 2 + 1)
我相信代码可以完成工作。
是否有更简单/更智能的方法来解决此问题?
答案 0 :(得分:3)
intervals = [(1, 3.5), (5.5, 8.7), (10.2, 22.6), (22.7, 23.1)]
def find_intersection(intervals, new_interval):
start, end = new_interval
return (i for i, (a, b) in enumerate(intervals)
if (a < start < b) or (a < end < b) or (a > start and b < end))
candidates = ((3.2, 5.), (6.1, 7.3), (9.1, 10.2), (5.8, 22.9))
for c in candidates:
print(c, "->", list(find_intersection(intervals, c)))
答案 1 :(得分:1)
列表中的第i
个间隔与
start[i] < e and s < end[i].
因此,通过增加start
值来对时间间隔进行排序,然后扫描列表,直到找到第一个end[i] > s
并继续使用start[i] < e
。随时随地保存索引。
这需要O(N Log N)进行排序,然后以Θ(N)最坏情况进行搜索。
如果允许排序,并且您有许多(s,e)
的时间间隔尝试,则通过二分法搜索i
和{{1 }}值,而不是线性搜索。这将成本从Θ(M + K)降低到Θ(Log N),其中M是第一个重叠的平均索引(通常M = O(N)),K是重叠的平均数量。
如果不允许排序,则需要使用上述条件依次测试每个时间间隔是否有重叠。成本Θ(N)。
答案 2 :(得分:1)
如果两个间隔相交
def intersect(i1, i2):
return max(i1[0], i2[0]) < min(i1[1], i2[1])
因此,仅是列表理解
def find intersection(intervals, i2):
return [i1 for i1 in intervals if intersect(i1, i2)]
答案 3 :(得分:1)
您可以利用interval tree package,它提供了内置函数,这些函数返回大量类似的查询。不幸的是,似乎没有一个函数返回重叠间隔的索引,而仅返回间隔本身。例如:
import IntervalTree
intervals = [(1, 3.5), (5.5, 8.7), (10.2, 22.6), (22.7, 23.1)]
tree = IntervalTree.from_tuples(intervals)
tree.search(3.2, 5.) % ~~> {Interval(1, 3.5)}
tree.search(9.1, 10.2) % ~~> set()
tree.search(5.8, 22.9) % ~~> {Interval(5.5, 8.7), Interval(10.2, 22.6), Interval(22.7, 23.1)}
tree[5.8:22.9] % ~~> same as above
一旦有了所需的时间间隔集,就可以轻松地返回其索引:
[intervals.index((tr.begin, tr.end)) for tr in tree[5.8:22.9]]
如果间隔列表很大,则可能要改用字典并查找索引,因为.index
方法花费的时间是列表长度的线性时间。
尽管安装软件包来解决此问题可能是开销,但是如果您要处理间隔问题,则可以使用interval tree data structure并利用写入该软件包中的基础优化方法可能是值得的。为了获得更好的性能,您可能还想检查ncls软件包,尽管它的文档和方法似乎很有限。
我希望这会有所帮助。