Line 1: Error: Invalid Blog('by Esehara' )

または私は如何にして心配するのを止めてバグを愛するようになったか

>> Zanmemo

あと何かあれは 「esehara あっと じーめーる」 か @esehara まで

何人集めたら、誕生日が同じペアが生まれる確率が高いのか確かめる

今日の風景

f:id:nisemono_san:20160803153457j:plain

なぜ人は半額のシールを見ると買ってしまうのか。

お話

あるところに、パーティ好きの二人組がいた。この二人のやるパーティは多種多様だったので、同じ客層はほぼ無かった。ところで、この二人組の最近の悩みは、パーティを主催するのにも飽きて来たことである。そこで、次のパーティを開くにあたって、このような提案をした。

「パーティを開いたときに、客が会場に入ってくるだろ? そこで、会場に入った人数で区切りをつけで、その中に同じ誕生日のペアがいるかどうかを賭けてみるというのはどうだろう?」

当然ながら、同じ誕生日のペアがいる確率は365人になればなるほど近づくだろうし、同じ誕生日でなければ、2人ならば、ほぼ同じ誕生日にならないだろう。それだと、この賭けは面白くない。そこで、できるだけお互いの確率を一緒にしたい。さて、この区切りを何人にすれば、お互いに平等であると言えそうだろうか。

議論

有名な話であり、Wikipediaにも「誕生日のパラドックス」として詳細にまとめられているけれども、解説する。

まず単純な話から始めよう。街中で、二人に声をかけ、その二人の誕生日が違う確率について考えてみよう。二人目が違う誕生日であるということは、改めて確認することなく、一人目の誕生日と一緒ではない、ということだ。

誕生日は365通りあり、そのうちの一つが選ばれる。その一つ以外を選べば違うということになるわけだから、残り364通りとなる。もちろん、誕生日自体は365通りあるわけだから、確率的には364/365 = 約99.72%となる。

さて、今度は3人に声をかけて、誕生日が違う確率について考えてみよう。まず2人が違う確率は364/365だった。そして、3人目は、その中の363通りなので、363/365となる。

ここで、単純に363/365と考えてはだめで、364/365 * 363/365と考えなくてはいけない。なぜなら、363/365だと、365通りのうち、2つの誕生日が含まれている確率だからだ。

しかし、考慮しなければならないのは、この場合、お互いの誕生日ということだ。まず、一人目と二人目を照しあわせ、その次に、一人目二人目と、三人目を照らしあわせないといけない。従って、364/365 * 363/365となる。

先のWikipediaの記事によれば、22人がだいたい五割に近いということになっている。計算の仕方は、1/2 * 2/3が2/6 = 1/3になるように、分母を乗算したものに対して、分子を364 * 363 * 362 ...といったようにしていけばよい。Haskellで書くなら次のようになるだろう:

import Text.Printf

birth_probability :: Integer -> IO ()
birth_probability 0 = return ()
birth_probability n = do
  putStrLn $ printf "Member :: %i" $ n + 1
  putStrLn $ show $ fromIntegral (product [(365 - n)..364]) / (365 ^ n)
  birth_probability $ n - 1

main :: IO ()
main = birth_probability 50

ここで顕著な例として、10人、20人、22人、23人、30人、40人の場合を取りだしてみると、次のような確率となる。

Member :: 10
0.8830518222889224
Member :: 20
0.58856161641942
Member :: 22
0.5243046923374499
Member :: 23
0.49270276567601456
Member :: 30
0.2936837572807314
Member :: 40
0.10876819018205101

この計算を信用するならば、10人なら約9割の確率で外れ、20人なら約6割、最も公平なのは23人の場合で5割、30人なら3割で40人なら1割しか外れない。

これが「誕生日のパラドックス」と呼ばれるのは、このように、直感的には少ないと思われる人数であっても、誕生日のペアが成立することにあるのだが、ここはプログラマ、さっそくこれを検証してみることにするわけだけど、コード自体は非常に簡単だ。

[10, 20, 22, 23, 30, 40].each do |n| 
  not_uniq_birthday = 0
  1.upto(1000000) do
    people = Array.new(n) { Random.rand(366)}
    not_uniq_birthday += 1 if people.size == people.uniq.size
  end
  puts "#{n}人の場合 => #{not_uniq_birthday}回外れ"
end

実行すると、以下のようなログとなる。

10人の場合 => 883407回外れ
20人の場合 => 589574回外れ
22人の場合 => 525452回外れ
23人の場合 => 493833回外れ
30人の場合 => 294524回外れ
40人の場合 => 109550回外れ

びっくりするほど、計算通りに外れてくれることがわかる。

結論

誕生日のパラドックス自体は有名な話ではあるけれども、しかし実際にそういう風に計算ができる、というのは知っていたとしても、では試行すると、本当にその計算に近い数字が出るのか、ということをやってみた人は少ない気がする。プログラミングによって、こういう素朴なことを試せるようになったようになったことはいいことだと思う。計算だけでなく、実験もどんどんやっていって、「確かに計算通りになるんだな!」というのを実感していってみたいと思う。

哲学101問 (ちくま学芸文庫)

哲学101問 (ちくま学芸文庫)