算数にチャレンジしてみた

算数にチャレンジ! 第624回問題(11月20日〜 11月26日)


 1〜512の数の書かれたカードが1枚ずつ、左から小さい順に、「1,2,3,4,・・・,511,512」と並んでいます。いま、次のような作業を行うことにします。

ア. 前から奇数番目のカードをすべて取り除く
イ. 前から偶数番目のカードをすべて取り除く

 まず、この作業を、ア→イ→ア→イ→・・・の順に、カードが残り1枚になるまで繰り返して行うことにします。

 このとき、最後に残るカードに書かれた数字を答えてください。

http://www.sansu.org/

やってみた。*1

>ruby -v
ruby 1.8.7 (2008-08-11 patchlevel 72) [i386-mswin32]
module Enumerable
  def delete_odd
    delete_modulo2 0
  end
  def delete_even
    delete_modulo2 1
  end
  
  def delete_modulo2 modulo
    [].tap do |result|
      each.with_index do |value, index|
        result << value unless index%2 == modulo
      end
    end
  end
end

tapかわいいよtap。ということで、Enumerableモジュールにこんな感じでメソッドを追加して、
※remove_oddとremove_evenは今回使いませんが。

values = (1..512)
[0, 1].cycle do |modulo|
  values = values.delete_modulo2 modulo
  break if values.size == 1
end
p values.first #=> 342

こんな感じでサックリと……ってなんかキモいな。
「偶数番目を削除」メソッドの実装が「奇数番目を抽出」になってるからか……何か気になるけどスルーで。

本来ならばwith_indexなdelete_ifを使いたいんだけど、with_indexすると結果が値とindexの配列の配列になっちゃうから自作するはめに。HashのEnumerable系メソッドもそうだけど、結果は同じ形式にして欲しいところ。


typoを修正。

追記 Enumerable#group_byを使った方法

ruby-listを見ていたらEnumerable#group_byなんて面白げなメソッドを発見したので修正してみた。

module Enumerable
  def delete_modulo2 modulo
    group_by.with_index{ |value, index| index%2 }[1-modulo]
  end
end

実行速度のことはよくわかんないけど、とにかくコードがシンプルになった。これはうれしい。

さらに追記 Enumerable#selectを使った方法

よく考えると、delete_ifってArrayのメソッドだからwith_indexしたら配列になっちゃってたのか。
単純にselectにするのが一番自然だった。

module Enumerable
  def delete_modulo2 modulo
    select.with_index{ |value, index| index%2 == 1-modulo }
  end
end

*1:n番目の問題ってリソースが決まってるんだからパーマリンクが欲しいなぁ。たぶんhttp://www.sansu.org/used-html/index624.htmlだけど今のところ空ページだわ。