3
2

誕生日のパラドックスをpythonを使って検証してみた

Last updated at Posted at 2024-03-25

概要

誕生日のパラドックスとは、「同じ誕生日の人がいる確率が50%を超えるのは、何人集まれば良いか?」という問題において、結果が直感と反しているというパラドックスです。

確率を計算すると以下の結果になります。

  • 40人の場合は、約89.12%
  • 23人の場合は、約50.73%

個人的にも納得できないので、Pythonを使って検証をしていきたいと思います。

計算式

同じクラス内に1組以上の誕生日が一致する確率は以下の式から導き出せます。(nは1クラスの人数)

p(n) = 1 - \frac{365!}{365^n(365-n)!}

コーディング

関数①:計算式を基にコーディング

まずは、計算式を基にプログラムを書いてみました。

def birthday_paradox_prob_calc(n: int) -> float:
    '''
    誕生日のパラドックスの計算を行う
    Args:
      n (int): 人数
    Returns:
      float: 確率(%)
    '''
    if n > 365:
        return 100.0

    prob_no_shared_birthday = 1.0
    for i in range(n):
        prob_no_shared_birthday *= (365 - i) / 365
    prob_shared_birthday = 1 - prob_no_shared_birthday
    return prob_shared_birthday * 100

0から100までの折れ線グラフを作成すると、以下のようなグラフができます。
たしかに、23人くらいで50%くらいになっています。

Figure_1.png

関数②:シミュレーションを行うようにコーディング

こちらの関数は、実際に1から365の配列を作成し配列内に重複があるかどうかの割合を返すプログラムです。try_countに大きい数値を指定することで、より正確なシミュレーションができます。

import random

def birthday_paradox_prob_act(n: int, try_count: int = 10000) -> float:
    '''
    誕生日のパラドックスのシミュレーションを実行する
    Args:
      n (int): 人数
      try_count (int): 試行回数
    Returns:
      float: 確率(%)
    '''
    if n > 365:
        return 100.0

    true_count = 0
    for _ in range(try_count):
        random_numbers = [random.randint(1, 365) for _ in range(n)]
        if len(random_numbers) != len(set(random_numbers)):
            true_count += 1

    return (true_count / try_count) * 100

試行回数を50を指定するとグラフは以下のようになりました。
(50を指定すると各人数のところで50回試行しその割合をグラフに表示します。そのため上がったり下がったりしています。)

Figure_1.png

試行回数を10,000を指定するとグラフは以下のようになり、ほぼ関数①と同じ形になりました。

Figure_1.png

グラフを出力

matplotlibを使用して、2つのグラフを表示します。

import matplotlib.pyplot as plt

def main():
    x = list(range(0, 101, 5))
    calc_data = [birthday_paradox_prob_calc(n) for n in x]
    act_data = [birthday_paradox_prob_act(n) for n in x]

    plt.rcParams['font.family'] = 'MS Gothic'
    plt.plot(x, calc_data, marker='o')
    plt.plot(x, act_data, marker='x')
    plt.title('誕生日のパラドックス')
    plt.xlabel('人数')
    plt.ylabel('確率 (%)')
    plt.grid(True)
    plt.show()

試行回数を50を指定するとグラフは以下のようになりました。
ぶれてはいますが、関数①のグラフと大体同じになってますね。

(青色が関数①、オレンジ色が関数②)

Figure_1.png

試行回数を10,000を指定するとグラフは以下のようになり、よくみると僅かにずれていますが、確率が収束したのが分かります。

Figure_1.png

おわりに

誕生日のパラドックスを検証するプログラムを作ってみました。
冒頭で話した、23人で50%というのは正しそうです...

ちょっと悔しいですが、気になった問題があったら検証してみたいと思います。

ここまで読んでいただきありがとうございました。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2