Mae向きなブログ

Mae向きな日記のブログ版。ようやくこちらに移行してきました。

クラスカルのアルゴリズム

id:naoyaさんが,クラスカルアルゴリズムPythonで実装されています。

自分の理解を深めるために,Rubyで書いてみました。

mst_kluskal.rb

#!/usr/bin/env ruby

class DisjointSet
  attr_accessor :parent, :rank
  def initialize(size)
    @parent = Array.new(size, 0)
    @rank   = Array.new(size, 0)
    for i in 0...size
      @parent[i] = i
    end
  end

  def union(x, y)
    self.link(self.find_set(x), self.find_set(y))
  end

  def link(x, y)
    if self.rank[x] > self.rank[y]
      self.parent[y] = self.parent[x]
    else
      self.parent[x] = self.parent[y]
      if self.rank[x] == self.rank[y]
        self.rank[x] += self.rank[y] + 1
      end
    end
  end

  def find_set(x)
    if x != self.parent[x]
      self.parent[x] = self.find_set(self.parent[x])
    end
    return self.parent[x]
  end
end

class Edge
  attr_reader :src, :dst, :weight
  def initialize(src, dst, weight)
    @src = src
    @dst = dst
    @weight = weight
  end
end

edges = [
         Edge.new('a', 'b', 4),
         Edge.new('a', 'h', 8),
         Edge.new('b', 'c', 8),
         Edge.new('b', 'h', 11),
         Edge.new('c', 'd', 7),
         Edge.new('c', 'f', 4),
         Edge.new('c', 'i', 2),
         Edge.new('d', 'f', 14),
         Edge.new('d', 'e', 9),
         Edge.new('e', 'f', 10),
         Edge.new('f', 'g', 2),
         Edge.new('g', 'h', 1),
         Edge.new('g', 'i', 6),
         Edge.new('h', 'i', 7),
        ]

c2i = { 'a' => 0, 'b' => 1, 'c' => 2, 'd' => 3, 'e' => 4,
        'f' => 5, 'g' => 6, 'h' => 7, 'i' => 8}
set = DisjointSet.new(c2i.size)
mst = []

# edges.sort!{ |a, b| a.weight <=> b.weight }
edges = edges.sort_by{ |a| a.weight }
edges.each do |x|
  u = c2i[x.src]
  v = c2i[x.dst]
  if set.find_set(u) != set.find_set(v)
    mst << x
    set.union(u, v)
  end
end

puts mst.inject(0){ |result, item| result + item.weight }

実行結果

$ ./mst_kluskal.rb
37

ちょっとした疑問点(1)

Array#sort!メソッドは,破壊的にソートを行うことができますが,要素の比較回数が気になるので,sort_byメソッドを使った方が効率が良いと聞いたことがあります。上記でもsort_byメソッドを使ったのですが,これには,破壊的にレシーバを変更するsort_by!はないんですね?

ちょっとした疑問点(2)

id:naoya さんは,配列parentとrankの大きさをエッジの数で確保していますが,ノードの数だけでいいような気がするのですが…。

6/15追記

ノードの数でいいようです。id:naoya さんも修正されています。

6/20追記

id:tksmd さんがPythonでプリムのアルゴリズムを書かれています。参考にさせてもらって,いつかRubyで書いてみたいと思います。
http://d.hatena.ne.jp/tksmd/20090616#p1