わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

「まま子立て」をワンライナーで

授業事例の中心にあるのは,「まま子立て」です.Webを調べると,いろいろ出てきました.

「ヨセフスの問題」を読んで,雑記の中を検索…ありました.最後の一人クイズです.
冒頭の論文で,授業で提示した課題はというと:

問題 円形に並び、2人抜きで1、2・・と数え、2と唱えた人を抜かします。最後に残る人が勝ちです。何番が勝ちでしょう。ルール (1)自分は1にいます。自分から数えます。(2)1、2、1、2・・・で2と唱えた位置の人は抜けます。(3)最後まで残った人が勝ちです。
(p.17)

問題 自分が1の位置にいて、最後まで残るためには、回りの人数を何人にすればいいでしょうか。
(同)

まず過去のスクリプトで,数値だけ変えてみます。

$ ruby -e 'def f(num,step); a=(1..num).to_a; pos=0; z=; until a.empty?; pos=(pos-1+step)%a.size; z<; until a.empty?; pos=(pos-1+step)%a.size; z<

3人の場合は,1の位置の人が「1」と言い,2の人が「2」と言うので抜けて,3の人が「1」と言い,1の人が「2」と言うので抜ける.なので最後まで残ったのは3の人,という次第です.
n人を縦に並べて出力していると,行数が多くなって,バックスクロールするのも手間です.これについては,ワンライナーの最後,「puts」を「p」に変えれば,すっきりします.

$ ruby -e 'def f(num,step); a=(1..num).to_a; pos=0; z=; until a.empty?; pos=(pos-1+step)%a.size; z<; until a.empty?; pos=(pos-1+step)%a.size; z<

それと,「最後の一人だけ」を出力したければ,「puts f(なになに)」の直後に「.last」をつければ解決します.初期状態の人数を,1人から17人として,求めてみます.

$ ruby -e 'def f(num,step); a=(1..num).to_a; pos=0; z=[]; until a.empty?; pos=(pos-1+step)%a.size; z<

「最後の一人」に規則性があること,初期人数がn=2^k-1人のとき,最後に残るのはn番目すなわち円環の中で最後に数を言う人であること,そして,初期人数がn=2^k人のとき,最後に残るのが1番目すなわち最初の最初に数を言う人であることが,分かります.
論文を離れ,「まま子立て」を確かめるワンライナーを作ることにします.設定は以下のとおり.

 ある家に子どもが30人いて、15人は先妻の子(これを継子という)、もう15人は後妻の子です。この30人を輪になるように並べ、ある1人の子どもから時計回りに数え、10番目の子を除きます。続いて、その次の子どもからまた数えなおして10番目の子を除きます。このように10番目、10番目・・・と繰り返し除いていったときに最後に残る1人に家を継がせることにしたそうです。
 後妻は、次の図のように子どもを並べました。
(図省略)
丸1の子どもから数え始めて、繰り返し10番目ごとに子ども除いていくと、15人いた継子(先妻の子)のうち14人までもが連続して除かれてしまいます。そこで1人だけ残った継子(丸14)は、後妻に訴えます。
継子14「これでは、あまりにも一方的過ぎるから、今度は自分(丸)から数え始めてください。」
後妻「自分の子はまだ15人も残っているし、じゃあ、あなた(継子14)から数え始めましょうか。」
 結果は、どうなったと思いますか?
 1人だけ残った継子(丸14)から数え始めると、今度はどんどん後妻の子どもたちが除かれていってしまい、ついには15人いた後妻の子どもたちが全員除かれてしまうのです。結局、1人だけ残った継子が後継ぎになってしまいました。

http://www.edita.jp/masanori432/one/masanori432318.html

継子14を「1の位置の人」とし,後妻サイドの残り15人を並べた16人でスタートするなら,抜ける人の順番は,次のようになります.

$ ruby -e 'def f(num,step); a=(1..num).to_a; pos=0; z=[]; until a.empty?; pos=(pos-1+step)%a.size; z<

最初の30人の配置も,取り入れることにします.

$ ruby -e 'num=30; step=10; a=%w(X1 X2 O3 X4 X5 X6 O7 O8 O9 O10 O11 X12 X13 O14 O15 X16 X17 X18 X19 O20 X21 O22 O23 O24 X25 O26 O27 X28 X29 O30); pos=0; z=[]; until a.empty?; pos=(pos-1+step)%a.size; z<

ポイントは,初期状態の配列を変更したのと,残り16人になったときに"O14"が先頭になるよう,ローテーション*1する処理を入れた点です.
出力は,こうなります.

["O14", "X16", "X17", "X18", "X19", "X21", "X25", "X28", "X29", "X1", "X2", "X4", "X5", "X6", "X12", "X13"]
["O10", "O20", "O30", "O11", "O22", "O3", "O15", "O27", "O9", "O24", "O7", "O23", "O8", "O26", "change", "X1", "X18", "X12", "X2", "X25", "X19", "X17", "X21", "X29", "X6", "X28", "X16", "X5", "X13", "X4", "O14"]

最初のリストは,継子1人と後妻の子15人の配置です.2番目のリストは,最初の30人から,O14が最後の1人になるまでの,取り除かれていく人の順番です.
配置の変化を知ることができるよう,もう少しだけ,スクリプトを変えてみましょう.出力は膨大になるので省略します.

$ ruby -e 'num=30; step=10; a=%w(X1 X2 O3 X4 X5 X6 O7 O8 O9 O10 O11 X12 X13 O14 O15 X16 X17 X18 X19 O20 X21 O22 O23 O24 X25 O26 O27 X28 X29 O30); pos=0; z=[]; until a.empty?; pos=(pos-1+step)%a.size; z<


冒頭の論文,「まま子立て」を教材化する前の「類型」もまた興味深いので,打ち出しておきます.授業のサンプルプログラムづくりや,当雑記でRubyを中心として何か書くときにも,たしかにそういう考え方ができるなあと思ったのでした.

教材化の類型は、数値の変更、文脈・場面の変更の2つの観点から、次の4つのタイプに分けられる。
タイプI型a 原題を生かし、数値もそのまま提示する【例】ねずみ算,入り子算
タイプI型b a型で行った後、原題の図形を変えたりして発展させる。【例】薬師算、俵杉算
タイプII型 数値はそのまま、文脈を現代風に変える【例】絹盗人算
タイプIII型a 原題を生かすが、きまりを見つけやすいように特殊の場合の数値に置き換える。【例】百五減算
タイプIIIb タイプI型bと似ているが数値をいろいろ変えて、発展させる【例】油分け算、薬師算
タイプIV型 原題の構造はそのまま生かし、数値を易しくし、場面を現代風に変える。授業後、原題に立ち戻って、紹介する。【例】まま子立て
(p.16)


(最終更新日時:Thu Jan 26 05:41:09 2012ごろ)

*1:Ruby 1.9ならArray#rotateが使えるのですが,1.8にはないメソッドのため,採用しませんでした.