ダイクストラ法とは、グラフ上の最短経路問題をとくアルゴリズム。↓このページに詳しいアルゴリズムの説明がある。
cf. ダイクストラ法(最短経路問題) – deq notes
Ruby でやってみた。
例題は、上のページにもあるこのグラフ(ただしノードにつけた番号のせいか上下が逆になっている)。
円(ノード)に番号がふられ、ノードをつなぐ辺(エッジ)にはそこを通るときのコスト(距離とも時間とも解釈できる)が付されている。この中の任意の 2 つのノードをつなぐ最短経路とコストを求めるのが最短経路問題だ。
今回は 0 番のノードをスタートし 5 番のノードをゴールとする最短経路とそのコストを求めてみる。
#!/usr/bin/env ruby # encoding: utf-8 class Node attr_reader :name attr_accessor :done, :cost, :from def initialize(name) @name = name @edges = [] @done = false @cost = nil @from = nil end def add_edge(edge) @edges << edge end def each_edge @edges.each{|e| yield(e) } end end Edge = Struct.new(:dest, :cost) def make_edge(nodes, a, b, cost) nodes[a].add_edge(Edge.new(b, cost)) nodes[b].add_edge(Edge.new(a, cost)) end nodes = [] 0.upto(5) do |i| nodes << Node.new(i) end edges = [ [0, 1, 2], # [node_a, node_b, cost] [0, 2, 4], [0, 3, 5], [1, 2, 3], [2, 3, 2], [1, 4, 6], [2, 4, 2], [4, 5, 4], [3, 5, 6] ] edges.each do |a, b, cost| make_edge(nodes, a, b, cost) end start = 0 goal = 5 start_node = nodes[start] start_node.cost = 0 start_node.done = true start_node.each_edge do |edge| n = nodes[edge.dest] n.cost = edge.cost n.from = start_node.name end while true do done_node = nil nodes.each do |node| if node.done || node.cost.nil? next else node.each_edge do |e| n = nodes[e.dest] if n.cost.nil? n.cost = node.cost + e.cost n.from = node.name else if node.cost + e.cost < n.cost n.cost = node.cost + e.cost n.from = node.name end end end if done_node.nil? || node.cost < done_node.cost done_node = node end end end done_node.done = true break if nodes.all?{|n| n.done } end puts nodes[goal].cost route = [goal] begin node = nodes[route.first] route.unshift(node.from) end until route.first == start puts route.map(&:to_s).join(" -> ")
実行結果:
takatoh@nightschool $ ruby dijkstra.rb 10 0 -> 2 -> 4 -> 5
というわけで、最短経路は 0 -> 2 -> 4 -> 5、コストは 10 という答えが得られた。これは上のリンク先の答えと同じなので、あっていると思っていいだろう。