ぽこひでブログ

ほのぼの大学4年をやってる [ぽこひで] の学生エンジニアブログ?

Rubyでm * n行列をかっこよく作って足してみた

こんばんは。
火曜日は1,2限に「パターン認識学」の講義があって、今日は2とか9とかのこんな感じのパターンを学習させて未知のパターンの数字を認識させる、みたいな講義でした。
f:id:hdye82647:20150428231902g:plain
やることは簡単で、まずこの5つの9のデータ白が0、黒が1だとしてそれを5*5行列として読み込んでそいつのセルごとの総和をプロトタイプ(5*5)として、未知の数値を判断してみよーって感じでした。

このデータの表現方法は自由だったので、僕は左上の9を
01110 01001 01111 00001 00011 (実際にスペースはないけど見やすそうなので)
といった感じに左上から右下に順に走査していく感じで表現して、この文字列をプログラム内で行列化してやろー的な目論見でこれが最初のコード。

def matrix(str)
  mat = Array.new(5,0).map{Array.new(5,0)}
  i = 0
  while str[i]
  for j in 0...5
    for k in 0...5
      mat[j][k] = str[i]
    end
  end
  i+=1
  end
end

みたいな感じで冗長すぎてruby使う意味が一切感じられないクソみたいなコードでした。。これはあまりにもかっこ悪いってことでStringクラスとかもう一回復習してみて、かなり改善したのがこれ。

def matrix(str,m,n)
  exit unless str.size == m*n
  return str.split("").map(&:to_i).each_slice(m).to_a
end

しかもm*n行列に対応できました。ただ、サイズがm*nじゃなかったら帰れ!ってことにして勝手に入力が整数文字列に限定していますが、整数文字列を1文字ずつsplit("")で分解して、それぞれをto_iで整数にします。この時点では
[1,2,3,4,5,6,7,8,9]
こんな感じのただの整数配列。こいつをm文字でスライスして配列に変換してやると1行でいい感じになりました!
試しに最初の9のデータを入れてやると

str = "0111001001011110000100011"
p str.split("").map(&:to_i).each_slice(5).to_a  
    #=>[[0, 1, 1, 1, 0], [0, 1, 0, 0, 1], [0, 1, 1, 1, 1], [0, 0, 0, 0, 1], [0, 0, 0, 1, 1]]

と一発で5*5行列に変換してくれました。
あとそのm*n行列同士の和を計算したかったので、これはどっかのサイトさんのコメントを参考(ほぼパクリ?)にして

  def sum(mat1,mat2)
    return [mat1.flatten,mat2.flatten].transpose.map{|a| a.inject(:+)}.each_slice(m).to_a
  end

この1行で行列の加算が完了しました。

x = [[0, 0, 1, 1, 1], [0, 1, 0, 0, 1], [1, 1, 1, 1, 1], [0, 0, 0, 1, 1], [0, 0, 0, 1, 0]]
y = [[0, 1, 1, 1, 0], [0, 1, 0, 0, 1], [0, 1, 1, 1, 1], [0, 0, 0, 0, 1], [0, 0, 0, 1, 1]]
sum(x,y)
#=>[[0, 1, 2, 2, 1], [0, 2, 0, 0, 2], [1, 2, 2, 2, 2], [0, 0, 0, 1, 2], [0, 0, 0, 2, 1]]

とうまくいきました。どちらも1行で綺麗にかけたので満足でした。講義のレポートはまだ終わってないんですが。。。こうやって綺麗にかけるとなんかやっぱruby楽しい!ってなりますね。行列計算とか綺麗に書きたい方の参考になったらなと思います。