わさっきhb

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

連続量を等分する

いきなりですが問題です.

1リットルの液体があります.
3つの容器に,同じ量になるよう分けます.
どうやって分けますか.

3つの容器は同一で,見比べることによって,どれが多いか少ないか,また同じ量なのかが分かるものとします.
そうすると,同じ量になるまで,多い容器から少ない容器へ注ぎ,目で見て,同じ量になるまで操作するという,何とも直感的なやり方が,答えになります.
そこで,「注ぐ操作」を限定します.こういう問題です.

3つの容器があります.1つの容器に,1リットルの液体があります.残り2つの容器は,空っぽです.
それら3つの容器に,液体が同じ量になるよう分けます.
次の操作が行えるものとします.

  • 3つの容器を見比べて,どれが多いか少ないか,また同じ量なのかを知ること.
  • 2つの容器について,液体の量を均す(一つがaリットル,もう一つがbリットルのとき,両方とも(a+b)/2リットルにする)こと.

どうやって分けますか.

これだと,「3つが同じ量にならないのでは」と思いたくなります.数学的にも,「有限回の操作では,どの容器も,液体の量を3分の1リットルにすることができない」ことが示せます.均す操作の繰り返しでは,どの容器に入っている量も,ゼロか,「2のべき乗分の整数」リットル(1リットルは1分の1と見なします)であり,分子の整数をいくつにしても,誤差なく「3分の1」リットルにならないからです.
もちろん,均す操作を繰り返せば,3分の1リットルとの誤差はどんどん,小さくなっていくはずです.「2のべき乗分の整数」リットルとなることと合わせて,検証用のRubyスクリプト(balancing.rb)を作成しました.コードはGistより参照できます.
引数なしで実行すると,3つの容器に対し,5回の均す操作で,容量の変化を見ることができます.

$ ruby balancing.rb
==== Step 0 ====
*[1] 1/1 = 1.0
 [2] 0/1 = 0.0
 [3] 0/1 = 0.0
error: 2/3 = 0.6666666666666666
==== Step 1 ====
 [1] 1/2 = 0.5
 [2] 1/2 = 0.5
*[3] 0/1 = 0.0
error: 1/3 = 0.3333333333333333
==== Step 2 ====
 [1] 1/4 = 0.25
*[2] 1/2 = 0.5
 [3] 1/4 = 0.25
error: 1/6 = 0.16666666666666666
==== Step 3 ====
 [1] 3/8 = 0.375
 [2] 3/8 = 0.375
*[3] 1/4 = 0.25
error: 1/12 = 0.08333333333333333
==== Step 4 ====
 [1] 5/16 = 0.3125
*[2] 3/8 = 0.375
 [3] 5/16 = 0.3125
error: 1/24 = 0.041666666666666664
==== Step 5 ====
 [1] 11/32 = 0.34375
 [2] 11/32 = 0.34375
*[3] 5/16 = 0.3125
error: 1/48 = 0.020833333333333332

「error」の行は最大誤差で,さかのぼって行頭の「*」のあるところが,最大誤差となっている容器(複数の場合もあります)です.Step 5の「[3] 5/16 = 0.3125」は,「3番目の容器は,16分の5リットル(0.3125リットル)」と読むことができます.
balancing.rbは引数をとることができ,第1引数は容器の数,第2引数は均す操作の回数です.3つの容器で50回だと,最後は次のようになります.

ruby balancing.rb 3 50 | tail -n 5
==== Step 50 ====
 [1] 375299968947541/1125899906842624 = 0.33333333333333304
*[2] 187649984473771/562949953421312 = 0.3333333333333339
 [3] 375299968947541/1125899906842624 = 0.33333333333333304
error: 1/1688849860263936 = 5.921189464667501e-16

ここで,[1]や[3]の容器の分母「1125899906842624」は,2の50乗のことです.
なお,ruby balancing 64 63のように,第1引数に2の整数乗,第2引数にそれより1少ない数を与えて実行すると,誤差なく等分でき,「==== End of operation ====」で終了します.
コーディングに当たっては,RubyのRationalクラスを活用しました.均す際には,最も量の多い容器と,最も量の少ない容器とを組み合わせています.