乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

Python

今回は投資信託の比較をするための データ抽出についてです。


解説動画はこちら


 

投資信託の成績比較


今回は投資信託の銘柄選定のために
投資信託の比較をしていきたいと思います。

データをお借りする先はこちら

みんかぶ



データを取得する

リンク先から、比較したい銘柄を選択して
「選択したファンドを比較する」ボタンをクリックすると
銘柄比較画面に飛べるので、そこのURLをコピーしておきます。

このコードでそのページのデータが取得できます。

import pandas as pd
from bs4 import BeautifulSoup
import requests
import re

url = "ここにペースト"
res = requests.get(url)


データ化する

そのままでは使いづらいので
読み込みできる形にします。
soup = BeautifulSoup(res.text, "html.parser")
table = soup.find("table",class_=re.compile("w-full border-t border-slate-300"))
trs = table.find_all("tr")
data = []
for tr in trs:
    tds = tr.find_all("td")
    tmp = []
    for td in tds:
        tmp.append(td.text.replace("\n",""))
    data.append(tmp)
   
df = pd.DataFrame(data)
df

これでデータフレームになり見やすくなりました。
ここから、使うデータだけに絞り込み整形していきます。




ファンド名、リターン、シャープレシオのみ取得する


先ほどのまでのコードで
全体のデータが取得できていたら
必要なものののみ整形します。

今回はファンド名、リターン、シャープレシオの値を使用します。

リターン、シャープレシオの値は
1,3,5年ごとの値になっているので
正規表現でうまく抜き出してデータ化します。
fand_name = data[0]
r1,r3,r5 = [],[],[]
for text in data[10]:
    pat = re.compile(
        r'(\d+)年([+-]?\d+(?:\.\d+)?%?|-)'
        r'(?=\d年|$)'
    )
    pairs = re.findall(pat, text)
    result = {int(y): float(v.replace("%","")) if v!="-" else 0 for y, v in pairs }
    r1.append(result[1])
    r3.append(result[3])
    r5.append(result[5])
    
s1,s3,s5 = [],[],[]
for text in data[11]:
    pat = re.compile(
      r'(\d+)年([+-]?\d+(?:\.\d+)?|-)'
      r'(?=\d年|$)'
    )
    pairs = re.findall(pat, text)
    result = {int(y): float(v) if v!="-" else 0 for y, v in pairs }
    s1.append(result[1])
    s3.append(result[3])
    s5.append(result[5])

df2 = pd.DataFrame()
df2["ファンド名"] = fand_name
df2["1年リターン"] = r1
df2["3年リターン"] = r3
df2["5年リターン"] = r5
df2["1年シャープレシオ"] = s1
df2["3年シャープレシオ"] = s3
df2["5年シャープレシオ"] = s5
df2

ここでうまくファンド名と数値データのみになっていれば
可視化して比較することができます。



リターンとシャープレシオで散布図にする

今回はリターンとシャープレシオを用いて比較していきます。

シャープレシオというのは

投資信託のリターン ÷ リスク(値動きのブレ)

の値で、リスクに対して
どれだけ効率的にリターンをあげているかの目安です。

シャープレシオが高い = より効率よく運用されている
ということになります。

シャープレシオが低い  = リスクを取りすぎている
とも取れます。


一般的な目安としては

シャープレシオの値評価
2.0以上非常に優れた運用成績
1.0から2.0良好な運用成績
0.5から1.0平均的な運用成績
0.5未満改善の余地がある運用成績


ということになっているようです。

早速比較してみましょう
import plotly.express as px

fig = px.scatter(
    df2,
    x="5年リターン",
    y="5年シャープレシオ",
    color="ファンド名",
    hover_name="ファンド名",
    title="ファンド別:5年リターン × 5年シャープレシオ",
)

fig.update_layout(
    xaxis_title="5年リターン (%)",
    yaxis_title="5年シャープレシオ",
    legend_title="ファンド名",
)

fig.show()
スクリーンショット 2025-11-08 16.52.42






まとめ

銘柄が多すぎて選べない際は、リターンやシャープレシオ
手数料、信託報酬などで、色々比較するのが良いです。

同じリターンなら、シャープレシオが高いものの方が
より効率良く運用されているとみなせます。

なお短期のシャープは信用度が低いので
長い期間で見るようにしましょう。


今回はここまでです
それでは

今回はケリー基準を用いた
破産しないための最適資金配分のシミュレーションです。


解説動画はこちら

 




破産しないための最適な賭け金の割合



・問題
コインを投げて、表が出たら賭け金が2倍、裏が出たら全て失う賭けです
このコインは、55%の確率で表が出るやや有利なコインです

手元には100万円あり、このゲームを250回繰り返すとき
毎回いくら賭けるのが最適でしょうか?






--------------------------------------------------------




・回答
ケリー基準をもとに
最適な賭け金を算出することができます。


ケリー基準 : 
資産運用において
最適な資金配分比率を決定するための数式

割合 = (勝率 × オッズ − (1−勝率) ) / オッズ

ここに
勝率 0.55(55%)
オッズ(2倍-1倍 = 1)
を入れると

0.55 - 0.45 = 0.1

資金の0.1倍を賭けるのが
最適解ということになります。




250回の賭けコイン試行シミュレーション


先ほどの問題をシミュレーションするコードです。

初期資金 : 100万円
勝率 : 55%
回数 : 250回

シミュレーション結果を集計した結果を
出力する関数です。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
pd.options.display.float_format = '{:.2f}'.format

# 250回のコイントスの賭けシミュレーター
def coin_250(default_assets=1000000,times=250,win_rate=0.55):
  kelly_criterion_percentage = (win_rate - (1 - win_rate))
  data = []
  assets = default_assets
  for i in range(times):
    stakes = int(assets * kelly_criterion_percentage)
    assets -= stakes
    result = np.random.choice([1,0],p=[0.55,0.45])
    if result == 1:
      assets += stakes * 2
    tmp =[assets,stakes, int(result)]
    data.append(tmp)
  
  # 250回の結果をデータフレームで集計
  df = pd.DataFrame(data,columns=["assets","stakes","result"])
  max_assets = df["assets"].max()
  min_assets = df["assets"].min()
  max_stakes = df["stakes"].max()
  min_stakes = df["stakes"].min()
  final_ps = int(df["assets"].tail(1).values[0])
  profit = final_ps - default_assets
  win_times = int(df["result"].sum())
  lose_times = len(df) - win_times

  group_id = (df["result"] != df["result"].shift()).cumsum()
  run_lengths = df.groupby(group_id)["result"].sum()
  run_lengths_0 = df.groupby(group_id).apply(lambda x: (len(x) - x["result"].sum()))
  # 最大連敗回数
  max_lose = run_lengths_0.max()
  # 最大連勝回数
  max_win = run_lengths.max()

  result_250 = [max_assets,min_assets,max_stakes,min_stakes,final_ps,profit,win_times,lose_times,max_win,max_lose]
  return result_250

この関数を使って
1000セット試行を行います。
times = 1000
results = []
for i in range(times):
  data = coin_250()
  results.append(data)

columns = ["最大資産","最小資産","最大掛金","最小掛金","最終資産","最終利益","勝利回数","敗北回数","最大連勝","最大連敗"]
df = pd.DataFrame(results,columns=columns)
df.describe()
desc最大資産最小資産最大掛金最小掛金最終資産最終利益勝利回数敗北回数最大連勝最大連敗
count1000.001000.001000.001000.001000.001000.001000.001000.001000.001000.00
mean16066691.73576608.611588961.9557127.3511146821.0310146821.03137.19112.818.496.45
std33581089.69289491.313311346.2928006.3827692950.8927692950.897.917.912.271.56
min900000.007130.00100000.00713.007687.00-992313.00107.0089.004.003.00
25%2515432.75346220.75248966.5034622.001159975.00159975.00132.00108.007.005.00
50%6327419.00575743.50632741.5057574.003163743.502163743.50137.00113.008.006.00
75%15386038.00801900.001515707.5080274.008628829.007628829.00142.00118.0010.007.00
max541305769.001100000.0054130576.00100000.00390665788.00389665788.00161.00143.0026.0014.00


こんな感じの結果になりました

勝利回数の分布をプロットしてみると
download-1

おおよそ勝率55%の回数で正規分布に近似しますね

最終利益はこんな感じです
download

0以上になっていれば、利益が出ている
0未満は負け
ということになります。

今回は1000セットやって
利益が出た回数が762回
0未満は238回でした




まとめ

勝率とオッズが分かっているならケリー基準を元に
資金調整すれば、資金が0になることはほぼ無いです。

ただし、勝率とオッズ次第では
元の資金より減る事もあるので注意が必要です。

より負けたく無い場合は
ケリー基準の半分の割合でかけるなど
保守的な割合にすればもっと負けづらくなります。

株式などにも応用できるため
色々シミュレーションに使うと面白そうです。


今回はここまでです
それでは

今回は競技プログラミングについてです。

解説動画はこちら



競技プログラミングの世界をのぞいてみよう


競技プログラミングとは

プログラミングを「速く・正確に・美しく」解く
頭脳スポーツです。

与えられるのは数行の問題文と、入力例
頭の中でアルゴリズムを組み立て
最速のコードで正答を競います。


競技プログラミングの概要

問題が与えられ、入力例と出力例も与えられます。

問題文から、入力を受け取って
出力例になるようなプログラムコードを書いていきます。

実際の競技では
プログラムコードが開催側のシステム内で実行されて
成否が出るような仕組みになっています。


問題文の例(超初級)

問題:最大値を求めよう

N 個の整数が与えられるので
その中で最大の値を出力してください。

入力例
5
10 3 5 8 2

出力例
10

サンプルコード
N = int(input())
nums = list(map(int, input().split()))
print(max(nums))

通常は計算量や、数値の上限などの制約が与えられます。

正解かどうかの判定は、もっと大掛かりな数値で自動計算されているようで
数が多い場合、制限時間に間に合わないことも有ります。



競技プログラミングの主な大会

AtCoder(日本)
Topcoder(海外)
その他も有るみたいだけど、色々休止中



競技プログラミングはどんなことに役立つのか

1.思考を“構造化”する力が身につく
問題を分解し、条件を抽象化し、最適なアルゴリズムを選ぶ
という一連の思考過程を繰り返し構造化思考力を鍛えることができます

2.アルゴリズムの理解が“AIを使いこなす力”になる
AIが生成したコードを見て
計算量のボトルネック、論理が最適化されているか
入力制約に対応できているか等を判断できます

3.「論理×直感」問題解決能力を鍛える最高のトレーニング
限られた時間と資源の中で最適な解決方法を
論理と直感、両方を磨きながら鍛えることができます

4.キャリアや採用で有利に働く
近年、大企業やスタートアップなどで評価される傾向
アルゴリズムが必要な企業への就職で有利になります




競技プログラミングの問題を解いてみよう


中級くらいの問題集、動画を止めて考えてみてね

問題1 : 文字列の入れ替えチェック(中級)

2つの文字列 S, T が与えられます
S の文字を並べ替えて T にできるなら Yes
できないなら No と出力してください

入力例
listen
silent

出力例
Yes

-------------------------------------------------------------


解説

文字の出現がすべて一致していればOK
sorted(S) == sorted(T) で判定可能です。

S = input().strip()
T = input().strip()
print("Yes" if sorted(S) == sorted(T) else "No")



問題2 : 数字の組み合わせの和(中上級)

N 個の整数が与えられます
この中から3つ選んで、その和が K になる組み合わせの数を求めてください

入力例
5 10
2 3 5 4 1

出力例
2

-------------------------------------------------------------


解説

単純に3重ループで解くコードの場合
3つの数値の和が K になれば OK です。

N, K = map(int, input().split())
A = list(map(int, input().split()))

count = 0
for i in range(N):
    for j in range(i+1, N):
        for k in range(j+1, N):
            if A[i] + A[j] + A[k] == K:
                count += 1
print(count)

ただし実際の競技では
3重ループだと、制限に合わない場合があるので
別の方法を検討することが多いようです。

ここでは一例として次のようなアルゴリズムで
解くこととします。

    1.  配列 A の要素をセット S にしておく(探索用)
    2.  二重ループで全ペア (i, j) を探索
    3.  そのペアの和 A[i] + A[j] を計算
    4.  target = K - (A[i] + A[j]) として、 S に存在すればカウント

これをコードに落とすと次のようになります。
# 入力
N, K = map(int, input().split())
A = list(map(int, input().split()))

# 集合(SET)を作成
S = set(A)
count = 0
seen = set()  # 同じ組み合わせの重複カウント防止用

for i in range(N):
    for j in range(i + 1, N):
        two_sum = A[i] + A[j]
        target = K - two_sum

        # 3つの値がすべて異なることを確認
        if target in S and target != A[i] and target != A[j]:
            # 組み合わせを小さい順にソートして一意化
            triple = tuple(sorted((A[i], A[j], target)))
            if triple not in seen:
                seen.add(triple)
                count += 1

print(count)






問題3 : 最短経路を求める迷路探索(上級)


H×W のマップが与えられ . は通行可
S から G までの最短距離を求めてください
到達できない場合は -1 を出力

入力例
4 5
S....
.###.
..#G.
.....

出力例
7


------------------------------------------


解説

BFS(幅優先探索 : Breadth First Search)で
最短距離を求めていきます。
(開始地点に近いノードから順に「横に広がるように」探していく手法)

入力は一旦省略し、直接データ化したコードです。
from collections import deque

data = """\
4 5
S....
.###.
..#G.
.....
"""

lines = data.strip().splitlines()
H, W = map(int, lines[0].split())
grid = [list(line) for line in lines[1:]]

# スタート(S)とゴール(G)を探して座標を求める
for i in range(H):
    for j in range(W):
        if grid[i][j] == "S":
            sx, sy = i, j
        if grid[i][j] == "G":
            gx, gy = i, j

# 4方向の移動ベクトルと距離配列の初期化
# dx,dyは上下左右の移動を表す、dist は各セルへの最短距離(始点からの距離)
# 訪問したら dist[nx][ny] = dist[x][y] + 1 と更新
dx = [1, -1, 0, 0]
dy = [0, 0, 1, -1]
dist = [[-1]*W for _ in range(H)]

# キュー初期化と始点の距離設定
# 始点をキューに入れて距離 0 をセット
# 以降はキューから取り出すごとに、そのセルから隣接を探索
q = deque()
q.append((sx, sy))
dist[sx][sy] = 0

# BFS のメインループ
# q.popleft() でキューの先頭から取り出し(FIFO)
# 4方向それぞれについて:
#	•	範囲チェック(境界外は無視)
#	•	壁 (#) なら通れないので無視
#	•	dist[nx][ny] == -1 は未訪問の確認(訪問済みならスキップ)
# 未訪問かつ通行可なら距離を +1 してキューに追加
while q:
    x, y = q.popleft()
    for k in range(4):
        nx, ny = x + dx[k], y + dy[k]
        if 0 <= nx < H and 0 <= ny < W:
            if grid[nx][ny] != "#" and dist[nx][ny] == -1:
                dist[nx][ny] = dist[x][y] + 1
                q.append((nx, ny))

# G に到達していれば dist[gx][gy] は非負の最短距離
#	到達していなければ -1(初期値)のまま出力される
print(dist[gx][gy])




まとめ

最適なアルゴリズムが必要な業務には
競技プログラミングの知識が役にたちます。

すごく時間の掛かる処理の高速化、省力化、消費電力低下など
実務面で有効な場面はかなり多いです。

若い人は就職面でも有利になるので、
競技プログラミングに取り組むのかかなりオススメです。


今回は簡易的なパチスロゲームを作って

自分の反応速度の限界にチャレンジしてみました。


解説動画はこちら

 

簡単パチスロアプリ

今回はStreamlitを用いて
簡単なパチスロアプリを作っていきます。

ローカルPCにstreamlitをインストールしたら
遊ぶことができると思います。
pip install streamlit 

必要なファイルは
main.py
game.py
の2つです。

コピペしてファイル作成して下さい。


main.py
from game import run_app

if __name__ == '__main__':
    run_app()




game.py
import streamlit as st
import random
import time
from typing import List, Tuple

# 絵柄の辞書と配当
SYMBOL_COUNTS = {
    'C': 6,
    'L': 5,
    'P': 3,
    'O': 3,
    'S': 2,
    'BAR': 1,
    '7': 1,
}

PAYOUT = {
    'C': 5,
    'L': 10,
    'P': 15,
    'O': 20,
    'S': 25,
    'BAR': 30,
    '7': 50,
}

# リールの数は21枚になること
TOTAL_PER_REEL = 21

def make_reel() -> List[str]:
    items = []
    for k, v in SYMBOL_COUNTS.items():
        items += [k] * v
    if len(items) != TOTAL_PER_REEL:
        raise ValueError(f"reel composition must be {TOTAL_PER_REEL}, got {len(items)}")
    random.shuffle(items)
    return items

def evaluate_result(s1: str, s2: str, s3: str) -> Tuple[int, str]:
    if s1 == s2 == s3:
        symbol = s1
        payout = PAYOUT.get(symbol, 0)
        return payout, f"{symbol} : + {payout} 枚"
    else:
        return 0, "揃わず"

def run_app():
    st.title('簡易パチスロ')

    # 初期化
    if 'medals' not in st.session_state:
        st.session_state['medals'] = 100
    if 'speed' not in st.session_state:
        st.session_state['speed'] = 0.1  # 秒
    if 'spinning' not in st.session_state:
        st.session_state['spinning'] = False
    # リール本体
    if 'reels' not in st.session_state:
        st.session_state['reels'] = [make_reel(), make_reel(), make_reel()]
    if 'indices' not in st.session_state:
        st.session_state['indices'] = [0, 0, 0]
    if 'stop_flags' not in st.session_state:
        st.session_state['stop_flags'] = [False, False, False]
    if 'last_message' not in st.session_state:
        st.session_state['last_message'] = ''

    # UI レイアウト
    col_top = st.columns([1, 1, 1])
    placeholders = [col_top[i].empty() for i in range(3)]
    for i in range(3):
        idx = st.session_state['indices'][i]
        symbol = st.session_state['reels'][i][idx]
        placeholders[i].markdown(f"### {symbol}")

    # 停止ボタン
    col_buttons = st.columns([1, 1, 1])
    if col_buttons[0].button('停止1'):
        st.session_state['stop_flags'][0] = True
    if col_buttons[1].button('停止2'):
        st.session_state['stop_flags'][1] = True
    if col_buttons[2].button('停止3'):
        st.session_state['stop_flags'][2] = True

    # メダル表示
    st.write(f"**メダル:** {st.session_state['medals']} 枚")

    # 停止スピード
    start_col, speed_col = st.columns([1, 1])
    if speed_col.slider('回転速度 (秒)', min_value=0.01, max_value=1.0, value=st.session_state['speed'], step=0.01, key='speed_input'):
        st.session_state['speed'] = st.session_state.pop('speed_input')

    # 開始ボタン
    if start_col.button('開始') and not st.session_state['spinning']:
        if st.session_state['medals'] < 5:
            st.warning('メダルが不足しています。')
        else:
            # 消費
            st.session_state['medals'] -= 5
            # 新しいリールを作る
            st.session_state['reels'] = [make_reel(), make_reel(), make_reel()]
            # indices をランダムに初期化
            st.session_state['indices'] = [random.randrange(TOTAL_PER_REEL) for _ in range(3)]
            st.session_state['stop_flags'] = [False, False, False]
            st.session_state['spinning'] = True
            st.session_state['last_message'] = ''
            st.rerun() 

    # 回転ループ
    if st.session_state['spinning']:
        while True:
            # まず停止していないリールのインデックスを進める
            for i in range(3):
                if not st.session_state['stop_flags'][i]:
                    st.session_state['indices'][i] = (
                        st.session_state['indices'][i] + 1
                    ) % TOTAL_PER_REEL

            # 表示更新
            for i in range(3):
                idx = st.session_state['indices'][i]
                symbol = st.session_state['reels'][i][idx]
                placeholders[i].markdown(f"### {symbol}")

            # 停止判定
            if all(st.session_state['stop_flags']):
                s1 = st.session_state['reels'][0][st.session_state['indices'][0]]
                s2 = st.session_state['reels'][1][st.session_state['indices'][1]]
                s3 = st.session_state['reels'][2][st.session_state['indices'][2]]
                payout, msg = evaluate_result(s1, s2, s3)
                if payout > 0:
                    st.session_state['medals'] += payout
                st.session_state['last_message'] = msg
                st.session_state['spinning'] = False
                st.rerun()
                break

            # 待機 → 再実行
            time.sleep(st.session_state['speed'])
            st.rerun()

    # 結果表示
    if st.session_state['last_message']:
        st.info(st.session_state['last_message'])




実行方法は main.py のある場所で
streamlit run main.py
です。


実行するとこんな画面になります。

スクリーンショット 2025-09-27 15.57.54

開始ボタンクリックでゲーム開始
停止ボタンをクリックしてリールを止め
3つ絵柄が揃うとコイン獲得です。

回転速度を変えると、変化のスピードを変えられます。

自分は1秒でないと絵柄を揃えられなかったので
だいぶ反応速度が落ちていますね

どこまで行けるか、チャレンジしてみて下さい。

それでは。





ルーレットなどのギャンブル
元手が倍になる確率はいくらになるでしょうか?


解説動画はこちら




問題

カジノのルーレットで元手が倍になる確率はどれくらいでしょうか?

ルール

100ドルを元手に 1ドル 単位で賭け続け
200ドル に到達するか0になったら終了

数字は1-36まであり、赤か黒の色が設定されている
数字0(緑)が1個、ここに当たるとディーラーが勝ち、賭けは没収

赤か黒に賭けると、当たったら2倍の払い戻し

このルールだと、元手が2倍になる確率はどれくらいでしょうか?




回答

ルーレットの元手が倍になる確率は
ギャンブラーの破産問題の公式に当てはめると解けるようです。

スタート資金を i、目標を N、負けたら破産(0)
勝率 p、負け率 q=1-p のとき
目標に到達する確率は以下で表せます:
スクリーンショット 2025-09-20 17.00.23

この式に代入していくと

•    i = 100 
•   N = 200 
•   p = 18/37  = 0.48648649
•   q = 19/37  = 0.51351351

こんな感じになり
計算式に当てはめると

スクリーンショット 2025-09-20 17.01.19
となります。

最終的な確率を求める部分を
Pythonのコードでやってみましょう。
p =  (1 - (19/18) ** 100) / ( 1 - (19/18) ** 200 )
print(p)
print(f"{p*100:.04}%")
0.004466284539492003
0.4466%


大体、0.45%くらい
1000回挑戦したら、4-5回でしょうか。




ルーレットのシミュレーションコード

これを実際にシミュレーションしてみます。

コードを簡略化するために
赤と黒の代わりに奇数なら当たり
偶数ならハズレにしてやってみます。
import random

def simulate_game(start=100, goal=200, trials=10):
    bankrupt_count, double_count = 0, 0
    for _ in range(trials):
        money = start
        while 0 < money < goal:
            # ルーレットの出目を 0-36 からランダムに選ぶ
            spin = random.randint(0, 36)
            if spin % 2 == 0:  # 偶数 → 負け
                money -= 1
            else:  # 奇数 → 勝ち
                money += 1
        if money == 0:
            bankrupt_count += 1
        elif money == goal:
            double_count += 1

    print(f"シミュレーション回数: {trials}")
    print(f"破産した回数: {bankrupt_count}")
    print(f"200ドルに到達した回数: {double_count}")
    print(f"破産確率: {bankrupt_count/1000:.3f}")
    print(f"成功確率: {double_count/1000:.3f}")

simulate_game(trials=1000)
シミュレーション回数: 1000
破産した回数: 997
200ドルに到達した回数: 3
破産確率: 0.997
成功確率: 0.003


おおよそ、数式の数値を近くはなりますね。

1000人いたら
4-5人は元手を増やしてカジノを出ることができる
そんな確率ですかね。



ルーレット以外の破産確率

1.ブラックジャック(基本戦略でプレイ)
2.パチンコ(一般的な遊技機)
3.競馬(単勝を繰り返す単純モデル)

この破産確率も見てみます。

ここでは計算を簡略化するために
1ゲームあたりの勝率を使って近似確率を求めていきます。

1ゲームあたりの勝率は以下のように設定することとします。
•   ブラックジャック(基本戦略+カジノ側有利)
 → プレイヤー勝率 ≈ 0.49、負け ≈ 0.51

•   パチンコ(通常 0.80〜0.90 程度の還元率)
 → 実質勝率 ≈ 0.45、負け ≈ 0.55

•   競馬(通常 0.70〜0.80 程度の還元率)
 → 実質勝率 ≈ 0.40、負け ≈ 0.50 と仮定


ルーレットにこの3つも加えてシミュレーションするコード
from decimal import Decimal, getcontext
getcontext().prec = 50  

def ruin_probability(s, N, p):
    """
    ギャンブラーの破産確率
    s: 初期資金
    N: 目標資金
    p: 勝率
    """
    q = Decimal(1) - p
    if p == q:
        return Decimal(1) - Decimal(s) / Decimal(N)
    else:
        ratio = q / p
        num = ratio**Decimal(s) - ratio**Decimal(N)
        den = Decimal(1) - ratio**Decimal(N)
        return num / den

# パラメータ
s = Decimal(100)   # 初期資金
N = Decimal(200)   # 目標資金

games = {
    "ブラックジャック": Decimal("0.49"),
    "ルーレット" : Decimal("0.48648649"),
    "パチンコ": Decimal("0.45"),
    "競馬": Decimal("0.40")
}

for game, p in games.items():
    ruin = ruin_probability(s, N, p)
    success = Decimal(1) - ruin
    print(f"{game}:")
    print(f"  破産確率  = {ruin:.20}")
    print(f"  倍化確率  = {success:.20}")
    print()
ブラックジャック:
  破産確率  = 0.98202320998693248326
  倍化確率  = 0.017976790013067516735

ルーレット:
  破産確率  = 0.99553370920702988362
  倍化確率  = 0.0044662907929701163797

パチンコ:
  破産確率  = 0.99999999807255307809
  倍化確率  = 1.9274469219075612712E-9

競馬:
  破産確率  = 0.99999999999999999754
  倍化確率  = 2.4596544265798292632E-18


ルーレットは先ほどと同じなので他を見ると
ブラックジャックは意外と勝てそうですね

競馬とパチンコはもう
勝てる見込みがほぼ無いです。


競馬だと
単勝を同じ金額ずっと買い続けるみたいなものが
この確率に近くなります。

単純な買い方では
競馬で元手を増やすことは出来ないでしょうね。



まとめ

1回あたりのおよその勝率から
破産確率は導き出すことができます。

基本的にギャンブルは元手を倍にできる確率は非常に少ないので
(ブラックジャックだけは、勝てる見込みが0ではない)
同じ100万円を注ぎ込むなら、まだ投資に回したほうがいいでしょう。

ギャンブルは卒業しよう!!!

ギャンブルでお金を減らす人が
一人でもいなくなりますように

それでは。


このページのトップヘ