algorytm.org

Implementacja w Ruby



Baza Wiedzy
wersja offline serwisu przeznaczona na urządzenia z systemem Android
Darowizny
darowiznaWspomóż rozwój serwisu
Nagłówki RSS
Artykuły
Implementacje
Komentarze
Forum
Bookmarki






Sonda
Implementacji w jakim języku programowania poszukujesz?

Algorytm Kruskala - Implementacja w Ruby
Ocena użytkownikóww: *****  / 45
SłabyŚwietny
Nadesłany przez Dawid Fatyga, 01 marca 2011 00:46
Kod przedstawiony poniżej przedstawia główną część rozwiązania problemu.
Pobierz pełne rozwiązanie.

kruskal_1_rb.rb:
#!/usr/bin/env ruby

#
# Implementacja algorytmu Kruskala, z wykorzystaniem struktury zbiorow
# rozlacznych.
#
# www.algorytm.org
#
# Graf odczytywany na wejsciu powinien zawierac kilka linii, w kazdej linii
# powinien znajdowac sie opis jednej krawedzi. Opis krawedzi to trojka napisow
# oddzielonych bialym znakiem:
#  * pierwszy to liczba rzeczywista
#  * drugi to indeks pierwszego konca krawedzi
#  * trzeci to indeks drugiego konca krawedzi
# Koniec grafu oznaczony jest przez koniec pliku.
#
# Przykladowy graf (opisany w artykule) wyglada nastepujaco:
#
#  7 e f
#  2 a f
#  6 f d
#  1 a e
#  2 b c
#  4 a b
#  8 c d
#  2 b e
#  3 d e
#
# Wynik dzialania algorytmu mozna zwizualizowac za pomoca programu graphviz,
# uruchamiajac polecenie:
#
#   ruby kruskal.rb | dot -og.png -Tpng
#
# i wpisujac dane grafu lub polecenie:
#
#   ruby kruskal.rb < dane.txt | dot -og.png -Tpng
#
# Plik 'dane.txt' powinien zawierac graf wejsciowy. Wynikiem bedzie plik 'g.png'
# ze zwizualizowanym grafem wejsciowym i zaznaczonym na czerwono drzewem
# rozpinajacym.
#
# Autor: Dawid Fatyga
#

#
# Klasa Vertex reprezentuje wierzcholek grafu, jednoczesnie implementujac
# strukture zbiorow rozlacznych.
#
# http://en.wikipedia.org/wiki/Disjoint-set_data_structure
#
class Vertex
	attr_accessor :name, :parent, :rank
	
	def initialize(name)
		self.name = name
		self.rank = 0
	end
		
	# Odnajduje reprezentanta danego zbioru rozlacznego, stosujac kompresje
	# sciezki.
	def find
		if self.parent.nil?
			self
		else
			self.parent = parent.find
		end
	end
	
	# Scala dwa zbiory rozlaczne, dolaczajac mniejsze drzewo do wiekszego drzewa.
	def union(other)
		self_root = self.find
		other_root = other.find
		
		if self_root.rank > other_root.rank
			other_root.parent = self_root
		elsif self_root.rank < other_root.rank
			self_root.parent = other_root
		elsif self_root != other_root
			other_root.parent = self_root
			self_root.rank += 1
		end
	end
end

#
# Klasa Edge reprezentuje pojedyncza krawedz grafu:
#  :weight    waga krawedzi
#  :a         pierwszy koniec krawedzi (instancja klasy Vertex)
#  :b         drugi koniec krawedzi (instancja klasy Vertex)
#
class Edge < Struct.new(:weight, :a, :b)
	#
	# Sprawdza czy konce krawedzi nie naleza do tego samego zbioru, nastepnie
	# laczy zbiory skojarzone z wierzcholkami, a nastepnie zwraca wynik testu.
	#
	# Zbiory sa laczone zawsze, poniewaz wg. algorytmu jesli konce krawedzi
	# naleza do innych zbiorow, to nalezy je polaczyc; laczenie juz polaczonych
	# zbiorow nie wplywa na dzialanie algorytmu.
	# 
	def disjoint_ends?
		(a.find != b.find).tap { a.union(b) }
	end
end

class Graph	
	def initialize
		@edges = []
		@vertices = {}
	end
		
	#
	# Algorytm (http://pl.wikipedia.org/wiki/Algorytm_Kruskala):
	#
	#  1. Utworz las L z wierzcholkow oryginalnego grafu - kazdy wierzcholek jest na poczatku osobnym drzewem.
	#  2. Utworz zbior S zawierający wszystkie krawedzie oryginalnego grafu.
  #  3. Dopoki S nie jest pusty:
  #     4. Wybierz i usun z S krawedz o minimalnej wadze.
  #     5. Jesli krawedz ta laczyla dwa rozne drzewa, to dodaj ja do lasu L, tak aby polaczyla dwa odpowiadające drzewa w jedno.
  #     6. W przeciwnym wypadku odrzuc ja
  #
  #  Po zakonczeniu algorytmu L jest minimalnym drzewem rozpinającym.
  #
	def kruskal(input=$stdin)
	
		# W tym miejscu
		#  * @edges jest zainicjalizowane pusta lista []
		#  * @vertices jest zainicjalizowane pusta tablica haszujaca {}

		until (line = input.gets).nil?
			w, a, b = line.split(/\s+/)
	
			# Instancje klasy Vertex sa zapamietywane w tablicy haszujacej byly
			# inicjalizowane tylko jeden raz.
			@vertices[a] ||= Vertex.new(a)
			@vertices[b] ||= Vertex.new(b)
	
			@edges.push(Edge.new(Float(w), @vertices[a], @vertices[b]))
		end
		
		# W tym miejscu:
		#  * @edges to zbior S zawierajacy wszystkie krawedzie grafu
		#  * @vertices to las L z wierzcholkow grafu
		
		# Zbior krawedzi S jest sortowany wg. wag; metoda select iteruje
		# po wynikowej tablicy wybierajac krawedzie, dla ktorych metoda
		# :disjoint_ends? zwroci wartosc 'true' (ktora to jednoczesnie laczy
		# wszystkie napotkane zbiory rozlaczne).
		#
		# W Wyniku zwracana jest lista krawedzi nalezaca do drzewa rozpinajacego.
		@edges.sort_by(&:weight).select(&:disjoint_ends?)
	end
	
	# Zwraca reprezentacje grafu w postaci ciagu znakow, w jezyku dot; krawedzie
	# nalezace do drzewa rozpinajacego zostana pokolorowane na czerwono
	def to_s(spanning_tree = [])
		@edges.map do |edge| 
			out = "#{edge.a.name} -- #{edge.b.name}"
			out += " [color = red]" if spanning_tree.include?(edge)
			out
		end.join("\n")
	end
end

graph = Graph.new
spanning_tree = graph.kruskal
puts "graph {\n#{graph.to_s(spanning_tree)}\n}"
Dodaj komentarz