简体   繁体   English

如何使用Dijkstra算法找到图中从顶点到另一个的最短路径

[英]How to find the shortest path from a vertex to other in a graph with a Dijkstra's algorithm

I'm working on a TOP's project where i should do a function that return the shortest path between two vertices of a graph, but i get an error. 我正在开发一个TOP的项目,我应该做一个返回图形的两个顶点之间的最短路径的函数,但是我得到一个错误。 What can i do to solve this? 我该怎么做才能解决这个问题?

Nothing yet because i am a newbie. 还没有,因为我是一个新手。

the Graph class Graph类

class Vertex
  attr_accessor :value, :neighbors, :prev, :dist
  def initialize(value)
      @value = value
      @neighbors = []
      @prev = nil
      @dist = Float::INFINITY
  end

  def add_edge(adjacent_vertex)
      @neighbors << adjacent_vertex
  end
end

class Graph
  attr_reader :vertices
  def initialize
      @vertices = {}
      @dijkstra_source = nil
  end

  def add_vertex(vertex)
      @vertices[vertex.value] = vertex
  end

  def add_edge(vertex1, vertex2)
      @vertices[vertex1.value].add_edge(@vertices[vertex2])
      @vertices[vertex2.value].add_edge(@vertices[vertex1])
  end

  def dijkstra(source)
    return if @dijkstra_source == source
      unvisited = @vertices.values
      unvisited.each do |vertex|
          vertex.dist = Float::INFINITY
          vertex.prev = nil
      end
      @vertices[source].dist = 0
      until unvisited.empty?
          current_node = unvisited.min_by(&:dist)
          break if current_node.dist == Float::INFINITY
          unvisited.delete(current_node)
          current_node.neighbors.each do |v|
              neighbor = @vertices[v]
              if unvisited.include?(neighbor)
                  alt = current_node.dist + 1
                  if alt < neighbor.dist
                    neighbor.dist = alt
                    neighbor.prev = current_node 
                  end
              end
          end
      end
      @dijkstra_source = source
  end

  def find_shortest_path(source, target)
      dijkstra(source)
      path = []
      u = target
      while u
          path.unshift(u)
          u = @vertices[u].prev
      end
      return path
  end
end

Where i instatiane the vertices 我在哪里设置顶点

require_relative 'graph'

class Knight
  attr_reader :character, :graph
  def initialize
      @character = "♞"
      @possible_moves = nil
      @positions = Array.new(8) { Array.new(8, " ") }
      @move_set = [
                    [-1,-2],
                    [-2,-1],
                    [-2,+1],
                    [-1,+2],
                    [+1,+2],
                    [+2,+1],
                    [+2,-1],
                    [+1,-2]
                   ]
      @graph = generate_graph
      generate_edges
  end

  def generate_graph
    graph = Graph.new
      @positions.each_index do |x|
          @positions[x].each_index do |y|
              vertex = Vertex.new([x,y])
              graph.add_vertex(vertex)
          end
      end
      graph
  end

  def generate_edges
      @graph.vertices.each do |key, value|
          @move_set.each do |move|
              x = key[0] + move[0]
              y = key[1] + move[1]
              if (0..7).include?(x) && (0..7).include?(y)
                  vertex = [x,y]
                  value.add_edge(vertex) unless value.neighbors.include?(vertex)
              end
          end
      end
  end
end

knight = Knight.new
knight.generate_edges
p knight.graph.find_shortest_path([1,2],[2,3])

I expect an array which stores inside the shortest path from a source vertex([1,2]) to a target vertex(2,3). 我期望一个数组存储在从源顶点([1,2])到目标顶点(2,3)的最短路径内。

But instead of that what i get is a NilClass error 但不是我得到的是NilClass错误

Traceback (most recent call last):
    1: from knight.rb:50:in `<main>'
/home/brito/the_odin_project/knight_moves/graph.rb:63:in `find_shortest_path': undefined method `prev' for nil:NilClass (NoMethodError)

The issue is with something being not initialized ( nil ) while an attempt to call prev method on it, as might be seen from the error message. 问题是在尝试调用prev方法时,没有初始化( nil ),如错误消息中所示。

The nearest suspicious call is in Graph#find_shortest_path . 最近的可疑调用位于Graph#find_shortest_path If you'd print the @vertices and u from there, you'll see that on the second iteration u becomes the Vertex instance , while @vertices has raw values as keys. 如果您从那里打印@vertices u ,您将看到在第二次迭代时, u成为Vertex实例 ,而@vertices原始值作为键。 The fix would be to change u = @vertices[u].prev to u = @vertices[u].prev.value to keep u being the raw value (note the check for the @vertices[u].prev in the first place. 解决方法是将u = @vertices[u].prevu = @vertices[u].prev.value以保持u原始值 (请注意第一个检查@vertices[u].prev地点。

The summing up: 总结:

def find_shortest_path(source, target)
  dijkstra(source)
  path = []
  u = target
  while u
    path.unshift(u)
    u = (@vertices[u].prev.value if @vertices[u].prev)
  end
  return path
end

Also the code above would fix the issue, the code is not ruby idiomatic; 此外,上面的代码将解决问题,代码不是ruby惯用; FWIW, I'd post the idiomatic version. FWIW,我发布了惯用版。

I understand, it's too much of uncommon patterns for you yet, but just in case you'd be interested to dig into: 我明白,这对你来说太多了不寻常的模式,但万一你有兴趣深入研究:

def find_shortest_path(source, target)
  dijkstra(source)

  loop.reduce([[], target]) do |(path, u), _|
    break path unless u
    [
      path << u,
      (@vertices[u].prev.value if @vertices[u].prev)
    ]
  end
end

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM