【问题标题】:Tarjan algorithm - Python to scalaTarjan 算法 - Python 到 Scala
【发布时间】:2013-08-17 14:42:49
【问题描述】:

我正在尝试将递归 python code for tarjan algorithm 转换为 scala,尤其是这部分:

def tarjan_recursive(g):
        S = []
        S_set = set()
        index = {}
        lowlink = {}
        ret = []
        def visit(v):
                index[v] = len(index)
                lowlink[v] = index[v]
                S.append(v)
                S_set.add(v)

                for w in g.get(v,()):
                        print(w)
                        if w not in index:
                                visit(w)
                                lowlink[v] = min(lowlink[w], lowlink[v])
                        elif w in S_set:
                                lowlink[v] = min(lowlink[v], index[w])
                if lowlink[v] == index[v]:
                        scc = []
                        w = None
                        while v != w:
                                w = S.pop()
                                scc.append(w)
                                S_set.remove(w)
                        ret.append(scc)

        for v in g:
                print(index)
                if not v in index:
                        visit(v)
        return ret

我知道 scala 中有 tarjan 算法 herehere 但它没有返回好的结果并从 python 翻译帮助我理解它。

这就是我所拥有的:

def tj_recursive(g: Map[Int,List[Int]])= {
        var s : mutable.ListBuffer[Int] = new mutable.ListBuffer()
        var s_set : mutable.Set[Int] = mutable.Set()
        var index : mutable.Map[Int,Int] =  mutable.Map()
        var lowlink : mutable.Map[Int,Int]=  mutable.Map()
        var ret : mutable.Map[Int,mutable.ListBuffer[Int]]= mutable.Map()

        def visit(v: Int):Int = {
                 index(v) = index.size
               lowlink(v) = index(v)
               var zz :List[Int]= gg.get(v).toList(0)
                            for( w <- zz) {
                  if( !(index.contains(w)) ){
                     visit(w)
                     lowlink(v) = List(lowlink(w),lowlink(v)).min
                   }else if(s_set.contains(w)){
                     lowlink(v)=List(lowlink(v),index(w)).min
                   }
               }

               if(lowlink(v)==index(v)){
                  var scc:mutable.ListBuffer[Int] = new mutable.ListBuffer()
                  var w:Int=null.asInstanceOf[Int]
                  while(v!=w){
                    w= s.last
                    scc+=w
                    s_set-=w
                  }
           ret+=scc
        }
        }

   for( v <- g) {if( !(index.contains(v)) ){visit(v)}}
   ret
}

我知道这根本不是 scala 方式(而且不干净......)但我计划在第一个版本工作时慢慢将其更改为更实用的样式.

现在,我收到了这个错误:

type mismatch;  found   : Unit  required: Int

在这一行

if(lowlink(v)==index(v)){ 

我认为它来自这条线,但我不确定:

if( !(index.contains(w)) 

但是调试它真的很难,因为我不能只打印我的错误......

谢谢!

【问题讨论】:

    标签: python scala translate tarjans-algorithm


    【解决方案1】:

    以下是 Python 的相当直译:

    def tj_recursive(g: Map[Int, List[Int]])= {
      val s = mutable.Buffer.empty[Int]
      val s_set = mutable.Set.empty[Int]
      val index = mutable.Map.empty[Int, Int]
      val lowlink = mutable.Map.empty[Int, Int]
      val ret = mutable.Buffer.empty[mutable.Buffer[Int]]
    
      def visit(v: Int): Unit = {
        index(v) = index.size
        lowlink(v) = index(v)
        s += v
        s_set += v
    
        for (w <- g(v)) {
          if (!index.contains(w)) {
            visit(w)
            lowlink(v) = math.min(lowlink(w), lowlink(v))
          } else if (s_set(w)) {
            lowlink(v) = math.min(lowlink(v), index(w))
          }
        }
    
        if (lowlink(v) == index(v)) {
          val scc = mutable.Buffer.empty[Int]
          var w = -1
    
          while(v != w) {
            w = s.remove(s.size - 1)
            scc += w
            s_set -= w
          }
    
          ret += scc
        }
      }
    
      for (v <- g.keys) if (!index.contains(v)) visit(v)
      ret
    }
    

    它在 e.g. 上产生相同的输出:

    tj_recursive(Map(
      1 -> List(2),    2 -> List(1, 5), 3 -> List(4),
      4 -> List(3, 5), 5 -> List(6),    6 -> List(7),
      7 -> List(8),    8 -> List(6, 9), 9 -> Nil
    ))
    

    您的实现的最大问题是返回类型为visit(应该是Unit,而不是Int),并且您在最终迭代中遍历图形的项目而不是图形的键for-comprehension,但我对样式和清晰度进行了许多其他编辑(同时仍保持基本形状)。

    【讨论】:

    • 嘿特拉维斯,谢谢你的帮助。我虽然已经解决了图表项目的问题,但没有:/我现在要检查缓冲区。再次感谢您!
    【解决方案2】:

    这是一个迭代版本。它是Wikipedia中算法的递归版本的翻译。

    case class Arc[A](from:A, to:A)
    
    class SparseDG[A](src: Iterable[Arc[A]]) {
    
      val verts = (src.map(_.from) ++ src.map(_.to)).toSet.toIndexedSeq
      val qVert = verts.size
      val vertMap = verts.zipWithIndex.toMap
      val indexedSrc = src.map{ arc => Arc(vertMap(arc.from), vertMap(arc.to)) }
    
      val exit  = (0 until qVert)
                  .map(v => indexedSrc.filter(_.from == v).map(_.to).toIndexedSeq)
    
    
      lazy val tarjan_iterative: Seq[Seq[A]] = {
        trait Step
        case object SetDepth           extends Step
        case object ConsiderSuccessors extends Step
        case object CalcLowlink        extends Step
        case object PopIfRoot          extends Step
        case class StackFrame(v:Int, next:Step)
    
        val result = Buffer[Seq[A]]()
        val index = new Array[Int](qVert).map(_ => -1)   // -1 = undefined
        val lowlink = new Array[Int](qVert).map(_ => -1) // -1 = undefined
        val wIndex = new Array[Int](qVert)               // used to iterate w nodes
        var _index = 0
        val s = Stack[Int]()
        val isRemoved = BitSet()
        val strongconnect = Stack[StackFrame]()
    
        (0 until qVert).foreach { v_idx =>
          if(index(v_idx) == -1) {
            strongconnect.push(StackFrame(v_idx, SetDepth))
            while(!strongconnect.isEmpty) {
              val StackFrame(v, step) = strongconnect.pop()
              step match {
                case SetDepth =>
                  index(v) = _index
                  lowlink(v) = _index
                  _index += 1
                  s.push(v)
                  isRemoved.remove(v)
                  strongconnect.push(StackFrame(v, ConsiderSuccessors))
    
                case ConsiderSuccessors =>
                  if(wIndex(v) < exit(v).size){
                    val w = exit(v)(wIndex(v))
                    if(index(w) == -1){
                      strongconnect.push(StackFrame(v, CalcLowlink))
                      strongconnect.push(StackFrame(w, SetDepth))
                    }
                    else{
                      if(!isRemoved.contains(w)){
                        if(lowlink(v) > lowlink(w)) lowlink(v) = index(w)
                      }
                      wIndex(v) += 1
                      strongconnect.push(StackFrame(v, ConsiderSuccessors))
                    }
                  }
                  else{
                    strongconnect.push(StackFrame(v, PopIfRoot))
                  }
    
                case CalcLowlink =>
                  val w = exit(v)(wIndex(v))
                  if(lowlink(v) > lowlink(w)) lowlink(v) = lowlink(w)
                  wIndex(v) += 1
                  strongconnect.push(StackFrame(v, ConsiderSuccessors))
    
                case PopIfRoot =>
                  if(index(v) == lowlink(v)){
                    val buf = Buffer[A]()
                    var w = 0
                    do{
                      w = s.pop()
                      isRemoved += w
                      buf += verts(w)
                    }
                    while(w != v)
                    result += buf.toSeq
                  }
              }
            }
          }
        }
        result.toSeq
      }
    
      lazy val hasCycle = tarjan_iterative.find(_.size >= 2).isDefined
    
      lazy val topologicalSort =
        if(hasCycle) None
        else         Some(tarjan_iterative.flatten.reverse)
    
    }
    

    运行维基百科文章中的示例图:

    val g = new SparseDG(Seq(
            Arc("1","2"),
            Arc("2","3"),
            Arc("3","1"),
            Arc("4","2"),
            Arc("4","3"),
            Arc("6","3"),
            Arc("6","7"),
            Arc("7","6"),
            Arc("4","5"),
            Arc("5","4"),
            Arc("5","6"),
            Arc("8","5"),
            Arc("8","8"),
            Arc("8","7")
          ))
    
    g.tarjan_iterative
    

    返回:

    ArrayBuffer(ArrayBuffer(1, 3, 2), ArrayBuffer(7, 6), ArrayBuffer(4, 5), ArrayBuffer(8))
    

    【讨论】:

      【解决方案3】:

      我知道这篇文章已经过时了,但我最近一直致力于在 Scala 中实现 Tarjans 算法。在代码的实现中,我正在看这篇文章,我突然想到,它可以用更简单的方式完成:

      case class Edge[A](from: A, to: Set[A])
      
      class TarjanGraph[A](src: Iterable[Edge[A]]) {
        lazy val trajan: mutable.Buffer[mutable.Buffer[A]] = {
          var s = mutable.Buffer.empty[A] //Stack to keep track of nodes reachable from current node
          val index = mutable.Map.empty[A, Int] //index of each node
          val lowLink = mutable.Map.empty[A, Int] //The smallest index reachable from the node
          val ret = mutable.Buffer.empty[mutable.Buffer[A]] //Keep track of SCC in graph
      def visit(v: A): Unit = {
        //Set index and lowlink of node on first visit
        index(v) = index.size
        lowLink(v) = index(v)
        //Add to stack
        s += v
        if (src.exists(_.from == v)) {
          for (w <- src.find(e => e.from == v).head.to) {
            if (!index.contains(w)) { //Node is not explored yet
              //Perform DFS from node W
              visit(w)
              //Update the lowlink value of v so it has the value of the lowest node reachable from itself and from node w
              lowLink(v) = math.min(lowLink(w), lowLink(v))
            } else if (s.contains(w)) {
              // Node w is on the stack meaning - it means there is a path from w to v
              // and since node w is a neighbor to node v there is also a path from v to w
              lowLink(v) = math.min(lowLink(v), index(w))
            }
          }
        }
        //The lowlink value haven't been updated meaning it is the root of a cycle/SCC
        if (lowLink(v) == index(v)) {
          //Add the elements to the cycle that has been added to the stack and whose lowlink has been updated by node v's lowlink
          //This is the elements on the stack that is placed behind v
          val n = s.length - s.indexOf(v)
          ret += s.takeRight(n)
          //Remove these elements from the stack
          s.dropRightInPlace(n)
        }
      }
      
      //Perform a DFS from  all no nodes that hasn't been explored
      src.foreach(v => if (!index.contains(v.from)) visit(v.from))
      ret
        }
      
        // A cycle exist if there is a SCC with at least two components
        lazy val hasCycle: Boolean = trajan.exists(_.size >= 2)
        lazy val trajanCycle: Iterable[Seq[A]] = trajan.filter(_.size >= 2).distinct.map(_.toSeq).toSeq
        lazy val topologicalSortedEdges: Seq[Edge[A]] =
          if (hasCycle) Seq[Edge[A]]()
          else trajan.flatten.reverse.flatMap(x => src.find(_.from == x)).toSeq
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-11-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-09
        相关资源
        最近更新 更多