「第1476回 MEGA BIG」シミュレーション

このツイートに感銘を受けたのでシミュレーションしてみた。
10,000回×15,710,882口をランダム生成してみて、全体の結果と245,000口を購入した場合の収支です。

なお、2-6等は別記事の通り300円なる想定です。

1等当選金の統計:
  1%値: 2417.13万円
  5%値: 2508.44万円
 10%値: 2566.13万円
 15%値: 2606.17万円
 20%値: 2637.11万円
 25%値: 2668.47万円
 30%値: 2690.33万円
 35%値: 2712.19万円
 40%値: 2734.50万円
 45%値: 2757.13万円
 50%値: 2780.09万円
 55%値: 2803.36万円
 60%値: 2827.04万円
 65%値: 2851.26万円
 70%値: 2875.84万円
 75%値: 2901.15万円
 80%値: 2939.12万円
 85%値: 2978.57万円
 90%値: 3032.58万円
 95%値: 3117.57万円
 99%値: 3270.33万円
  平均: 2793.34万円

1等当選口数の統計:
  1%値: 204.00口
  5%値: 214.00口
 10%値: 220.00口
 15%値: 224.00口
 20%値: 227.00口
 25%値: 230.00口
 30%値: 232.00口
 35%値: 234.00口
 40%値: 236.00口
 45%値: 238.00口
 50%値: 240.00口
 55%値: 242.00口
 60%値: 244.00口
 65%値: 246.00口
 70%値: 248.00口
 75%値: 250.00口
 80%値: 253.00口
 85%値: 256.00口
 90%値: 260.00口
 95%値: 266.00口
 99%値: 276.00口
  平均: 239.86口

還元率の統計:
  1%値: 173.70%
  5%値: 173.70%
 10%値: 173.70%
 15%値: 173.70%
 20%値: 173.70%
 25%値: 173.70%
 30%値: 173.70%
 35%値: 173.70%
 40%値: 173.70%
 45%値: 173.70%
 50%値: 173.70%
 55%値: 173.70%
 60%値: 173.70%
 65%値: 173.70%
 70%値: 173.70%
 75%値: 173.70%
 80%値: 173.70%
 85%値: 173.70%
 90%値: 173.70%
 95%値: 173.70%
 99%値: 173.70%
  平均: 173.70%

245,000口購入時の収支の統計:
  1%値: -4988.85万円
  5%値: -2261.77万円
 10%値: -1967.79万円
 15%値: 353.42万円
 20%値: 625.04万円
 25%値: 945.33万円
 30%値: 2738.32万円
 35%値: 3141.79万円
 40%値: 3489.92万円
 45%値: 3919.95万円
 50%値: 5158.46万円
 55%値: 5808.21万円
 60%値: 6281.27万円
 65%値: 6884.42万円
 70%値: 8101.55万円
 75%値: 8911.53万円
 80%値: 9774.29万円
 85%値: 11151.52万円
 90%値: 12638.87万円
 95%値: 15151.72万円
 99%値: 20020.82万円
  平均: 5448.32万円

7350万円&今回のシミュレーションの場合、下振れ11.9%を回避できれば儲かりそうでした
幸せになって欲しい!!

>>> np.quantile(merged_results[3], 11.8/100)
-9.781935999999309
>>> np.quantile(merged_results[3], 11.9/100)
15.788008000000014

コードも置いておきます。
※メモリもりもりM2 Ultra(192GB)のコードなので適宜調整ください

#!python3

import gc
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import font_manager

# 複数回のシミュレーション結果をマージしてヒストグラムを作成
n_iterations = 20              # 500回のシミュレーションを20回繰り返す
n_simulate_per_iteration = 500 # メモリ192GB想定、適宜調整を
results = []

# プロット設定
plt.figure(figsize=(15, 20))
font_path = '/System/Library/Fonts/ヒラギノ角ゴシック W3.ttc'
font_prop = font_manager.FontProperties(fname=font_path)
plt.rcParams['font.family'] = font_prop.get_name()
titles = ['1等当選金の分布', '1等当選口数の分布', '還元率の分布', '245,000口購入時の収支の分布']
xlabels = ['1等当選金額(万円)', '1等当選口数', '還元率(%)', '収支(万円)']

def run_simulation(n_simulate):
    # シミュレーション設定
    YEN_CARRYOVER  = 5_830_122_720    # キャリーオーバー
    YEN_SELL       = 4_713_264_600    # 売上
    N_TICKET       = 15_710_882       # 総投票口数
    N_GAME         = 8                # 対象ゲーム数(第1476回は4試合中止で8試合)
    N_BUY_SIMULATE = 245_000          # シミュレーションでの購入口数

    # N_SIMULATE回分のランダム投票を作成
    np_vote = np.random.randint(low=0, high=3+1, size=n_simulate*N_TICKET*N_GAME, dtype=np.uint8).reshape(n_simulate, N_TICKET, N_GAME)

    # 結果が正解(0)のゲーム数をカウント
    np_count_matched = (np_vote == 0).sum(axis=2)

    # 1-6等の当選口数をカウント
    np_count_prize = np.array([(np_count_matched == i).sum(axis=1) for i in range(N_GAME, N_GAME-6, -1)]).T

    # 1等の払戻金額を計算
    np_prize_1st = (int(YEN_CARRYOVER + YEN_SELL * 0.5) - np_count_prize[:, 1:].sum(axis=1) * 300) / np_count_prize[:, 0] // 10 * 10

    # 還元率を計算
    np_odds = (np_prize_1st * np_count_prize[:, 0] + 300 * np_count_prize[:, 1:].sum(axis=1)) / YEN_SELL * 100

    # N_BUY口購入した場合の収支計算
    np_count_matched_simulate = np_count_matched[:, :N_BUY_SIMULATE]
    np_count_prize_simulate   = np.array([(np_count_matched_simulate == i).sum(axis=1) for i in range(N_GAME, N_GAME-6, -1)]).T
    np_profit_simulate        = np_prize_1st * np_count_prize_simulate[:, 0] + 300 * np_count_prize_simulate[:, 1:].sum(axis=1) - N_BUY_SIMULATE * 300
    np_profit_simulate_man    = np_profit_simulate / 10000

    # 単位変換: 1等当選金を万円に
    np_prize_1st_man = np_prize_1st / 10000

    return np_prize_1st_man, np_count_prize[:, 0], np_odds, np_profit_simulate_man

for i in range(n_iterations):
    print(f"Running simulation {i+1}/{n_iterations}")
    np.random.seed(i)  # 各イテレーションで異なるシードを使用
    results.append(run_simulation(n_simulate_per_iteration))
    gc.collect()  # メモリを解放

# 結果をマージ
merged_results = [np.concatenate([r[i] for r in results]) for i in range(4)]

#プロット
for i, (data, title, xlabel) in enumerate(zip(merged_results, titles, xlabels), 1):
    plt.subplot(4, 1, i)
    plt.hist(data, bins=50, edgecolor='black', weights=np.ones_like(data)/len(data)*100)
    plt.title(title)
    plt.xlabel(xlabel)
    plt.ylabel('頻度 (%)')
    plt.grid(True, linestyle='--', alpha=0.7)
    if i == 3:  # 還元率のヒストグラム
        x_min, x_max = data.min(), data.max()
        plt.xlim(int(x_min * 0.99), int(x_max * 1.01))
        plt.xticks(np.linspace(int(x_min * 0.99), int(x_max * 1.01), 10))

plt.tight_layout()
plt.savefig('toto_simulation_histograms_merged.png', dpi=300, bbox_inches='tight')
plt.close()

print("マージされたヒストグラムが 'toto_simulation_histograms_merged.png' として保存されました。")

# 統計情報の出力
def print_stats(data, name, unit):
    percentiles = [1, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 99]
    print(f"{name}の統計:")
    for p in percentiles:
        print(f"{p:3d}%値: {np.percentile(data, p):.2f}{unit}")
    print(f"  平均: {np.mean(data):.2f}{unit}")

for data, name, unit in zip(merged_results, ["1等当選金", "1等当選口数", "還元率", "245,000口購入時の収支"], ["万円", "口", "%", "万円"]):
    print_stats(data, name, unit)


いいなと思ったら応援しよう!