【问题标题】:Find intervals which have a non-empty intersection with a given interval查找与给定区间具有非空交集的区间
【发布时间】:2019-02-14 20:21:55
【问题描述】:

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

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

有没有更简单/更智能的方法来解决这个问题?

【问题讨论】:

标签: python sorting


【解决方案1】:
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)))

【讨论】:

    【解决方案2】:

    列表中的i-th 间隔重叠当且当

    start[i] < e and s < end[i].
    

    因此,通过增加start 的值对区间进行排序,然后扫描列表直到找到第一个end[i] &gt; s 并继续直到start[i] &lt; e。随时保留索引。

    这需要 O(N Log N) 进行排序,然后是 Θ(N) 最坏情况进行搜索。


    如果允许排序并且您有许多 (s,e) 区间可供尝试,则通过在 start[i]end[i] 值中的二分搜索而不是通过线性搜索找到第一个和最后一个 i 可能会很有用.这将成本从 Θ(M + K) 降低到 Θ(Log N),其中 M 是第一个重叠的平均索引(通常 M = O(N)),K 是重叠的平均数。


    如果不允许排序,则需要使用上述条件依次测试每个间隔是否重叠。成本 Θ(N)。

    【讨论】:

      【解决方案3】:

      如果两个区间相交

      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)]
      

      【讨论】:

        【解决方案4】:

        您可以使用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 包,尽管它的文档和方法似乎有限。

        我希望这会有所帮助。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-01-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多