查找具有给定间隔的非空交集的间隔

时间:2018-09-10 11:47:21

标签: python sorting

问题如下:我有一个列表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)

我相信代码可以完成工作。

是否有更简单/更智能的方法来解决此问题?

4 个答案:

答案 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软件包,尽管它的文档和方法似乎很有限。

我希望这会有所帮助。