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

ギャンブル

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


解説動画はこちら

 




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



・問題
コインを投げて、表が出たら賭け金が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になることはほぼ無いです。

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

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

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


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

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


解説動画はこちら




問題

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

ルール

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万円を注ぎ込むなら、まだ投資に回したほうがいいでしょう。

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

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

それでは。


今回はブラックジャックの
シミュレーションをしてみました。


解説動画はこちら




今回は
ブラックジャックのシミュレーターを作って
遊んでみることとしました。

開発するブラックジャックのルールなどを
決めておきましょう。

こんなルールのプログラムを作ることとします。

・初期カードは52枚を4セット
・配ったら捨て残り10枚以下になったらカード交換
・プレイヤーとディーラーの2人対戦
・実行開始時、プレイヤーディーラーそれぞれカードを2枚引く
・先にプレイヤーがカードを引きディーラーが次に引く
・ディーラーは自分の手札が17以上になるまで引き続ける
・プレイヤーは18以上なら勝負、21を超えていたらバースト
・21に近い方の勝ちで掛け金の倍額をもらえ引き分けは戻し
・JとQとKは10として扱う
・ダブルダウンなし、スピリットなし、サレンダーなし
・その他特殊なルールなし
 

さて簡単なプログラムを組んでみることとします。

カードはスペード、ハート、クラブ、ダイヤの4種類で
1-13まであります。
それを4セット使うこととします。

最初は点数計算です
1は11とも計算できるルールがあるので
21を超えない場合は11として数え
21を超えても1とするようにしました。

# ライブラリ
import random

# 点数計算
def score_calc(hands):
    score = 0
    flg = False
    for hand in hands:
        s = int(hand.split(':')[1])
        if s>=10:
            score += 10
        elif s==1:
            score+=1
            flg=True
        else:
            score += s
    if score>21 and flg:
        score = 0
        for hand in hands:
            s = int(hand.split(':')[1])
            if s>=10:
                score += 10
            elif s==1:
                score+=1
            else:
                score += s
    return score

次に1ゲームの実行を行うことを考えます。

最初に2枚ずつ配り
そこからは1枚ずつ配ります。

そこまで複雑なものでも無く
プレイヤーは18以上
ディーラーは17以上になるまで
カードを引き続けます。

カードが揃ったら勝負に入り
バーストせず21に近い方が勝ちです。

# ゲーム
def game1(game_card):
    # 初期配
    player =  [game_card.pop(0)]
    player +=  [game_card.pop(0)]
    dealer =  [game_card.pop(0)]
    dealer +=  [game_card.pop(0)]
    p_flg , d_flg = False,False
    # ディール
    while True:
        # プレイヤーは18以上になるまで引く
        if score_calc(player)>=18:
            p_flg = True
        # ディーラーは17以上になるまで引く
        if score_calc(dealer)>=17:
            d_flg = True
        if not p_flg:
            player +=  [game_card.pop(0)]
        if not d_flg:
            dealer +=  [game_card.pop(0)]
        if p_flg and d_flg:
            break
    # 勝負
    p_num = score_calc(player)
    d_num = score_calc(dealer)
    win = 'draw'
    if p_num > 21 and d_num > 21:
        win = 'draw'
    elif p_num > 21 and d_num <=21:
        win = 'dealer'
    elif p_num <= 21 and d_num >21:
        win = 'player'
    elif p_num < d_num:
        win = 'dealer'
    elif p_num > d_num:
        win = 'player'
    result = {'player':p_num,'dealer':d_num,'win':win}
    return result

これで対戦の準備が出来ました。
カードを作って対戦してみることとしましょう。

100回ほど対戦し
途中カードが尽きそうになったら
カードを入れ替えます。

# カードのセット数
sets = 4
#スペード、ハート、クラブ、ダイヤ * 13枚
card = [c+':'+str(n)  for c in ['S','H','C','D'] for n in range(1,14)]
# カード作成
cards = card * sets
# シャッフル
random.shuffle(cards)

# 対戦
results = []
for i in range(100):
    # 1ゲームの結果
    tmp = game1(cards)
    results.append(tmp)
    
    # 10枚以下になったらカードを交換
    if len(cards)<=10:
        cards = card * sets
        random.shuffle(cards)

最終的に結果がどうなるのかも
集計してみることとします。

# 対戦結果のサマリー
calc = {'player': 0, 'dealer': 0}
for row in results:
    if row['win'] == 'player':
        calc['player'] +=1
    if row['win'] == 'dealer':
        calc['dealer'] +=1
print(calc)
{'player': 30, 'dealer': 42}

こんな感じでプレイヤーとディーラーの
勝ちを集計してみました。

これだけだとあまり面白くは無いですよね。
やはり醍醐味は「金を掛ける」
ということだと思いますんで
金を掛けるシミュレーションもしてみましょう。

資金は10万円
掛け金は1000円
勝ったら倍もらえるようにしました。

fund = 100000
latch = 1000

# カードのセット数
sets = 4
#スペード、ハート、クラブ、ダイヤ * 13枚
card = [c+':'+str(n)  for c in ['S','H','C','D'] for n in range(1,14)]
# カード作成
cards = card * sets
# シャッフル
random.shuffle(cards)

# 対戦
results = []
for i in range(100):
    # 1ゲームごとにかける
    fund -= latch
    tmp = game1(cards)
    win = tmp['win']
    results.append(tmp)

    # 勝ったら倍額貰える
    if win=='player':
        fund += latch * 2
    # 引き分けは戻し
    elif win=='draw':
        fund += latch

    # 10枚以下になったらカードを交換
    if len(cards)<=10:
        cards = card * sets
        random.shuffle(cards)
さて結果はどうなるんでしょうか!!!

# 対戦結果のサマリー
calc = {'player': 0, 'dealer': 0}
for row in results:
    if row['win'] == 'player':
        calc['player'] +=1
    if row['win'] == 'dealer':
        calc['dealer'] +=1
print(calc)
print('資金残り : ' , fund)
{'player': 41, 'dealer': 31}
資金残り :  110000

今回は勝ちが多くて儲かっていますね!!


ここからは掛け方を変えて
試してみることとしましょう。

最初はグッドマン法です。
これは、勝ちに応じて賭け金を「1、2、3、5」と
4段階で増やし負けた時点で1に賭け金を戻す手法です。

プログラムをちょっと改良して
掛け金を変えてみます。

fund = 100000
latch = 1000

# カードのセット数
sets = 4
#スペード、ハート、クラブ、ダイヤ * 13枚
card = [c+':'+str(n)  for c in ['S','H','C','D'] for n in range(1,14)]
# カード作成
cards = card * sets
# シャッフル
random.shuffle(cards)

# 対戦
results = []
for i in range(100):
    # 1ゲームごとにかける
    fund -= latch
    tmp = game1(cards)
    tmp['latch'] = latch
    win = tmp['win']

    # 勝ったら倍額貰える
    if win=='player':
        fund += latch * 2
        if latch < 3000:
            latch += 1000
        elif latch ==3000:
            latch += 2000
    # 引き分けは戻し
    elif win=='draw':
        fund += latch
    else:
        latch = 1000
    tmp['fund'] = fund
    results.append(tmp)
    # 10枚以下になったらカードを交換
    if len(cards)<=10:
        cards = card * sets
        random.shuffle(cards)

さて、結果は??
# 対戦結果のサマリー
calc = {'player': 0, 'dealer': 0}
for row in results:
    if row['win'] == 'player':
        calc['player'] +=1
    if row['win'] == 'dealer':
        calc['dealer'] +=1
print(calc)
print('資金残り : ' , fund)
{'player': 34, 'dealer': 35}
資金残り : 102000

負けが少し多くても
資金が初期金額を上回っていますね。

うまく掛け金が多い時に勝てれば
初期金額を上回れますが
そううまくは行きませんね。


次は10%投資法です。
これは掛け金を毎回、資金の10%で掛けてみる
というものです。

掛け金を資金の10分の1にするように
変えてみることとします。

fund = 100000

# カードのセット数
sets = 4
#スペード、ハート、クラブ、ダイヤ * 13枚
card = [c+':'+str(n)  for c in ['S','H','C','D'] for n in range(1,14)]
# カード作成
cards = card * sets
# シャッフル
random.shuffle(cards)

# 対戦
results = []
for i in range(100):
    # 1ゲームごとにかける , 資金の10%
    latch = fund//10
    fund -= latch
    tmp = game1(cards)
    tmp['latch'] = latch
    win = tmp['win']

    # 勝ったら倍額貰える
    if win=='player':
        fund += latch * 2
    # 引き分けは戻し
    elif win=='draw':
        fund += latch
    tmp['fund'] = fund
    results.append(tmp)
    # 10枚以下になったらカードを交換
    if len(cards)<=10:
        cards = card * sets
        random.shuffle(cards)
        
# 対戦結果のサマリー
calc = {'player': 0, 'dealer': 0}
for row in results:
    if row['win'] == 'player':
        calc['player'] +=1
    if row['win'] == 'dealer':
        calc['dealer'] +=1
print(calc)
print('資金残り : ' , fund)
{'player': 46, 'dealer': 35}
資金残り :  200700

勝ちが多ければ、そりゃあ増えますが
負けが混むと目も当てられません。

長く続けることは出来ますが
アップダウンが激しすぎますね。

最後は31システム投資法です。

1,1,1,2,2,4,4,8,8 に分けて賭ける手法で
2連勝すると初期金額を上回り
全部負けてもこの分しか負けません。

fund = 100000

# カードのセット数
sets = 4
#スペード、ハート、クラブ、ダイヤ * 13枚
card = [c+':'+str(n)  for c in ['S','H','C','D'] for n in range(1,14)]
# カード作成
cards = card * sets
# シャッフル
random.shuffle(cards)

# 対戦
results = []
for i in [1,1,1,2,2,4,4,8,8]:
    # 1ゲームごとにかける額は31の金額で
    latch = 1000 * i
    fund -= latch
    tmp = game1(cards)
    tmp['latch'] = latch
    win = tmp['win']

    # 勝ったら倍額貰える
    if win=='player':
        fund += latch * 2
    # 引き分けは戻し
    elif win=='draw':
        fund += latch
    tmp['fund'] = fund
    results.append(tmp)
    # 10枚以下になったらカードを交換
    if len(cards)<=10:
        cards = card * sets
        random.shuffle(cards)

# 対戦結果のサマリー
calc = {'player': 0, 'dealer': 0}
for row in results:
    if row['win'] == 'player':
        calc['player'] +=1
    if row['win'] == 'dealer':
        calc['dealer'] +=1
print(calc)
print('資金残り : ' , fund)
{'player': 5, 'dealer': 2}
資金残り : 103000

何度か試してみましたが
資金の推移としては連勝さえできれば
かなりの確率で初期金額を上回れます。

[{'player': 18, 'dealer': 25, 'win': 'player', 'latch': 1000, 'fund': 101000},
 {'player': 19, 'dealer': 22, 'win': 'player', 'latch': 1000, 'fund': 102000},
 {'player': 21, 'dealer': 19, 'win': 'player', 'latch': 1000, 'fund': 103000},
 {'player': 18, 'dealer': 17, 'win': 'player', 'latch': 2000, 'fund': 105000},
 {'player': 19, 'dealer': 20, 'win': 'dealer', 'latch': 2000, 'fund': 103000},
 {'player': 21, 'dealer': 22, 'win': 'player', 'latch': 4000, 'fund': 107000},
 {'player': 18, 'dealer': 19, 'win': 'dealer', 'latch': 4000, 'fund': 103000},
 {'player': 21, 'dealer': 21, 'win': 'draw', 'latch': 8000, 'fund': 103000},
 {'player': 22, 'dealer': 22, 'win': 'draw', 'latch': 8000, 'fund': 103000}]

推移はこんな感じですが
2連勝したらそこで打ち切る
というルールでやれば、回収金額の方が
多くなるかもしれません。

そうは上手くは行かないんですけどね・・・

この掛け方はいわゆる
マーチンゲール法というものの派生で
必ず勝てる掛け方ではありません。


まとめですが
シミュレーションでは初期金額を上回ることもあれば
大きく下回ることもあります。

必勝出来ないのであれば
リスクも当然あるので
やるべきでは無いでしょうねーーー

次は掛け方以外にも
ハンドの関係性からのカードの引き方や
攻略法も見ていきたいと思います。

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

無料マンガアプリでカイジを見ていたら
気になって仕方なくなってしまいました。

解説動画はこちら




さてチンチロリンのルールですが
こんな内容です。

サイコロを3つ同時に振って役が揃うまで振る(最大3回まで)

強い役が勝つ

役名条件
ピンゾロ1・1・1
ゾロ目2・2・2 , 3・3・3 , 4・4・4 , 5・5・5 , 6・6・6
シゴロ4・5・6
出目の大きい順番2枚が同じ、残った1つの目
ションベン役がなかった場合,お椀からサイコロが出た場合
ヒフミ1・2・3

他にも細かなルールは有りますが
今回は割愛します。

さてまずはチンチロリンの役を判定するプログラムを
作ってみましょう。

def tintiro_hand(h):
    # ピンゾロ
    if all([h[0]==1,h[1]==1,h[2]==1]):
        return 1
    # ゾロ目
    if h[0]==h[1] and h[1]==h[2]:
        return 2
    # シゴロ
    if [4,5,6]==list(sorted(h)):
        return 3
        # ヒフミ
    if [1,2,3]==list(sorted(h)):
        return 11
    # 出目 and ションベン
    calc = {}
    for n in h:
        if n in calc:
            calc[n]+= 1
        else:
            calc[n]=1
    if 2 in calc.values():
        return 3 + 7-sorted(calc.items(),key=lambda x:x[1])[0][0]
    else:
        return 10

def judge(h1,h_2):
    if h1==h_2:
        return 'DRAW'
    if h1<h_2:
        return 'WIN'
    else:
        return 'LOSE'


チンチロリンではサイコロを3つ使います。

このサイコロの組み合わせは
itertoolsで求めることが出来ます。

まずは通常のサイコロで戦った際の勝敗を見てみましょう。
import itertools
from fractions import Fraction

hands1 = list(itertools.product([1,2,3,4,5,6],repeat=3))
hands2 = list(itertools.product([1,2,3,4,5,6],repeat=3))
wins = {}
for hand1 in hands1:
    for hand2 in hands2:
        w = judge(tintiro_hand(hand1),tintiro_hand(hand2))
        if w in wins:
            wins[w] +=1
        else:
            wins[w] = 1
total = sum(wins.values())
draw,win,lose =wins['DRAW'],wins['WIN'],wins['LOSE']
print('DRAW\t' , Fraction(draw,total) , ' \t{:%}'.format(draw/total))
print('WIN \t'    , Fraction(win ,total)  , '\t{:%}'.format(win/total))
print('LOSE\t'   , Fraction(lose,total)  , '\t{:%}'.format(lose/total))

DRAW	 1639/5832  	28.103567%
WIN 	 4193/11664 	35.948217%
LOSE	 4193/11664 	35.948217%

だいたい36%ほどで勝ったり負けたりですね。

マンガではここで特別なサイコロが登場します。
それがシゴロ賽です!!!

4,5,6しか出目のない特殊なサイコロ

地下労働施設の班長
大槻が用いたイカサマサイコロです。

これを使うと極端に役の出方が変わります。

通常のサイコロと勝敗を比べてみましょう。
import itertools
from fractions import Fraction

hands1 = list(itertools.product([1,2,3,4,5,6],repeat=3))
hands2 = list(itertools.product([4,5,6,4,5,6],repeat=3))
wins = {}
for hand1 in hands1:
    for hand2 in hands2:
        w = judge(tintiro_hand(hand1),tintiro_hand(hand2))
        if w in wins:
            wins[w] +=1
        else:
            wins[w] = 1
total = sum(wins.values())
draw,win,lose =wins['DRAW'],wins['WIN'],wins['LOSE']
print('DRAW\t' , Fraction(draw,total) , ' \t{:%}'.format(draw/total))
print('WIN \t'    , Fraction(win ,total)  , '\t{:%}'.format(win/total))
print('LOSE\t'   , Fraction(lose,total)  , '\t{:%}'.format(lose/total))

DRAW	 107/1944 	5.504115%
WIN 	 175/1944 	9.002058%
LOSE	 277/324   	85.493827%

相手側の勝率が極端に変わりますね!!!

通常時35%ほどだった負け確率が85%と
50%ほど負ける確率が増加します。

実際に役が出る確率はどういう確率でしょうか?

お互いのサイコロの出目でみてみましょう。

サイコロの出目の組み合わせは
お互い 6**3 で216通りあります。

通常のサイコロを振ってみると
import itertools
import matplotlib.pyplot as plt
%matplotlib inline

hands1 = list(itertools.product([1,2,3,4,5,6],repeat=3))
hands = {}
for hand1 in hands1:
    h = tintiro_hand(hand1)
    if h in hands:
        hands[h] +=1
    else:
        hands[h] = 1

plt.figure(figsize=(12,6))
x = [k for k,v in sorted(hands.items())]
y = [v for k,v in sorted(hands.items())]
for x1,y1 in zip(x,y):
    plt.text(x1, y1+1 , y1 , size = 10, color = "green")
    plt.text(x1, y1+10 , '{:.01%}'.format(y1/216), size = 10, color = "black")
label = ['111','ゾロ目','シゴロ','6','5','4','3','2','1','ションベン','123']
plt.bar(x,y,tick_label=label)
plt.grid()
plt.show()
download-1
こんな確率ですね。
今回は3回振りなおしを考慮していないので
出目を考えるとションベンになる確率が高いですね。

これと比べてシゴロ賽はどうでしょうか?
import itertools
import matplotlib.pyplot as plt
%matplotlib inline

hands = list(itertools.product([4,5,6,4,5,6],repeat=3))
hands2 = {i:0 for i in range(1,12)}
for hand in hands:
    h2 = tintiro_hand(hand)
    if h2 in hands2:
        hands2[h2] +=1
    else:
        hands2[h2] = 1

plt.figure(figsize=(12,6))
x = [k for k,v in sorted(hands2.items())]
y = [v for k,v in sorted(hands2.items())]
for x1,y1 in zip(x,y):
    plt.text(x1, y1+1 , y1 , size = 10, color = "green")
    plt.text(x1, y1+10 , '{:.01%}'.format(y1/216), size = 10, color = "black")
label = ['111','ゾロ目','シゴロ','6','5','4','3','2','1','ションベン','123']
plt.bar(x,y,tick_label=label)
plt.grid()
plt.show()
download-2

強い手の確率が極端に上がります。

見比べてみると瞭然ですね。
スクリーンショット 2020-05-23 17.10.35

マンガではどうやってこのイカサマを見つけたかというと
出目をメモってる三好がいて
その出目の歪さからイカサマサイコロに気づいた・・・
という感じでした。

やはり、データの整備とデータから発見できるかという
まさにデータサイエンス的な要素をもつ
このチンチロリン編は

賭博破戒録カイジの第3章に出てきます。

みていない方はぜひご賞味ください。

最後は倍、いや100倍返しでwww

とまあ、456サイコロは売ってるみたいなので
これを使うと圧倒的に勝てますねーー

やはり、統計って大事ですよねー
今回はこれまでです
それでは



このページのトップヘ