わさっきhb

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

誕生日のパラドックス検算用のワンライナー

誕生日のパラドックス(たんじょうびのパラドックス)とは「何人集まればその中に同じ誕生日の人がいる確率が 50%を超えるか?」という問題から生じるパラドックスである。普通に考えれば365日の半分、だいたい180人前後と考えるが、答えは23人である。直感的な答えよりもずっと少なくはないだろうか。 誕生日のパラドックスは論理的な矛盾に基づいているという意味でのパラドックスではなく、結果が一般的な直感と反しているという意味でのパラドックスである。

誕生日のパラドックス - Wikipedia

何週か前の情報セキュリティの授業で,一方向ハッシュ関数を説明したときに,取り上げました.今見てみると,「2月29日は考えない」「誕生日は365日とも等確率」のほか,「双子は対象外」という仮定をおかないといけないのですね.
Rubyで計算を試みてみます.上記Wikipediaエントリの中で定義しているp_1(n)に対して,p_1(1)=1p_1(2)=\frac{364}{365},…を順に計算し*1,これが最初に1/2を下回ったところで,計算を打ち切ります.

$ ruby -e 'p=1.0;m=365;1.upto(m){|i|p=p*(m-i+1)/m;puts "#{i}:#{p}";if p<0.5;break end}'

1:1.0
2:0.9972602739726028
3:0.9917958341152185
4:0.9836440875334497
5:0.9728644263002063
6:0.9595375163508884
7:0.9437642969040244
8:0.9256647076483308
9:0.905376166110833
10:0.8830518222889221
11:0.8588586216782667
12:0.8329752111619353
13:0.8055897247675702
14:0.7768974879950266
15:0.7470986802363133
16:0.7163959947471498
17:0.6849923347034391
18:0.6530885821282105
19:0.6208814739684632
20:0.5885616164194198
21:0.5563116648347941
22:0.5243046923374498
23:0.49270276567601445

たしかに23人と出ました.
ちょっとワンライナーに手を加えてみます.というのも,pはFloatで,「p=p*(m-i+1)/m」の乗除算を繰り返していくうちに,誤差が蓄積する可能性があります.計算式は分数ですから,Rationalを使い,出力時にはto_fを差し込むことにします.

$ ruby -e 'p=Rational(1,1);m=365;1.upto(m){|i|p=p*(m-i+1)/m;puts "#{i}:#{p.to_f}";if p<0.5;break end}'

1:1.0
2:0.9972602739726028
3:0.9917958341152187
4:0.9836440875334497
5:0.9728644263002064
6:0.9595375163508885
7:0.9437642969040246
8:0.925664707648331
9:0.9053761661108333
10:0.8830518222889223
11:0.858858621678267
12:0.8329752111619356
13:0.8055897247675706
14:0.776897487995027
15:0.7470986802363135
16:0.7163959947471501
17:0.6849923347034393
18:0.6530885821282107
19:0.6208814739684633
20:0.5885616164194201
21:0.5563116648347942
22:0.5243046923374499
23:0.49270276567601456

23で計算を終えるのは変わりませんが,末尾の桁が先ほどのと少々,違っていました.
ところでRationalって組み込みクラスでしたっけ? リファレンスを引く前に,Ruby 1.8と1.9でワンライナーを動かしてみますか.上の2つのコマンドは,Ruby 1.9で実行しました.Ruby 1.8で動かすと

-e:1: undefined method `Rational' for main:Object (NoMethodError)

と出ました.じゃあ修正.

$ ruby -rrational -e 'p=Rational(1,1);m=365;1.upto(m){|i|p=p*(m-i+1)/m;puts "#{i}:#{p.to_f}";if p<0.5;break end}'

Ruby 1.8での結果は以下のとおり.

1:1.0
2:0.997260273972603
3:0.991795834115219
4:0.98364408753345
5:0.972864426300206
6:0.959537516350888
7:0.943764296904025
8:0.925664707648331
9:0.905376166110833
10:0.883051822288922
11:0.858858621678267
12:0.832975211161936
13:0.805589724767571
14:0.776897487995027
15:0.747098680236314
16:0.71639599474715
17:0.684992334703439
18:0.653088582128211
19:0.620881473968463
20:0.58856161641942
21:0.556311664834794
22:0.52430469233745
23:0.492702765676015

「-rrational」つきのワンライナーは,Ruby 1.9でもエラー・警告なく実行できて,出力は,なしのと同じでした.
といったところで,リファレンスも見ておきましょう.違っていましたね.

rational 有理数を扱うためのライブラリです。
Ruby 1.8.7 リファレンスマニュアル>ライブラリ一覧
class Rational 有理数を扱うクラスです。
Ruby 1.9.1 リファレンスマニュアル>組み込みライブラリ

*1:“できる”プログラマになりたい学部学生へ:紙と鉛筆で,分数式として値がどうなるかを書いてみて,そこから漸化式を見つけ,1個の変数に割り当てるという練習をすれば,同種の一見難しそうな問題にも役立ちます.p_1(n)<1/2を代数的に解こうとするのは,無謀というものです.