【问题标题】:Comparing overlapping ranges比较重叠范围
【发布时间】:2025-12-01 21:20:09
【问题描述】:

我将使用 Scala 语法来问这个问题,即使这个问题确实与语言无关。

假设我有两个列表

val groundtruth:List[Range]
val testresult:List[Range]

我想找到testresult 中与groundtruth 中的某些元素重叠的所有元素。

我可以这样做:

def overlaps(x:Range,y:Range) = (x contains y.start) || (y contains x.start)
val result = testresult.filter{ tr => groundtruth.exists{gt => overlaps(gt,tr)}}

但这需要O(testresult.size * groundtruth.size) 的运行时间。

有没有更快的算法来计算这个结果,或者有什么数据结构可以让exists测试更高效?


附:该算法应该适用于使用如下表达式生成的groundtruthtestresult。换句话说,不能保证列表中范围之间的关系,Ranges 的平均大小为 100 或更大。

(1 to 1000).map{x =>
   val midPt = r.nextInt(100000);
   ((midPt - r.nextInt(100)) to (midPt + r.nextInt(100)));
}.toList

【问题讨论】:

    标签: algorithm data-structures nlp overlapping-matches


    【解决方案1】:

    试试interval treeCormen, Leiserson, Rivest and Stein 在(IIRC)第 14 章中讨论这些内容。

    或者,如果您的区间列表均已排序且列表中的区间不重叠,则以下算法可在线性时间内解决您的问题,并且只需遍历两个列表即可:

    (define interval cons)
    (define lower car)
    (define upper cdr)
    
    (define (overlap a b)
      (cond ((or (null? a) (null? b)) '())
            ((< (upper a) (lower b))
             (overlap (cdr a) b))
            ((> (lower a) (upper b))
             (overlap a (cdr b)))
            (#t  ;; (car a) and (car b) overlap
                 ;; EDIT: there's a bug in the following part.
                 ;; The code shouldn't skip over both cars at once,
                 ;; since they may also overlap with further intervals.
                 ;; However, I'm too tired to fix this now.
             (cons (interval (max (lower a) (lower b))
                             (min (upper a) (upper b)))
                   (overlap a b)))))
    

    (希望你能看懂 Scheme :)

    【讨论】:

    • 区间树看起来很完美。我回家后去实施。
    • @Ken:请注意,线性时间算法在进行了一些小的调整后适用于区间树,因为它们会为您进行排序,但以智能方式对区间进行排序可能比生成更容易一个平衡的区间搜索树。
    【解决方案2】:

    如果您可以按范围起始值对groundtruth 列表进行排序,那么对于testresult 中的每个范围,您可以进行二分搜索以获取下限小于或等于相关范围的范围子集.然后,您需要按顺序搜索该子集以查找其上限大于或等于您正在测试的范围的上限的那些。

    最坏的情况仍然是 O(n^2),因为可能所有 groundtruth 范围都有一个符合标准的下限,但实际数据的运行时间可能会少得多。

    【讨论】:

      【解决方案3】:

      如果 groundtruth 存储在散列集中,检查其中是否存在测试结果成员将是 O(n)。

      编辑:我没有意识到您只使用由它们的端点表示的范围。哦!

      某种基于树的结构一定是你最好的选择。

      【讨论】:

      • “重叠”关系不是equivalence relation,因此它不适合在哈希表中进行恒定时间查找。
      • 正确,但整数相等,因此您可以在 groundtruth 哈希集中的 testresult 中查找各个整数。操作的总成本与测试结果的大小成线性关系。
      • 所以你建议val ranges=new scala.collection.mutable.HashSet[Int]; for ( gt &lt;- groundtruth; point &lt;- gt) ranges += point; val result = testresult.filter{ tr =&gt; tr.exists{point =&gt; ranges(point)}} 当范围很短时它可以很好地工作,但是当范围很长时它会浪费很多时间(即平均大小为100)。
      • 我对你的 scala 代码应该是什么意思一头雾水。我没有意识到您的测试结果存储为范围。