生成一组元组的传递闭包的最佳方法是什么?
示例:
Set((1, 2), (2, 3), (3, 4), (5, 0))
Set((1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4), (5, 0))
答案 0 :(得分:7)
//one transitive step
def addTransitive[A, B](s: Set[(A, B)]) = {
s ++ (for ((x1, y1) <- s; (x2, y2) <- s if y1 == x2) yield (x1, y2))
}
//repeat until we don't get a bigger set
def transitiveClosure[A,B](s:Set[(A,B)]):Set[(A,B)] = {
val t = addTransitive(s)
if (t.size == s.size) s else transitiveClosure(t)
}
println(transitiveClosure(Set((1,2), (2,3), (3,4))))
这不是一个非常有效的实现,但它很简单。
答案 1 :(得分:3)
在unfold
的帮助下,
def unfoldRight[A, B](seed: B)(f: B => Option[(A, B)]): List[A] = f(seed) match {
case Some((a, b)) => a :: unfoldRight(b)(f)
case None => Nil
}
def unfoldLeft[A, B](seed: B)(f: B => Option[(B, A)]) = {
def loop(seed: B)(ls: List[A]): List[A] = f(seed) match {
case Some((b, a)) => loop(b)(a :: ls)
case None => ls
}
loop(seed)(Nil)
}
变得相当简单:
def transitiveClosure(input: Set[(Int, Int)]) = {
val map = input.toMap
def closure(seed: Int) = unfoldLeft(map get seed) {
case Some(`seed`) => None
case Some(n) => Some(seed -> n -> (map get n))
case _ => None
}
map.keySet flatMap closure
}
撰写closure
的另一种方式是:
def closure(seed: Int) = unfoldRight(seed) {
case n if map.get(n) != seed => map get n map (x => seed -> x -> x)
case _ => None
}
我不确定我最喜欢哪种方式,我自己。我喜欢Some(seed)
测试的优雅以避免循环,但同样,我也喜欢映射map get n
结果的优雅。
这两个版本都没有返回seed -> seed
for循环,所以如果需要,你必须添加它。这里:
def closure(seed: Int) = unfoldRight(map get seed) {
case Some(`seed`) => Some(seed -> seed -> None)
case Some(n) => Some(seed -> n -> (map get n))
case _ => None
}
答案 2 :(得分:2)
将问题建模为有向图,如下所示:
将元组中的数字表示为图形中的顶点。 然后每个元组(x,y)表示从x到y的有向边。之后,使用 Warshall的算法来查找图的传递闭包。
对于结果图,然后将每个有向边转换为(x,y)元组。这是元组集的传递闭包。
答案 3 :(得分:0)
假设您拥有的是DAG(示例数据中没有循环),您可以使用下面的代码。它期望DAG作为从T到List [T]的映射,您可以使用
从输入中获取input.groupBy(_._1) mapValues ( _ map (_._2) )
这是传递闭包:
def transitiveClosure[T]( dag: Map[ T, List[T] ] ) = {
var tc = Map.empty[ T, List[T] ]
def getItemTC( item:T ): List[T] = tc.get(item) match {
case None =>
val itemTC = dag(item) flatMap ( x => x::getItemTC(x) )
tc += ( item -> itemTC )
itemTC
case Some(itemTC) => itemTC
}
dag.keys foreach getItemTC
tc
}
此代码仅为每个元素计算出一次闭包。但是:
要消除堆栈溢出问题(对于DAG),您可以进行拓扑排序,反转它,并按顺序处理项目。但另见本页: