[Ruby]ãã¤ã¯ã¹ãã© ã¢ã«ã´ãªãºã ã§ã°ã©ããã¼ã¿ä¸ã®2ç¹éã®æççµè·¯ãç®åºãã
ダイクストラ アルゴリズム ã§ãã°ã©ããã¼ã¿ä¸ã®2ç¹éã®æççµè·¯ãç®åºãã¦ã¿ã¾ãã
ã¾ãã¯ãã°ã©ãã®ãã¼ã¿æ§é ã使ãã¨ããããã°ã©ãã®æ§ç¯ã¨æ¢ç´¢ã«å¿ è¦ãªæä½éã®APIã®ã¿ç¨æãã¾ããã
- Graph
- ã°ã©ãã§ãã
- verticesãã£ã¼ã«ã ã§ã°ã©ãä¸ã®é ç¹ãé åã§æã¡ã¾ãã
- Graph#add_vertexã§é ç¹ã追å ã§ãã¾ãã
- åé ç¹ã«ã¯ãè¿½å æã«IDãå²ãæ¯ããã¾ãã
- Graph#connect,disconnectã§ã°ã©ãä¸ã®é ç¹åå£«ãæ¥ç¶ã§ãã¾ãã
- æ¥ç¶æã«ã¯ãéã¿ããæå®ã§ãããã軽ãçµè·¯ãæçã¨ããã¾ãã
- Vertex
- é ç¹ã§ããGraph#verticesã§ä¿æããããªãã¸ã§ã¯ãã§ãã
- edgesãã£ã¼ã«ãã§çµè·¯(æ¥ç¶ããã¦ããé ç¹ã¨çµè·¯ã®éã¿ã®ãã¢)ã®é åãæã¡ã¾ãã
# ã°ã©ã class Graph def initialize( directed=false ) @serial = 0 @directed = directed @vertices = [] end def connect( from, to, weight=1 ) assert_vertex_exist from assert_vertex_exist to @vertices[from].connect( to, weight ) @vertices[to].connect( from, weight ) unless @directed end def disconnect( from, to ) assert_vertex_exist from assert_vertex_exist to @vertices[from].disconnect( to ) @vertices[to].disconnect( from, weight ) unless @directed end def add_vertex( data ) id = @serial @serial += 1 @vertices[id] = Vertex.new( id, data ) return id end def inspect return "---\n" + vertices.collect {|v| v.inspect }.join("\n") end attr_reader :vertices private def assert_vertex_exist( id ) if ( id < 0 || id >= @vertices.length || !@vertices[id] ) raise "vertex not exist. id=#{id}" end end end # é ç¹ class Vertex def initialize( id, data ) @id = id @data = data @edges = [] end def connect( to, weight ) @edges.push({ :to=> to, :weight=> weight }) end def disconnect( to ) @edges.reject! {|e| e.to === to } end def inspect return "#{@id}:#{@data.inspect}\n" + @edges.collect{|e| " -> #{e[:to]} : #{e[:weight]}" }.join("\n") end attr_reader :id, :data, :edges end
ãµã³ãã«ã§ä½¿ç¨ããã°ã©ãã¯ä»¥ä¸ã
次ã®ã³ã¼ãã§çæã§ãã¾ãã
graph = Graph.new ids = [] ('a'..'r').each{|i| ids << graph.add_vertex( i ) } graph.connect(ids[0], ids[1], 1.0) graph.connect(ids[0], ids[6], 2.0) graph.connect(ids[0], ids[8], 2.0) graph.connect(ids[1], ids[2], 4.0) graph.connect(ids[1], ids[3], 6.0) graph.connect(ids[2], ids[10], 2.0) graph.connect(ids[2], ids[11], 3.0) graph.connect(ids[3], ids[4], 1.0) graph.connect(ids[3], ids[12], 1.0) graph.connect(ids[4], ids[5], 2.0) graph.connect(ids[4], ids[13], 3.0) graph.connect(ids[5], ids[6], 4.0) graph.connect(ids[5], ids[9], 4.0) graph.connect(ids[6], ids[7], 1.0) graph.connect(ids[7], ids[8], 2.0) graph.connect(ids[7], ids[9], 2.0) graph.connect(ids[8], ids[14], 1.0) graph.connect(ids[9], ids[15], 1.0) graph.connect(ids[16], ids[17], 1.0)
ãã¤ã¯ã¹ãã© ã¢ã«ã´ãªãºã ã«ããæççµè·¯æ¢ç´¢ã®æµãã¯ä»¥ä¸ã®ã¨ããã
- éå§ä½ç½®ãã飿¥ããé ç¹ããæã軽ãçµè·¯ãåªå
ãã¦é ã«æ¢ç´¢ãã¦ããã¾ãã
- ä¾é¡ã®å ´åã§ããã°ãéå§é ç¹ã§ãã a ã®é£æ¥é ç¹[b,g,i]ãã¾ãæ¢ç´¢ããaããããããã®é ç¹ã¾ã§ã®éã¿ãç®åºãã¾ãã
- ç®åºããéã¿ãããã³ãåã®é ç¹ã®IDã¯ãæççµè·¯ã®ç®åºæã«ä½¿ç¨ãããããé ç¹ãã¨ã«è¨æ¶ãã¦ããã¾ãã
- 次ã«ãâã§æ¢ç´¢ãããé ç¹ã®ãã¡ãæãçã=éã¿ã®åè¨å¤ãä½ãçµè·¯ã§ãããbãã®é£æ¥é ç¹[c,d]ãæ¢ç´¢ãã¾ãã
- æãçãçµè·¯ãé ã«å¦çãããããåªå 度ã¤ããã¥ã¼ã使ç¨ãã¾ãã
- bã®æ¢ç´¢ãå®äºããããæ¬¡ã«è»½ãçµè·¯ã§ãããg or iãã®é£æ¥é ç¹ãæ¢ç´¢ãã¾ãã
- 以ä¸ãé ã«ç¹°ãè¿ããå¸¸ã«æãçãçµè·¯ãåªå ãã¦ãã¹ã¦ã®çµè·¯ãæ¢ç´¢ãã¦ããã¾ãã
- ä¾é¡ã®å ´åã§ããã°ãéå§é ç¹ã§ãã a ã®é£æ¥é ç¹[b,g,i]ãã¾ãæ¢ç´¢ããaããããããã®é ç¹ã¾ã§ã®éã¿ãç®åºãã¾ãã
- ãã§ã«æ¢ç´¢æ¸ã¿ã®é ç¹ã«å¥ã®ã«ã¼ãã§å度å°éããå ´åãããçãæ¹ã®çµè·¯ãæ¡ç¨ããè¨æ¶ããã¦ããç®åºããéã¿ã¨åã®é ç¹ã®IDãæ´æ°ãã¾ãã
- éå§ä½ç½®ããå°éå¯è½ãªãã¹ã¦ã®é ç¹ãæ¢ç´¢ããã°ãå¦çã¯ããã§çµäºã
- ç®çã®é ç¹ã«å°éãã¦ããä»ã®çµè·¯ã®ã»ããçãå¯è½æ§ãããããããã¹ã¦ã®çµè·¯ã®æ¢ç´¢ãå®äºããã¾ã§å¦çã¯ç¶ç¶ããã¾ãã
å®è£ ã¯æ¬¡ã®ãããªæãã«ãªãã¾ããåªå 度ã¤ããã¥ã¼ã®å®è£ ã¯priority-queueã使ç¨ãã¾ããã
require 'graph' require 'priority_queue' # ãã¤ã¯ã¹ãã© ã¢ã«ã´ãªãºã ã§ãã¼ãéã®æççµè·¯ãç®åºãã class ShortestPathFinder def initialize( graph ) @graph = graph end # startãããã¹ã¦ã®ãã¼ãã¸ã®æççµè·¯ãæ¢ç´¢ãã def traverse( start ) initialize_state( start ) @queue.push( start, 0 ) while( vertex_id_and_distance = @queue.delete_min ) visit_vertex(vertex_id_and_distance[0], vertex_id_and_distance[1]) end return self end # æå®é ç¹ã¾ã§ã®æçãã¹ãæ§ç¯ããã def shortest_path_to( vertex_id ) raise "not traversed" unless @start path = [] while( @previous.include?( vertex_id ) && vertex_id != @start ) path.unshift(vertex_id) vertex_id = @previous[vertex_id] end return path end private # ç¶æ ãåæåãã def initialize_state(start) @queue = PriorityQueue.new @distances = {} @previous = {} @start = start end # é ç¹ã訪åãã def visit_vertex( vertex_id, distance ) @graph.vertices[vertex_id].edges.each {|e| to = e[:to] distance_of_next_node = distance + e[:weight] if ( not_visited_or_shorter_path?( to, distance_of_next_node ) ) @previous[to] = vertex_id @distances[to] = distance_of_next_node @queue[to] = distance_of_next_node end } end # è¨ªåæ¸ã¿ã§ãªããã¾ãã¯ããçããã¹ã§ãããè©ä¾¡ããã def not_visited_or_shorter_path?( vertex_id, distance ) return !@distances.include?(vertex_id) || @distances[vertex_id] > distance end end
å©ç¨ä¾ãgraph ã¯âã§ä½æãããã®ã§ãã
finder = ShortestPathFinder.new( graph ).traverse(ids[0]) graph.vertices.each {|v| path = finder.shortest_path_to(v.id).collect{|id| graph.vertices[id].data }.join(' -> ') puts "#{v.data} : #{ path }" }
å®è¡çµæã§ããå°éä¸è½ãªq,rã®ãã¹ã¯ç©ºé åã«ãªãã¾ãã
a : b : b c : b -> c d : b -> d e : g -> f -> e f : g -> f g : g h : g -> h i : i j : g -> h -> j k : b -> c -> k l : b -> c -> l m : b -> d -> m n : g -> f -> e -> n o : i -> o p : g -> h -> j -> p q : r :