使用 Spark Scala 进行区间合并

Posted

技术标签:

【中文标题】使用 Spark Scala 进行区间合并【英文标题】:Interval merge using Spark Scala 【发布时间】:2019-10-14 21:32:04 【问题描述】:

我有一个区间列表,只要有重叠,我想合并它们。

示例:List((1,1),(2,2),(4,4),(5,5)) 这里想要的输出是List((1,2),(4,5))

我有一个价值 2.5GB 的数字列表,我想将其转换为范围。

注意:输入列表中没有重复项

步骤

    输入:List[Int] . 映射到元组列表:List((a,a),(b,b), ...)。 使用范围合并逻辑减少它。
val l = List(List((1,1)),List((2,2)),List((4,4)),List((5,5)))
val list =sc.parallelize(l)

def merge(r1:(Int,Int),r2:(Int,Int))  :(Int,Int)  = 
    if(r1._2+1==r2._1) (r1._1,r2._2)
    else if(r2._2+1 == r1._1) (r2._1,r1._2)
    else null


val res = list.reduce((x,y) =>
   x.map(r1 => 
        y.map(r2 => 
            val m = merge(r1,r2)
             m match 
                case null => List(r1,r2)
                case _ => List(m)
             
         ).flatten
    ).flatten
)

res: List[(Int, Int)] = List((4,5), (2,2), (1,2))

实际输出是res: List[(Int, Int)] = List((4,5), (2,2), (1,2)),正如我所期望的List((4,5),(1,2))

编辑:我的解决方案

我尝试了以下代码。它似乎使用少量输入,但我的原始数据花费了太长时间。 还有比这更好的解决方案吗?

def overlap(x: (Int,Int),y:(Int,Int)) = 
    if(x._2==y._1) (x._1,y._2)
    else if(x._1==y._2) (y._1,x._2)
    else null


def isOverlapping(x: (Int,Int),y:(Int,Int)) = 
    x._1 == y._1 || x._1 == y._2 || x._2==y._1 || x._2==y._2 


val res = list.reduce((x,y) =>
  val z =  x.map(r1 => 
        y.map(r2 => 
            val m = merge(r1,r2)
             m match 
                case null => List(r1,r2)
                case _ =>
                     List(m)
                
             
         ).flatten
    ).flatten
    
//-------compressing the accumulated list z to merge overlapping tuples

    z.foldLeft(List[(Int,Int)]())  (acc, i) => 
    if (!acc.exists(isOverlapping(i, _)))
        i +: acc
      else
        acc.map(x => 
            val m = overlap(x,i)
             m match 
                case null => x
                case _ => m
             
        )
    
//---------

)


res: List[(Int, Int)] = List((4,5), (1,2))

【问题讨论】:

你能解释更多“只要有重叠就合并”吗?提供更多示例? 在您的示例中,List((1,1),(2,2),(4,4),(5,5)) 似乎具有相同的值,每个元组都是这种情况吗?在您的示例中,元组也是排序的。总是这样吗? @AlexandrosBiratsis 是的,元组中存在相同的值,它们也已排序。 @C.S.ReddyGadipally 在你看到连续的元组时合并,即如果有List((1,1),(2,2)),它应该合并为List((1,2)) @Learner,输入 List((1,1),(2,2),(3,3),(4,4),(5,5)) 的输出应该是什么? 【参考方案1】:

我最近解决了这个问题。我使用 List[List[Int]] 作为我的收藏。

我的方法是使用排序集合,这样当我们实际尝试减少重叠间隔时,我们会利用排序(我们使用开始位置作为键开始排序,但如果两个开始位置相等,我们使用结束位置)并且可以完成 O(nlogn) 复杂度的问题。我专门使用了 sorted Set,这样如果有重复的间隔,在我们进一步减少它们之前将其删除。

一旦集合被排序,那么我们只需要检查相邻的对是否重叠。我通过检查 1stPair.end >= 2ndPair.Start 来做到这一点。如果为真,则表示 Pairs 是重叠的,我们可以通过取 (1stPair.start,max(1stPair.end,2ndPair.end)) 将这 2 Pairs 变为 1 pair。这里不需要检查对之间的开始间隔,因为它是有序的,所以 2ndPair.start 将始终 >= 1stPair.start。这是我们通过使用排序集合获得的节省。

我假设,如果这些对彼此相邻而没有重叠,我仍然认为这是重叠并减少了它。例如([1,2],[2,3] 减少为 [1,3])。整个解决方案的时间复杂度是排序的复杂度。由于我使用的是 SortedSet 自带的内置排序算法,我猜它提供了最快的 sort(O(nlogn)。为了减少间隔,它只有 1 次通过集合,所以复杂度是线性的。比较这些复杂度和“O( n)”的重要性低于“O(nlogn)”。因此总体复杂度为“O(nlogn)”。这是在 Scala Worksheet 中运行并检查了其他几个输入,它工作正常。

 import scala.collection.SortedSet
  object order extends Ordering[List[Int]] 
    override def compare(a: List[Int], b: List[Int]): Int = 
      if (a(0) != b(0)) a(0) - b(0)
      else a(1) - b(1)
    
  
  val sorted_input = SortedSet(List(6, 9), List(1, 4), List(3, 5), List(8, 12))(order)
  def deduce(list: List[List[Int]], pair: List[Int]): List[List[Int]] = 
    val finalList = (pair, list) match 
      case (pair, head :: tail) if (pair(0) <= head(1)) => List(head(0), if (pair(1) > head(1)) pair(1) else head(1)) :: tail
      case (pair, emptyList)                            => pair :: emptyList
    
    finalList
                                                
  sorted_input.foldLeft(List[List[Int]]())(deduce) //> res0: List[List[Int]] = List(List(6, 12), List(1, 5))

【讨论】:

以上是关于使用 Spark Scala 进行区间合并的主要内容,如果未能解决你的问题,请参考以下文章

求多个区间合并后区间大小的巧妙解决方法差分

从合并石子学区间DP

LeetCode56:合并区间

区间合并一些小思路

合并数组

POJ 3667 Hotel(线段树区间合并)