如何生成元组集的传递闭包?

发布于 2024-11-07 00:10:48 字数 198 浏览 0 评论 0原文

生成一组元组的传递闭包的最佳方法是什么?

示例:

  • 输入 Set((1, 2), (2, 3), (3, 4), (5, 0))
  • 输出 Set((1, 2), (1 , 3), (1, 4), (2, 3), (2, 4), (3, 4), (5, 0))

What is the best way to generate transitive closure of a set of tuples?

Example:

  • Input Set((1, 2), (2, 3), (3, 4), (5, 0))
  • Output Set((1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4), (5, 0))

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

南冥有猫 2024-11-14 00:10:48
//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))))

这不是一个非常有效的实现,但是很简单。

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

This is not a very efficient implementation, but it is simple.

卷耳 2024-11-14 00:10:48

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
    }

With the help of 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)
}

it becomes rather simple:

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
}

Another way of writing closure is this:

def closure(seed: Int) = unfoldRight(seed) {
    case n if map.get(n) != seed => map get n map (x => seed -> x -> x)
    case _ => None
}

I'm not sure which way I like best, myself. I like the elegance of testing for Some(seed) to avoid loops, but, by the same token, I also like the elegance of mapping the result of map get n.

Neither version returns seed -> seed for loops, so you'll have to add that if needed. Here:

    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
    }
云淡月浅 2024-11-14 00:10:48

将问题建模为有向图,如下所示:

将元组中的数字表示为图中的顶点。
那么每个元组(x,y)代表从x到y的有向边。之后,使用Warshall算法求出图的传递闭包。

对于生成的图,每个有向边都会转换为 (x, y) 元组。这就是元组集合的传递闭包。

Model the problem as a directed graph as follows:

Represent the numbers in the tuples as vertices in a graph.
Then each tuple (x, y) represents a directed edge from x to y. After that, use Warshall's Algorithm to find the transitive closure of the graph.

For the resulting graph, each directed edge is then converted to an (x, y) tuple. That is the transitive closure of the set of tuples.

指尖上得阳光 2024-11-14 00:10:48

假设您拥有的是 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 的路径足够长(递归不是尾递归),则此代码可能会导致堆栈溢出。
  • 对于大型图,如果您想要一个不可变的 Map,最好将 tc 制作为一个可变的 Map,然后在最后转换它。
  • 如果您的元素确实像示例中那样很小,那么您可以通过使用数组而不是映射来显着提高性能,尽管这样做会使某些事情变得复杂。

为了消除堆栈溢出问题(对于 DAG),您可以进行拓扑排序,反转它,然后按顺序处理项目。但另请参阅此页面:

最著名的图传递闭包算法

Assuming that what you have is a DAG (there are no cycles in your example data), you could use the code below. It expects the DAG as a Map from T to List[T], which you could get from your input using

input.groupBy(_._1) mapValues ( _ map (_._2) )

Here's the transitive closure:

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
}

This code figures out the closure for each element just once. However:

  • This code can cause a stack overflow if there are long enough paths through the DAG (the recursion is not tail recursion).
  • For a large graph, you would probably be better off making tc a mutable Map and then converting it at the end if you wanted an immutable Map.
  • If your elements were really small integers as in your example, you could improve performance significantly by using Arrays rather than Maps, although doing so would complicate some things.

To eliminate the stack overflow problem (for DAGs), you could do a topological sort, reverse it, and process the items in order. But see also this page:

best known transitive closure algorithm for graph

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文