minofoto and miscellaneous notes

ごく気まぐれに,書きたいことを適当に書いています。本当の話かもしれませんし,フィクションかもしれません。

パンチカードを並べ替える

Ruby で遊ぶネタが他にないかな、と思っていたら、子供の頃パンチカードを作って遊んだのを思い出しました。
数字を書いたカードの上部に穴を開け、2進数でその数字を示せるように、パンチカードに切り欠きを入れます。
たとえば2のカードは、右から2番目の穴の上を切って溝に、3のカードは1番右と2番目の穴を切ります。
そうしておくと、竹串を順番に穴を通して引っかかったカードだけを並べ替えていくと、カードを順番に並べることができるというもの。

これを書いてみました。
まずカードを前後に動かす操作。b が穴の場所を示す2進数です。

def put_forward(array, b)
  int_array_1 = []
  int_array_2 = []
  for i in array
    if i & b == b
      int_array_1.push(i)
    else
      int_array_2.push(i)
    end
  end
  int_array_1 += int_array_2
end

def put_backward(array, b)
  int_array_1 = []
  int_array_2 = []
  for i in array
    if i & b == b
      int_array_2.push(i)
    else
      int_array_1.push(i)
    end
  end
  int_array_1 += int_array_2
end

こうしておいて、

int_array_ordered = 1..31
int_array_rand = int_array_ordered.sort_by{rand}
int_array_new = int_array_rand.dup

for i in [0b00001, 0b00010, 0b00100, 0b01000, 0b10000]
    int_array_new = put_backward(int_array_new, i)
end

print int_array_ordered, "\n"
print int_array_rand, "\n"
print int_array_new, "\n"

とするとちゃんとソートされていることがわかります。
調べると、基数ソートという名前がついているようですね。

一応こんな関数を書くとソートできます。

def punchcardsort(array)
  len = (array.max).to_s(2).length
  sort_key = []
  for i in 1..len
    sort_key.push(2**i)
  end
  for i in sort_key
    array = put_backward(array, i)
  end
  return array
end

しかしもちろん、ruby に用意されている sort を使った方がずっと速いようです。