2015年09月08日

デスマコロシアムが最終回ということで参加中です。
最終回だからという理由ではなくちょうどCodeIQとコードゴルフを始めたのでちょうどよかっただけですが。

現在開催中のデスマコロシアムはもうこれ(Ruby(45))以上短くできそうにないので過去問を解いてみました。
今回解く問題はデスマコロシアムの第9回です。

問題は

AbcdefghIjklmnopQrstuvwxYzabcdefGhijklmnOpqrst...

という文字を出力(a..zを8回繰り返し、8個ごとに大文字に変換)するという問題です。

Rubyだと簡単に

208.times{|i|putc (i%8<1?65:97)+i%26}

(37byte)
こういう解答ができますね。意外と簡単に思いつきますがなかなか縮められそうに無いですね。
Rubyの最短解もこれとほぼ同じでした。
なんとか削れそうなのはカッコと三項演算子でしょうか。
三項演算子を使う限りは短くできそうに無いのでビット演算でなんとか削る方法を考えます。

幸いなことに(?)大文字と小文字の整数コードの差は32で、a..zの間に6ビット目は変化しないのでここのビットをオン・オフすることでなんとかする方針を考えます。

i%8が0の時とそれ以外の時で6ビット目を切り替えられるようにします。
どこか一つのビットだけを見て0か1~7を判断する方法はありませんが一つ方法があります。
0と1~7の判断は難しいですが0と-1~-7は比較的簡単にできます。
-1~-7は4ビット目以上は1が並びます。当然6ビット目も1です。
というわけで
(-(i%8)&32)==(i%8>0?32:0)
はtrueになります
-i%8の結果は0~7になってしまうので-(i%8)とします。
というわけで-(i%8)&32でiが8の倍数の時は0、そうでなければ32になる計算式が出来ました。
これを使うと

208.times{|i|putc -(i%8)&32^65+i%26}

(36byte)
というわけで1文字縮まりましたね。大勝利ですがここで終わりではありません。
実はもう少し削れます。-i%8とカッコを消すと違う結果になってしまって使い物になりませんがi%-8とすることで(計算結果の値は違いますが8の倍数かそれ以外かを判断する上では)同じ結果を得られます。ということで。

208.times{|i|putc i%-8&32^65+i%26}

(34byte)
こうなりました。というわけでここで終了です。3文字縮まりましたね。i%-8&32と65+i%26はそのまま足し算しても大丈夫なのですが+を使うと演算の優先度の関係で違う結果になってしまう(もしくはカッコが必要)ので排他的論理和の^を使ってます。^じゃなくても|でも同じ結果になりますね。

(21:30)

トラックバックURL

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔