見出し画像

ほんで、MEGA BIGくじにいくら賭ければいいの?

この記事では、MEGA BIGくじの最適な賭け額、最適な賭け額の算出方法について説明する。

※この記事の内容は間違っている可能性があるので注意してください。間違いがあればご指摘いただけると嬉しいです。できれば専門家にレビューしてほしいです。
※この記事はMEGA BIGの購入を薦めているわけではありません。


MEGA BIG 祭

2024/8/30、MEGA BIG祭が突如発生した。
MEGA BIGは通常期待値がマイナスであるが、台風の影響でサッカーの試合が一部中止になり第1476回のMEGA BIGの期待値が1を超える可能性があるという投稿があったのだ。

金のにおいに敏感なクラスタではこの情報が広まり、わりと多くの人がMEGA BIGを買っていた。
中には全財産をぶっこむ者も現れた。
(後述の分析からすると全財産7350万円で全財産賭けるのはほぼ最適行動)

期待値は1以上ならたしかに儲かりそう!
ワイもMEGA BIG買うぞ~。

ほんで、MEGA BIGくじにいくら賭ければいいの?

期待値1以上で儲かりそうなのはわかった。
問題は、いくら賭けるかだ。いくら賭けるかは期待値だけじゃなくてリスクも考慮して決める。

結論
最適な賭け額は売上額と総資産によって決まる。今回の売上額だとざっくりこんな感じ

  • 総資産 2000万円以下:0円。指をくわえて祭を見るのみ。

  • 総資産 2000~3000万円:資産の0~40%

  • 総資産 3000~5000万円:資産の40~80%

  • 総資産 5000万円~1億円:資産の80~100%

  • 総資産 1~18億円:資産ぜんつっぱ

  • 総資産 18億円以上:18億円(金くれ)

※12試合中4試合中止&キャリーオーバーが大きいという特別な条件が揃った今回のみが上記の数値となる。通常は総資産がいくらであっても最適な賭け額は0円である。
※最適な賭け額を用意できなかった場合、最適な賭け額以下であれば賭けた方が良い。例えば総資産1億円でも10万円しか用意できなかった場合、10万円賭けるのが良い。
(総資産すべてを現金化してMEGA BIG購入するのは現実的には難しい)
※総資産とは不動産とか株式とかもすべてを含め、借金は差し引く。例えば現金1億、株1億、借金0.5億なら総資産は1.5億。サラリーマンとか定期収入がある人は月収×3ぐらいを追加してもいいかも、なんとなくそんな気がする。
※売上額が大きくなると最適な賭け額は小さくなる。締切直前に売上額を確認して投票する戦略を取る場合に、競合も同じ戦略で締切直前に多額の投票をしてくると賭けすぎになりうることに注意。
※後述するが、1位の賞金のみを考えている。以降の検討もすべて同じ。2位以下は配当額は低いが高確率で当たるので、考慮すれば最適な賭け額は大きくなるはず。

もっと詳しく

くじ1口の期待値

くじ1口の期待値はMEGA BIGくじの売上額によって決まる。
今回の売上額は4,713,264,600円(4.7e9円)だった。
以下のグラフからたしかに期待値は1以上ある。

くじ1口の期待値

最適な投資額

賭ける額(最適な投資額)はケリー基準を使って決められる。ざっくりいうと、自分の資産額を固定したときに対数リターンが一番大きくなる時の投資額が最適な投資額になる。
(本当にケリー基準が最適なんか?という議論はある。実際にはケリー基準の半分の額を採用するハーフケリーだとか、ケリー基準を小さくしたやつが使われることが多い。)

以下のグラフから、
①資産額が2000万円以下は0円賭ける(=賭けない)ことが最適
②資産額が2000万円~1億円は資産の一部を賭けることが最適
③資産額が1~18億円はほぼ全力で賭けることが最適
④資産額が一定額(約18億円)を超えるとそれ以上賭けないことが最適
であることがわかる。

(③はなかなか衝撃的な結果だと思った。冒頭で紹介した造船太郎氏は最適行動を取っていて凄い。)

資産額に対する最適な投資額
資産額に対する最適な投資額(資産額1億円以下の範囲を抜粋)


これは定性的には以下のように考えられる。

  • 当選確率が小さいので、資産額が小さいと外したときのダメージが大きいので賭けない方が良い。

  • 多くの口数を賭けるほど安定して1等が当たるので、資産額が大きいのであれば適切な範囲で多くの口数賭けた方が良い。

  • 一定以上の額になるとキャリーオーバーのうま味が限界に達するので、効果的な賭け額には上限がある。

投資額と対数リターンの関係

例えば総資産額3000万円のとき、投資額と対数リターンの関係は下図のようになる。
・投資額が0円のときに対数リターンが0
・最適な投資額に向かって対数リターンが大きくなっていく
・最適な投資額1250万円で対数リターンが最大になる
・最適な投資額1250万円を超えると小さくなっていく
対数リターン>0となる投資額であればお金が増えることが期待できる。
そのため、仮に最適な投資額を用意できなかったとしても、最適投資額より小さい投資額であれば賭けるのは適切な行動となる。
例えば総資産3000万円でも10万円しか用意できなかった場合、10万円賭けるのが良い。

投資額に対する対数リターン(総資産額3000万円)

儲かる確率

最適な投資額を賭けたとして、どれぐらい確率でお金が増えるのか?
資産額1億円ぐらいあると80%ぐらいの確率でお金が増えるらしい。いいじゃん。
(2000万円~1億円の範囲で確率が増えたり減ったりしている理由はわからない(※)。もしかしたらバグとか何か間違えたりとかしているかも。)

資産額に対する最適投資額を賭けた時に、お金が増える確率

(※)[追記]お金が増えるために必要な1等の当選数が変わるので確率が増えたり減ったりするのではないかというコメントをもらった。ありがとうございます。本当にそうなっているか確認できていないが、そういうことが起きていそうな気がする。

リターンの大きさ

最適な投資額を賭けたとして、どれぐらいお金が増えるのか?
資産額1億円ぐらいあるとざっくり1.4倍(=exp(対数リターン))ぐらいになるらしい。アツい。

資産額に対する最適投資額を賭けた時の平均対数リターン

最適な投資額の算出方法

以下の方法で平均対数リターンをモデリングし、資産額を固定した時に平均対数リターンが最大となる投資額を最適な投資額としている。手計算は大変なのでpythonで数値計算している。

モデリング

■投票口数:N(=15,190,882口)
ただし自分の投票口数を含まない。
ちなみに投票口数はリアルタイムで見れる。投票口数を監視しながら直前に投票すると良い。

■当選確率:p(=1/4^8)
1試合当たり4パターンで8試合なので。
(なお、試合中止がなければ12試合なので1/4^12。12試合あったら当選確率が低すぎて話にならない。)

■1口あたりの金額:u(=300円)

■キャリーオーバー金額:C(=5,830,122,720円)
(キャリーオーバーが十分大きくないと期待値は絶対にマイナス)

■1等の当選金配分割合:r(=0.35)
胴元に50%持っていかれたうちの70%が一等に配分されるため。

■1等賞金の1口あたりの上限額:L(=1,200,000,000円)
上限額に達する確率はかなり低いのでほぼ影響しない

■2等以下を無視する
めんどくさいため。より厳しい(期待値が小さくなる側の)評価になっている。
2等以下は高確率で当たるのと配当が1口あたり300円と割高になったので、2等以下を考慮すると期待値が30%ぐらいあがるらしい。

■自分の総資産:A

■自分の投票数:M
最適なこれを求めたい。


自分以外の1等の当選口数をnとする。nは以下の二項分布に従う。

$$
P(X = n \mid N, p) = \binom{N}{n} p^n (1-p)^{N-n}
$$

自分の1等の当選口数をmとする。mは以下の二項分布に従う。

$$
P(X = m \mid M, p) = \binom{M}{m} p^m (1-p)^{M-m}
$$

1等に当選した1口あたりの賞金をRとする。

$$
R =
\begin{cases}
\min(\frac{(N+M) \cdot u \cdot r + C}{n+m},L) & \quad \text{if } n+m > 0 \\
0 & \quad \text{if } n+m = 0
\end{cases}
$$

平均対数リターンをUとする。

$$
U=\sum_{m=0}^{M} \sum_{n=0}^{N} P(X = m \mid M, p)P(X = n \mid N, p)\log\left(\frac{A-Mu+mR}{A}\right)
$$

数値計算

PythonでA毎にMを変更してUを計算し、Uが最大となるMをAの最適な投資額とする。コードは記事の最後に載せた。

マジで計算合ってる?

わからない。間違っていてもおかしくない。
一応それっぽい結果であることは確認している。

①最適投資額が0円以上となる総資産額
最適投資額が0円以上になるのが総資産額約2000万円以上であった。
券の組み合わせは全部で4^8とおりであり、4^8口買うために必要な金額は約1966万円でそれっぽい。
あと1口のみ買う場合のケリー基準でも総資産額2000万円ぐらいで最適賭け額が1口(300円)以上になると思うのでそれっぽい。

②最適投資額の上限
売上額は約45億円、1等の当選金配分割合0.35、キャリーオーバー58億円であった。自分の購入額をX億円とする。
自分以外の45億円分の投票と自分のX億円の投票で1等の賞金を分けあうことから、Xが十分大きいとき自分がもらえる1等賞金は次式となる。

$$
((45+X) \cdot 0.35 + 58)\cdot\frac{X}{45 + X}
$$

よって、リターンは次式となる。

$$
f(X) = ((45+X) \cdot 0.35 + 58)\cdot\frac{X}{45 + X} -X
$$

リターンf(X)が最大になるXは約18億円となる。
グラフでも最適投資額の上限は18億円であった。
ちゃんと一致している。

ちなみに、2等以降も考慮するとリターンは次式になる。

$$
f(X) = ((45+X) \cdot 0.5 + 58)\cdot\frac{X}{45 + X} -X
$$

リターンf(X)が最大になるXは約27億円となる。2等以降の影響けっこう大きいかも。

ほんで、お前はいくら賭けたの?

3000円…(´・ω・`).;:…(´・ω...:.;::..(´・;::: .:.;: サラサラ..
(この検証は締切後に行っていて、購入時は何も考えずに適当に金額決めちゃった…。最適行動についての直感が弱い。とはいえ、全資産賭けるのが最適だとわかっていたとしても10%すら賭けられなかったと思う。)

最適投資額算出のPythonコード

雑ですまん。

# -*- coding: utf-8 -*-
"""Untitled1.ipynb

Automatically generated by Colab.

"""

!pip install japanize-matplotlib

import japanize_matplotlib
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from scipy.stats import binom

"""## モデリング"""

def binomial_prob_mass(num_vote: int, win_prob: float, max_num_win: int):
    num_win = np.arange(max_num_win)
    num_win_prob = binom.pmf(num_win, num_vote, win_prob)
    return num_win_prob, num_win


def calc_max_num_win(num_vote, win_prob, max_num_win_rate=10):
    max_num_win = int((int(num_vote * win_prob) + 1) * max_num_win_rate) + 1
    max_num_win = min([max_num_win, num_vote + 1])
    return max_num_win


def calc_win_reward(
        num_vote,
        my_num_vote,
        carry_over_jpy,
        num_win,
        my_num_win,
        unit_amount_jpy=300,
        limit_pay_jpy=1_200_000_000,
        allocation_ratio=0.35,
    ):
    total_allocation = (num_vote + my_num_vote) * unit_amount_jpy * allocation_ratio + carry_over_jpy
    total_num_win = num_win + my_num_win
    total_num_win = np.where(total_num_win != 0, total_num_win, np.inf)

    win_reward = total_allocation / total_num_win
    win_reward = np.clip(win_reward, 0, limit_pay_jpy)
    return win_reward


def calc_expected_value(
        num_vote,
        win_prob,
        my_num_vote,
        carry_over_jpy,
        target_type,
        my_asset_amount_jpy,
        unit_amount_jpy=300,
        limit_pay_jpy=1_200_000_000,
        allocation_ratio=0.35,
        plot=False,
    ):
    max_num_win = calc_max_num_win(num_vote, win_prob)
    # (N,)
    num_win_prob, num_win = binomial_prob_mass(num_vote, win_prob, max_num_win)
    if plot:
        plt.figure(figsize=(3, 3))
        plt.plot(num_win, num_win_prob)
        plt.show()

    # (1, N)
    num_win_prob = num_win_prob[np.newaxis]
    num_win = num_win[np.newaxis]

    max_num_win = calc_max_num_win(my_num_vote, win_prob)
    # (M,)
    my_num_win_prob, my_num_win = binomial_prob_mass(my_num_vote, win_prob, max_num_win)
    if plot:
        plt.figure(figsize=(3, 3))
        plt.plot(my_num_win, my_num_win_prob)
        plt.show()
    # (M, 1)
    my_num_win_prob = my_num_win_prob[:, np.newaxis]
    my_num_win = my_num_win[:, np.newaxis]

    # (M, N)
    win_reward = calc_win_reward(
        num_vote,
        my_num_vote,
        carry_over_jpy,
        num_win,
        my_num_win,
        unit_amount_jpy,
        limit_pay_jpy,
        allocation_ratio,
    )

    if plot:
        plt.figure(figsize=(3, 3))
        plt.hist(win_reward.flatten(), bins=100, log=True)
        plt.show()

    if target_type == "return":
        x = (my_asset_amount_jpy - my_num_vote * unit_amount_jpy + my_num_win * win_reward) / my_asset_amount_jpy
    elif target_type == "log_return":
        x = (my_asset_amount_jpy - my_num_vote * unit_amount_jpy + my_num_win * win_reward) / my_asset_amount_jpy
        x = np.log(x)
    else:
        raise ValueError()

    if plot:
        plt.figure(figsize=(3, 3))
        plt.hist(x.flatten(), bins=100, log=True)
        plt.show()

    # (M, N)
    prob = my_num_win_prob * num_win_prob
    expected_values = (prob * x).sum()

    return expected_values, prob, x

"""## 期待値計算"""

unit_amount_jpy = 300

win_prob = (1/4) ** 8
my_num_vote = 1
carry_over_jpy = 5_830_122_720
target_type = "return"
my_asset_amount_jpy = unit_amount_jpy * my_num_vote

num_votes = np.linspace(2_000_000, 30_000_000, 100).astype(int)

total_sales = []
return_expected_values = []
for num_vote in num_votes:
    total_sales.append(num_vote * unit_amount_jpy)
    return_expected_value, _, _ = calc_expected_value(num_vote, win_prob, my_num_vote, carry_over_jpy, target_type, my_asset_amount_jpy)
    return_expected_values.append(return_expected_value)

plt.figure(figsize=(3, 3))
plt.plot(total_sales, return_expected_values, label="期待値")
plt.plot(total_sales, [1.0] * len(total_sales), label="1.0")
plt.xlabel("売上金額[円]")
plt.ylabel("リターン(=払い戻し額/投資額)")
plt.legend()
plt.show()

"""## 投資額毎の平均対数リターンの変化"""

unit_amount_jpy = 300

num_vote = 15_190_882
win_prob = (1/4) ** 8
carry_over_jpy = 5_830_122_720
target_type = "log_return"

my_asset_amount_jpy = 30_000_000
my_num_votes = (np.linspace(0, 1.0, 1000) * my_asset_amount_jpy / unit_amount_jpy).astype(int)

total_bets_jpy = []
log_return_expected_values = []
for my_num_vote in my_num_votes:
    total_bets_jpy.append(my_num_vote * unit_amount_jpy)
    log_return_expected_value, _, _ = calc_expected_value(num_vote, win_prob, my_num_vote, carry_over_jpy, target_type, my_asset_amount_jpy)
    log_return_expected_values.append(log_return_expected_value)

print(f"{log_return_expected_values=}")

plt.figure(figsize=(3, 3))
plt.plot(total_bets_jpy, log_return_expected_values, label="期待値")
plt.plot(total_bets_jpy, [0.0] * len(total_bets_jpy), label="0.0")
plt.xlabel("投資額[円]")
plt.ylabel("対数リターン)")
plt.legend()
plt.show()

"""## 最適投資額の算出"""

unit_amount_jpy = 300

num_vote = 15_190_882
win_prob = (1/4) ** 8
carry_over_jpy = 5_830_122_720
target_type = "log_return"

my_asset_amounts_jpy = [
    1_000_000,
    10_000_000,
    20_000_000,
    25_000_000,
    30_000_000,
    35_000_000,
    40_000_000,
    45_000_000,
    50_000_000,
    55_000_000,
    60_000_000,
    65_000_000,
    70_000_000,
    75_000_000,
    80_000_000,
    85_000_000,
    90_000_000,
    95_000_000,
    100_000_000,
    200_000_000,
    300_000_000,
    400_000_000,
    600_000_000,
    800_000_000,
    1_000_000_000,
    1_500_000_000,
    2_000_000_000,
    2_500_000_000,
    3_000_000_000,
    4_000_000_000,
]

best_total_bets_jpy = []
best_log_return_expected_values = []
best_plus_ret_probs = []
for my_asset_amount_jpy in my_asset_amounts_jpy:
    print("------------")
    print(f"{my_asset_amount_jpy=}")

    my_num_votes = (np.linspace(0, 0.99999, 200) * my_asset_amount_jpy / unit_amount_jpy).astype(int)

    total_bets_jpy = []
    log_return_expected_values = []
    plus_ret_probs = []
    for my_num_vote in my_num_votes:
        total_bets_jpy.append(my_num_vote * unit_amount_jpy)

        log_return_expected_value, prob, x = calc_expected_value(num_vote, win_prob, my_num_vote, carry_over_jpy, target_type, my_asset_amount_jpy)
        log_return_expected_values.append(log_return_expected_value)

        plus_ret_prob = (prob * (x > 0)).sum()
        plus_ret_probs.append(plus_ret_prob)

    max_index = np.argmax(log_return_expected_values)
    best_total_bets_jpy.append(total_bets_jpy[max_index])
    best_log_return_expected_values.append(log_return_expected_values[max_index])
    best_plus_ret_probs.append(plus_ret_probs[max_index])

    print(f"{total_bets_jpy[max_index]=}")
    print(f"{log_return_expected_values[max_index]=}")
    print(f"{plus_ret_probs[max_index]=}")

print(f"{my_asset_amounts_jpy=}")
print(f"{best_total_bets_jpy=}")
print(f"{best_log_return_expected_values=}")
print(f"{best_plus_ret_probs=}")


my_asset_amounts_jpy = np.array(my_asset_amounts_jpy)
best_total_bets_jpy = np.array(best_total_bets_jpy)

plt.figure(figsize=(6, 3))
plt.plot(my_asset_amounts_jpy, best_total_bets_jpy, label="最適な投資額")
plt.plot(my_asset_amounts_jpy, my_asset_amounts_jpy, label="資産額=投資額", linestyle="dashed")
plt.xlabel("資産額[円]")
plt.ylabel("投資額[円]")
plt.xscale("log")
plt.ylim(0, max(best_total_bets_jpy) * 1.1)
plt.legend()
plt.show()

plt.figure(figsize=(6, 3))
plt.plot(my_asset_amounts_jpy[my_asset_amounts_jpy<100_000_000], best_total_bets_jpy[my_asset_amounts_jpy<100_000_000], label="最適な投資額")
plt.plot(my_asset_amounts_jpy[my_asset_amounts_jpy<100_000_000], my_asset_amounts_jpy[my_asset_amounts_jpy<100_000_000], label="資産額=投資額", linestyle="dashed")
plt.xlabel("資産額[円]")
plt.ylabel("投資額[円]")
plt.xscale("log")
plt.ylim(0, max(best_total_bets_jpy[my_asset_amounts_jpy<100_000_000]) * 1.1)
plt.legend()
plt.show()

fig, ax = plt.subplots(1, 1, figsize=(6, 3))

ax.plot(my_asset_amounts_jpy, best_total_bets_jpy, label="最適な投資額")
ax.set_xlabel("資産額[円]")
ax.set_ylabel("投資額[円]")
ax.set_xscale("log")

ax2 = ax.twinx()
ax2.plot(my_asset_amounts_jpy, best_plus_ret_probs, label="儲かる確率 (at 最適な投資額)", color="red")
ax2.set_ylabel("儲かる確率")
ax2.set_xscale("log")

# 第一軸と第二軸の両方のハンドルとラベルを取得し、1つの凡例を作成
ax.legend(ax.get_lines() + ax2.get_lines(), [l.get_label() for l in ax.get_lines() + ax2.get_lines()], loc="upper left")

plt.show()


fig, ax = plt.subplots(1, 1, figsize=(6, 3))

ax.plot(my_asset_amounts_jpy, best_total_bets_jpy, label="最適な投資額")
ax.set_xlabel("資産額[円]")
ax.set_ylabel("投資額[円]")
ax.set_xscale("log")

ax2 = ax.twinx()
ax2.plot(my_asset_amounts_jpy, best_log_return_expected_values, label="平均対数リターン (at 最適な投資額)", color="red")
ax2.set_ylabel("対数リターン")
ax2.set_xscale("log")

# 第一軸と第二軸の両方のハンドルとラベルを取得し、1つの凡例を作成
ax.legend(ax.get_lines() + ax2.get_lines(), [l.get_label() for l in ax.get_lines() + ax2.get_lines()], loc="upper left")

plt.show()


この記事が気に入ったらサポートをしてみませんか?