Matrix Rotation (CODEEVAL)

行列回転の問題。今回は面白かったです。

CHALLENGE DESCRIPTION:

You are given a 2D N×N matrix. Each element of the matrix is a letter: from ‘a’ to ‘z’. Your task is to rotate the matrix 90° clockwise:

a b c        g d a
d e f  =>    h e b
g h i        i f c

INPUT SAMPLE:

a b c d
a b c d e f g h i j k l m n o p
a b c d e f g h i

OUTPUT SAMPLE:

c a d b
m i e a n j f b o k g c p l h d
g d a h e b i f c

CONSTRAINTS:

  1. The N size of the matrix can be from 1 to 10
  2. The number of test cases is 100

My Code

とりあえず書いて見たコード。

#!/usr/bin/env ruby -w
def generate_matrix(numbers)
  n = Math.sqrt(numbers.size).to_i
  numbers.each_slice(n).to_a
end

def rotate(matrix)
  matrix_size = matrix.size
  result = Array.new(matrix_size) { [] }
  matrix.each_with_index do |line, i|
    line.each_with_index do |elm, j|
      result[j][matrix_size - i - 1] = elm
    end
  end
  result
end

ARGF.each_line do |line|
  matrix = generate_matrix(line.chomp.split)
  puts rotate(matrix).join(" ")
end

他のやり方

rotate メソッドがダサいので他の方法を調べてみました。
 
すると、Ruby Quiz というサイトで同様の問題が出題されていたようで Ruby-Forum でエレガントな解き方が紹介されていました。
Matrix Rotator (#209) - Ruby Forum
 
それがこれ。

    def rotate(n=1)
      (n%4).times {self.replace(self.reverse.transpose)}
    end

self.reverse.transpose 。これすごい。これは思いつかない。

>>  [["a", "b"], ["c", "d"]].reverse
=> [["c", "d"], ["a", "b"]]

>>  [["a", "b"], ["c", "d"]].reverse.transpose
=> [["c", "a"], ["d", "b"]]

別解

def generate_matrix(numbers)
  n = Math.sqrt(numbers.size).to_i
  numbers.each_slice(n).to_a
end

class Array
  def rotate(n = 1)
    (n % 4).times { self.replace(self.reverse.transpose) }
    self
  end
end

ARGF.each_line do |line|
  matrix = generate_matrix(line.chomp.split)
  puts matrix.rotate.join(" ")
end