堆栈级别太深(SystemStackError)DFS

时间:2018-11-23 17:50:47

标签: ruby graph

在执行 DFS算法以查找最大的组件时,我遇到stack level too deep的问题。事实是,我正在将map(.osm)文件转换为图形。我想找到最大的组件。但是,使用较小的地图(缩放较大)可以工作,但是使用较大的图形可以使我得到高于状态的误差,您能帮我吗? 这是导致问题的DFS代码:

def dfsFunction (vertex)
    @dfs[vertex] = true
    @component[@componentIndex] << vertex
    adjectenVertices = []
    @edges.each do |edge|
        if edge.v1.id == vertex.id
            adjectenVertices << edge.v2
        elsif edge.v2.id == vertex.id
            adjectenVertices << edge.v1
        end
    end
    adjectenVertices.each_with_index do |vertex|
        if @dfs[vertex] == false
            dfsFunction(vertex)
        end
    end
end




@dfs = {}
    @vertices.each do |id,vertex|
        @dfs[vertex] = false
    end
    @component = {}
    @componentIndex = -1
    @dfs.each do |vertex, boolean|
        if @dfs[vertex] == false
            @componentIndex = @componentIndex +1
            @component[@componentIndex] = []
            dfsFunction(vertex)
        end
    end

2 个答案:

答案 0 :(得分:0)

这里的问题是,您使用了递归,对于复杂的图形它越来越深了。对于每次递归,必须增加堆栈以保存结果值。 要解决此问题,您可以做两件事:更改为循环或重写经过尾调用优化的递归,并为ruby启用尾调用优化(有关一些解释,请参见http://nithinbekal.com/posts/ruby-tco/)。

我制作了一个小版本,使用while循环。希望对您有帮助...

class Edge
  attr_reader :left, :right

  def initialize(left, right)
    @left, @right = left, right
  end

  def vertexes
    [left, right]
  end
end

class Graph
  attr_reader :vertexes, :edges

  def initialize(vertexes, edges)
    @vertexes, @edges = vertexes, edges
  end

  def add_edges(new_edges)
    new_edges.each { |edge| add_edge(edge) }
  end

  def add_edge(edge)
    add_vertex(edge.left)
    add_vertex(edge.right)
    @edges << edge
    @edges.uniq!
    edge
  end

  def add_vertex(vertex)
    @vertexes << vertex
    @vertexes.uniq!
    vertex
  end

  def edges_for(vertex) 
    edges.select { |edge| edge.left == vertex || edge.right == vertex }
  end
end

module DFS
  def self.components(graph)
    vertexes = graph.vertexes.clone
    graph.vertexes.map do |vertex|
      next unless vertexes.delete(vertex)
      component = Graph.new([vertex], [])
      edges = graph.edges_for(vertex)
      while (new_vertexes = edges.map(&:vertexes).flatten & vertexes).any? do
        component.add_edges(edges)
        edges = new_vertexes.map { |new_vertex| graph.edges_for(new_vertex) }.flatten
        vertexes -= new_vertexes
      end
      component.add_edges(edges)
      component
    end.compact
  end
end

答案 1 :(得分:0)

MRI默认情况下关闭了尾递归优化。但是可能会打开它:

RubyVM::InstructionSequence.compile_option = {
 tailcall_optimization: true,
 trace_instruction: false
}

此外,代码本身必须使用尾部递归,例如:

  def test(v)
    return unless v > 0
    p v
    test(v-1) 
  end

代替:

def test(v)
  test(v-1) if v > 0
  p v
end