Project Euler 83
http://projecteuler.net/index.php?section=problems&id=83
ダイクストラ法というアルゴリズムがあるのを知ったので、その方法で解きました。
ただ、実装の具体例などは全く見ていないためかなり独自実装です。
適当に毎回全ノードを探索して、最短ルートを探すようにしたため、計算量は、約 80^4 / 2 = 20480000 くらいです。
その方法が間違えているのかどうか知りませんが、Ruby では結構時間がかかってしまいました。
72秒でした。
いかがなものかと思い、C++ でも実装してみたところ、5.08秒でした。
うかつにも C++ にときめきました。
Project Euler を始めて薄々感づいているのですが、Ruby って単純にループするだけでも結構コストがあるような気がしています。
前のエントリーでも計測しましたが、この前は each と inject だったので、今回は times を使って計測しなおしました。
irb> start = Time.now; 10000000.times{};puts Time.now - start 1.828148
うーん、やっぱりたった1000万のループで何もしないのに2秒近くかかるんですよね。
とはいっても Ruby の簡便さは大好きなので困らないかぎり Ruby で続けるつもりです。
以下、ちょー適当コードですが気が向いたので晒しておきます。
ちなみにC++も全くと言っていいほど同じ構造です。
def problem83 matrix_file = "problem82.txt" imax = 80 jmax = 80 #matrix_file = "problem82_test.txt" #imax = 5 #jmax = 5 matrix = [] File.foreach(matrix_file) do |line| matrix << line.chomp.split(",").map {|t| t.to_i} end tansaku = [] 0.step(imax-1) do |i| tansaku << [nil]*jmax end tansaku[0][0] = matrix[0][0] calccount = 0 loop do saitan = [] 0.step(imax-1) do |i| 0.step(jmax-1) do |j| if tansaku[i][j].nil? lst = [] if i-1 >= 0 && tansaku[i-1][j] != nil lst << tansaku[i-1][j] end if j-1 >= 0 && tansaku[i][j-1] != nil lst << tansaku[i][j-1] end if i+1 < imax && tansaku[i+1][j] != nil lst << tansaku[i+1][j] end if j+1 < jmax && tansaku[i][j+1] != nil lst << tansaku[i][j+1] end if lst.size > 0 saitan << [lst.min + matrix[i][j], i, j] end calccount += 1 end end end min = saitan.min_by {|t| t[0]} tansaku[min[1]][min[2]] = min[0] puts "#{calccount} #{min[0]}" if calccount % 1000 == 0 break unless tansaku[imax-1][jmax-1].nil? end tansaku[imax-1][jmax-1] end