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

シミュレーション

今回は、犬の名前ランキングの
TOP10の名前の犬に
出会えるまで帰れないロケの
検証シミュレーションです。


解説動画はこちら


 
動画作ってた時は思い出せなかったですが
「スクール革命」という番組で
芸人の「ちゃんぴょんず」が
このロケをしてました。

犬の名前ランキング
トップ10の犬に全部出会うというロケです。

街を歩く犬を散歩している人に声をかけて
何匹出会ったら、全部出会えるのか
プログラムで検証してみましょう。


犬の名前ランキング

まずは犬の名前ランキングです

毎年アンケート調査されている様で
そこのデータを引用します。

import pandas as pd
import numpy as np

df = pd.read_html("https://www.anicom-sompo.co.jp/news-release/2023/20231019/")[0]
df

順位 名前 頭数
0 1位(1) ムギ 1264
1 2位(2) ココ 1091
2 3位(4) ソラ 812
3 4位(3) モカ 786
4 5位(5) マロン 732
5 6位(6) レオ 714
6 7位(8) モコ 676
7 8位(13) ラテ 642
8 9位(7) モモ 641
9 10位(12) チョコ 631


犬の名前ランキングは
こんな感じになっている様です。


シミュレーション

犬は全体で何頭いるか不明なので
TOP10が全体の20%だと仮定します。

まずはこのTOP10をデータ化します。
top_data = []
for i, row in df[["名前","頭数"]].iterrows():
    name = row["名前"]
    num = row["頭数"]
    top_data += [name] * num

print(len(top_data))
7989

残りの80%はハズレですね。
top_num = df["頭数"].sum()
h_num = top_num * 4
h_data = ["ハズレ"] * h_num
print(len(h_data))
31956

data = top_data + h_data
print(len(data))
39945


全体で4万頭ほどのデータになります。

ランダムでシャッフルしてあげると
こんな感じのデータになります。
tmp = data.copy()
np.random.shuffle(tmp)
print(tmp[0:10])
['ハズレ', 'ハズレ', 'ムギ', 'モモ', 'ハズレ', 'ハズレ', 'ムギ', 'ハズレ', 'ハズレ', 'ハズレ']


あとはTOP10の名前に
全部出会えるまで会うだけです。

シミュレーションするプログラムは
こんな感じです。
res = {n:0 for n in df["名前"].values}
count = 0
for name in tmp:
    count+=1
    if "ハズレ"!=name:
        res[name]+=1
        # TOP10全部会ってるか判定
        if all([t>0 for t in res.values()]):
            break
print(count)
210

あとは1回の試行では結果がばらつくので
1万回ほど試行してあげれば
どれくらいの回数で見つかるのかが
分かるはずです。

# 回数チェック関数
def check_count(tmp):
    res = {n:0 for n in df["名前"].values}
    count = 0
    for name in tmp:
        count+=1
        if "ハズレ"!=name:
            res[name]+=1
            # TOP10全部会ってるか判定
            if all([t>0 for t in res.values()]):
                break
    return count

calc_data = []
for i in range(10000):
    tmp = data.copy()
    np.random.shuffle(tmp)
    c = check_count(tmp)
    calc_data.append(c)

calc_df = pd.DataFrame(calc_data,columns=["count"])
calc_df.hist(bins=100)
output_23_1


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

大体平均160回くらいで
出会いきれる事になりそうです。

運が良いと数十頭
運が悪いと数百頭

そんな結果でした。

このように、テレビのロケであっても
おおよそのシミュレーションが出来るので

事前にシミュレーションしておくと
諸々の調整がスムーズに行く気がします。

プログラミングでの効率化が
オススメです!!!!

今回はテレビ番組の
ロケをシミュレーションしてみました。

それでは。

今回は神経衰弱を一人で行う
シミュレーションプログラムを考えてみました


解説動画はこちら



神経衰弱

今回は神経衰弱です

トランプで行うゲームのひとつで
カードを伏せた状態で混ぜ
プレイヤーは好きな2枚を
その場で表に向けます

2枚が同じ数字であれば
それらを得ることができ
もう一度プレイできます

2枚が異なる数の場合
カードを元通りに伏せて
次のプレイヤーの順番に

すべてのカードが取られるまで行い
取ったカードの枚数が多い
プレイヤーの勝ちになります


今回は友達がいないので
一人で神経衰弱をしてみる事を考えてみます

何ターンあれば全部空けられるのか?
仮想のカードを作って
シミュレーションしてみましょう


考え方

最初はデッキを用意して
52枚をシャッフルします
# 52枚のカードのデッキ
deck = [i for i in range(1,14)] * 4

import numpy as np

# シャッフルする
np.random.shuffle(deck)
print(deck)
[1, 11, 7, 6, 11, ・・・]

こんな感じで52枚分のデータを用意します


ターン時の行動を考えてみましょう

こんな感じでしょうか?

カードを1枚開ける
  知ってる数字があればそれを開ける
カードをもう1枚開ける
  同じ数字なら取る 次の行動へ
  違う数字ならターンプラス1
2枚ペアが判明していたらそれを開ける

ここら辺の行動を関数にまとめて
ゲームできる様にしてみました


ゲーム関数

import numpy as np
import collections

# under : カードを覚えておくための変数

# 2個以上分かったカードがあるかどうか
def check_2(under):
    c = collections.Counter(under.values())    
    for i in range(1,14):
        if c[i]>=2:
            return i
    return 0

# 2個のペアのインデックスを返す
def get_2pair(under , num):
    pair = [k for  k , v in under.items() if v == num]
    return pair

# ペアカードを取る
def get_card(under , pair):
    for i in pair:
        under[i] = 99
    return under

# 開けていないカードを1枚選択する
def open_1card(under):
    # 候補 0:未開、1-13:開封済、99:取得済
    cand = [k for k,v in under.items() if v==0]
    return np.random.choice(cand)

# 数字が判明しているかをチェック
def under_check(under,num,select):
    # 候補 0:未開、1-13:開封済、99:取得済
    cand = [k for k,v in under.items() if v==num and k!=select]
    if len(cand)>0:
        return True
    else:
        return False
    
# 数字が判明しているものを選択する
def open_under_card(under,num,select):
    # 候補 0:未開、1-13:開封済、99:取得済
    cand = [k for k,v in under.items() if v==num and k!=select]
    return np.random.choice(cand)

# ログのプリント
def logs(turn , gets , sheets , num):
    text = 'ターン : {0:02} , 取得済み : {1:02} , {2}枚目 , 数字:{3:02}'.format(turn , gets , sheets , num)
    print(text)
# 神経衰弱ゲームをシミュレーションする
def game():
    
    # デッキを作ってシャッフルする
    deck = [i for i in range(1,14)] * 4
    np.random.shuffle(deck)
    
    # 回数と開けたカードの記憶
    turn , gets , under = 1, 0 , {i:0 for i in range(52)}
    
    # ゲーム開始
    for j in range(100):
        # 上がりの判定
        if all([i==99 for i in under.values()]):
            print('上がり , turn : {0:02}'.format(turn))
            break
        
        #2枚開いたのがあればそれを消す
        pair_num = check_2(under)
        if pair_num > 0:
            print('判明済み有り : {0:2}'.format(pair_num))
            # ペアを開きに変える
            pair = get_2pair(under , pair_num)
            under = get_card(under , pair)
            gets +=1
            print('get card : {0:02},{1:02} , 数字 : {2:02}'.format(pair[0] , pair[1] , pair_num))

        else:
            #1枚めくる
            select_one = open_1card(under)
            # 開けたカードの数字
            num_one = deck[select_one]
            # 数字を覚えておく
            under[select_one] = num_one
            logs(turn , gets , 1 , num_one)
            
            #数字が分かっているものがあればそれを取る
            if under_check(under,num_one,select_one):
                target = open_under_card(under , num_one , select_one)
                under[select_one] , under[target] = 99 , 99
                print('判明済み有り : {0:2}'.format(num_one))
                logs(turn , gets , 2 , num_one)
                gets +=1
                print('get card : {0:02},{1:02} , 数字 : {2:02}'.format(select_one , target , num_one))

            else:
                #分かってない場合はもう一枚めくる
                select_2nd = open_1card(under)
                num_2nd = deck[select_2nd]
                # 数字を覚えておく
                under[select_2nd] = num_2nd               
                logs(turn , gets , 2 , num_2nd)
   
                #同じ数字ならカードを取る
                if num_one==num_2nd:
                    under[select_one] , under[select_2nd] = 99 , 99
                    gets +=1
                    print('get card : {0:02},{1:02} , 数字 : {2:02}'.format(select_one,select_2nd,num_one))
                else:
                    #違う数字ならターンプラス1
                    turn +=1
                    print('ターン終了')
                    
    return turn
    
game()

ターン : 01 , 取得済み : 00 , 1枚目 , 数字:04
ターン : 01 , 取得済み : 00 , 2枚目 , 数字:02
ターン終了
ターン : 02 , 取得済み : 00 , 1枚目 , 数字:07
ターン : 02 , 取得済み : 00 , 2枚目 , 数字:05
ターン終了
ターン : 03 , 取得済み : 00 , 1枚目 , 数字:09
ターン : 03 , 取得済み : 00 , 2枚目 , 数字:02
ターン終了
判明済み有り :  2
get card : 08,51 , 数字 : 02
ターン : 04 , 取得済み : 01 , 1枚目 , 数字:10
ターン : 04 , 取得済み : 01 , 2枚目 , 数字:09
ターン終了
判明済み有り :  9
get card : 29,34 , 数字 : 09
ターン : 05 , 取得済み : 02 , 1枚目 , 数字:07
判明済み有り :  7
ターン : 05 , 取得済み : 02 , 2枚目 , 数字:07
get card : 06,22 , 数字 : 07
ターン : 05 , 取得済み : 03 , 1枚目 , 数字:13
ターン : 05 , 取得済み : 03 , 2枚目 , 数字:03
ターン終了
ターン : 06 , 取得済み : 03 , 1枚目 , 数字:08
ターン : 06 , 取得済み : 03 , 2枚目 , 数字:08
get card : 37,31 , 数字 : 08
ターン : 06 , 取得済み : 04 , 1枚目 , 数字:01
ターン : 06 , 取得済み : 04 , 2枚目 , 数字:08
ターン終了
ターン : 07 , 取得済み : 04 , 1枚目 , 数字:06
ターン : 07 , 取得済み : 04 , 2枚目 , 数字:07
ターン終了
ターン : 08 , 取得済み : 04 , 1枚目 , 数字:12
ターン : 08 , 取得済み : 04 , 2枚目 , 数字:05
ターン終了
判明済み有り :  5
get card : 09,48 , 数字 : 05
ターン : 09 , 取得済み : 05 , 1枚目 , 数字:12
判明済み有り : 12
ターン : 09 , 取得済み : 05 , 2枚目 , 数字:12
get card : 28,38 , 数字 : 12
ターン : 09 , 取得済み : 06 , 1枚目 , 数字:08
判明済み有り :  8
ターン : 09 , 取得済み : 06 , 2枚目 , 数字:08
get card : 19,46 , 数字 : 08
ターン : 09 , 取得済み : 07 , 1枚目 , 数字:03
判明済み有り :  3
ターン : 09 , 取得済み : 07 , 2枚目 , 数字:03
get card : 03,26 , 数字 : 03
ターン : 09 , 取得済み : 08 , 1枚目 , 数字:04
判明済み有り :  4
ターン : 09 , 取得済み : 08 , 2枚目 , 数字:04
get card : 30,21 , 数字 : 04
ターン : 09 , 取得済み : 09 , 1枚目 , 数字:13
判明済み有り : 13
ターン : 09 , 取得済み : 09 , 2枚目 , 数字:13
get card : 13,25 , 数字 : 13
ターン : 09 , 取得済み : 10 , 1枚目 , 数字:05
ターン : 09 , 取得済み : 10 , 2枚目 , 数字:05
get card : 17,18 , 数字 : 05
ターン : 09 , 取得済み : 11 , 1枚目 , 数字:01
判明済み有り :  1
ターン : 09 , 取得済み : 11 , 2枚目 , 数字:01
get card : 01,23 , 数字 : 01
ターン : 09 , 取得済み : 12 , 1枚目 , 数字:11
ターン : 09 , 取得済み : 12 , 2枚目 , 数字:04
ターン終了
ターン : 10 , 取得済み : 12 , 1枚目 , 数字:01
ターン : 10 , 取得済み : 12 , 2枚目 , 数字:13
ターン終了
ターン : 11 , 取得済み : 12 , 1枚目 , 数字:09
ターン : 11 , 取得済み : 12 , 2枚目 , 数字:11
ターン終了
判明済み有り : 11
get card : 11,15 , 数字 : 11
ターン : 12 , 取得済み : 13 , 1枚目 , 数字:03
ターン : 12 , 取得済み : 13 , 2枚目 , 数字:01
ターン終了
判明済み有り :  1
get card : 35,45 , 数字 : 01
ターン : 13 , 取得済み : 14 , 1枚目 , 数字:02
ターン : 13 , 取得済み : 14 , 2枚目 , 数字:10
ターン終了
判明済み有り : 10
get card : 20,24 , 数字 : 10
ターン : 14 , 取得済み : 15 , 1枚目 , 数字:06
判明済み有り :  6
ターン : 14 , 取得済み : 15 , 2枚目 , 数字:06
get card : 10,49 , 数字 : 06
ターン : 14 , 取得済み : 16 , 1枚目 , 数字:12
ターン : 14 , 取得済み : 16 , 2枚目 , 数字:11
ターン終了
ターン : 15 , 取得済み : 16 , 1枚目 , 数字:10
ターン : 15 , 取得済み : 16 , 2枚目 , 数字:03
ターン終了
判明済み有り :  3
get card : 12,32 , 数字 : 03
ターン : 16 , 取得済み : 17 , 1枚目 , 数字:04
判明済み有り :  4
ターン : 16 , 取得済み : 17 , 2枚目 , 数字:04
get card : 39,14 , 数字 : 04
ターン : 16 , 取得済み : 18 , 1枚目 , 数字:12
判明済み有り : 12
ターン : 16 , 取得済み : 18 , 2枚目 , 数字:12
get card : 40,50 , 数字 : 12
ターン : 16 , 取得済み : 19 , 1枚目 , 数字:11
判明済み有り : 11
ターン : 16 , 取得済み : 19 , 2枚目 , 数字:11
get card : 41,47 , 数字 : 11
ターン : 16 , 取得済み : 20 , 1枚目 , 数字:07
判明済み有り :  7
ターン : 16 , 取得済み : 20 , 2枚目 , 数字:07
get card : 43,05 , 数字 : 07
ターン : 16 , 取得済み : 21 , 1枚目 , 数字:06
ターン : 16 , 取得済み : 21 , 2枚目 , 数字:10
ターン終了
判明済み有り : 10
get card : 42,44 , 数字 : 10
ターン : 17 , 取得済み : 22 , 1枚目 , 数字:09
判明済み有り :  9
ターン : 17 , 取得済み : 22 , 2枚目 , 数字:09
get card : 27,16 , 数字 : 09
ターン : 17 , 取得済み : 23 , 1枚目 , 数字:13
判明済み有り : 13
ターン : 17 , 取得済み : 23 , 2枚目 , 数字:13
get card : 02,07 , 数字 : 13
ターン : 17 , 取得済み : 24 , 1枚目 , 数字:02
判明済み有り :  2
ターン : 17 , 取得済み : 24 , 2枚目 , 数字:02
get card : 00,33 , 数字 : 02
ターン : 17 , 取得済み : 25 , 1枚目 , 数字:06
判明済み有り :  6
ターン : 17 , 取得済み : 25 , 2枚目 , 数字:06
get card : 36,04 , 数字 : 06
上がり , turn : 17

こんな感じで26ペアを取れた時点で
ゲームは終了です

多分合ってるとは
思うんですけどねー

大したデバッグはしていないので
間違いがあったらごめんなさいね



1000回でシミュレーション

最後にこれを1000回繰り返して
何手で上がれたかのシミュレーションです

ログを抜いたgame2関数を用意して
1000回実行した際の
結果をまとめてみました

calc = {}
for i in range(1000):
    key = game2()
    if key in calc:
        calc[key]+=1
    else:
        calc[key] =1
for k,v in sorted(calc.items()):
    print('ターン数 : {0:02} , {1:03}回'.format(k,v))
ターン数 : 11 , 001回
ターン数 : 13 , 004回
ターン数 : 14 , 041回
ターン数 : 15 , 149回
ターン数 : 16 , 266回
ターン数 : 17 , 295回
ターン数 : 18 , 168回
ターン数 : 19 , 059回
ターン数 : 20 , 016回
ターン数 : 21 , 001回

これでみると
大体17回もあれば
上がれるみたいですねー

ほぼほぼ20回以内に
上がれているので
記憶力があれば
20ターンもかからないようです

今回は神経衰弱を
シミュレーションしてみました

それでは



今日は非常にどうでもいいネタを持ってきました。
鰻屋さんなんかにある秘伝のタレ
あれがどれくらい持つのかを
シミュレーションで検証してみました。

解説動画はこちら



さて、実際にタレを用意できませんので
プログラムでシミュレーションして行きましょう。

データをタレとして用意して
世代交代させるようにします。

10リットル = 10000ミリリットル
1ミリリットル単位で
1日1リットル世代交代させると
初代は何日残るのか?

これをシミュレーションします。

まずはタレを用意しましょう。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import warnings
warnings.simplefilter('ignore')
%matplotlib inline

# 秘伝のタレのデータフレームを用意
amount = 10000
tare = [1 for i in range(amount)]

df = pd.DataFrame(data = tare,columns=['世代'])
df.head()
世代
0 1
1 1
2 1
3 1
4 1

非常に濃厚なタレを用意しました。
データフレームに1ミリリットル単位で
第1世代として用意します。

これを1日1リットル分ランダムで
世代を入れ替えます。

cost = 1000
tmp = df.sample(n=cost)
tmp['世代']= 2
df.iloc[tmp.index] = tmp

df['世代'].plot(kind='hist')
plt.show()

pd.DataFrame(df.value_counts())
download


はい、こんな感じで1日分を入れ替えると
世代交代します。


まとめて50日分やって見ましょう。

amount = 10000
cost,days = 1000 , 50
# 秘伝のタレのデータフレームを用意
tare = [1 for i in range(amount)]
df = pd.DataFrame(data = tare,columns=['世代'])

# 1日入れ替える
for i in range(2,days):
    tmp = df.sample(n=cost)
    tmp['世代']= i+1
    df.iloc[tmp.index] = tmp

# 結果をみる
df.plot(kind='hist',bins = days)
plt.show()
downloadのコピー

第一世代はほんの少しだけ残っていますが
風前の灯火ですね。

どれくらい日数世代交代させると
第一世代が亡くなるのかを
見て見ましょう。

amount = 10000
cost,days = 1000 , 1000
result = []
for c in range(100):
    tare = [1 for i in range(amount)]
    df = pd.DataFrame(data = tare,columns=['世代'])
    for i in range(2,days):
        tmp = df.sample(n=cost)
        tmp['世代']= i+1
        df.iloc[tmp.index] = tmp

        if int(df[df['世代']==1].count())==0:
            result.append(i)
            break
            
np.mean(result)
96.55

はい出ました!!!!

およそ100日ほど世代交代させると
第1世代は消失しますね。

ということで100日も入れ替えて使っていれば
元々のタレは使われて無くなるのではないかと思います。


秘伝のタレ、100日経てば、タダのタレ

あおとが宜しいようでwww

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

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


解説動画はこちら




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

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

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

・初期カードは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連勝したらそこで打ち切る
というルールでやれば、回収金額の方が
多くなるかもしれません。

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

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


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

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

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

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

今回はビンゴ5を買い続けてみることにしました

さて結果はどうなってしまうのでしょうか?!?!

解説動画はこちら



さて今回はビンゴ5についてです。

ビンゴ5は1994年10月1日に発売が開始された
数字選択式全国自治宝くじの一つで

タテ・ヨコ3マスずつ、計9個のマス目のうち
中央のFREEのマスを除いた8個のマス目に
記載された5つの数字の中から1つずつ
01から40までの数字のうち
計8個の数字を選択するものです。

1口200円で購入可能ですね。

選択箇所が8つあるので
それぞれ選べる数字は次のようになっています

[01, 02, 03, 04, 05] [06, 07, 08, 09, 10] [11, 12, 13, 14, 15]
[16, 17, 18, 19, 20]         フリー      [21, 22, 23, 24, 25]
[26, 27, 28, 29, 30] [31, 32, 33, 34, 35] [36, 37, 38, 39, 40]


真ん中はフリーなので選べないですね

ビンゴは縦横斜めの数字があえば
ラインが一つ揃います。

最大ライン数は8つ
ライン数によって当選金額も変わります。

数字の組み合わせは 5 ** 8 == 390625通り
賞金は次のようになっています

スクリーンショット 2021-05-15 16.39.53

さて
ここからシミュレーションすることを考えましょう。

まず最初はビンゴ5の当たり目を作る所からです
import random

def bingo5hits():
    # 8か所の数字の組み合わせ
    base = [[i2 for i2 in range(i1*5+1,i1*5+6)] for i1 in range(8) ]

    # ランダムに1つ選ぶ
    bingo5 = [random.choice(b) for b in base]
    
    return bingo5

print(bingo5hits())
[1, 7, 12, 16, 23, 28, 32, 38]

これを当たりとして
自分の選択数字と比べてみて
当たりをチェックしましょう

当たり目からラインの成立をチェックする
def check_bingo(b , s):
    lines = 0
    # 縦1のライン
    if b[0]==s[0] and b[3]==s[3] and b[5]==s[5]:
        lines+=1
    # 縦2のライン
    if b[1]==s[1] and b[6]==s[6]:
        lines+=1
    # 縦2のライン
    if b[2]==s[2] and b[4]==s[4] and b[7]==s[7]:
        lines+=1
    # 横1のライン
    if b[0]==s[0] and b[1]==s[1] and b[2]==s[2]:
        lines+=1
    # 横2のライン
    if b[3]==s[3] and b[4]==s[4]:
        lines+=1
    # 横2のライン
    if b[5]==s[5] and b[6]==s[6] and b[7]==s[7]:
        lines+=1
    # 斜め1のライン
    if b[0]==s[0] and b[7]==s[7]:
        lines+=1
    # 斜め2のライン
    if b[2]==s[2] and b[5]==s[5]:
        lines+=1
    return lines

次はライン数から賞金額を計算してみましょう。
def pay_off(a):
    if a==1:
        return 200
    if a==2:
        return 700
    if a==3:
        return 2500
    if a==4:
        return 18200    
    if a==5:
        return 45000
    if a==6:
        return 300000    
    if a==8:
        return 5556200
    return 0

これで獲得賞金も問題ないですね

出目のチェックはこんな感じで出来ます。

出目の表示
def print_num(n):
    for i,b in enumerate(n,start=1):
        print(b,end='\t')
        if i==4:
            print(' - ',end='\t')
        if i==3 or i==5:
            print()
            
num = [i*5+1 for i in range(8)]
print_num(num)

ここからはシミュレーション実行です
早速100枚買った結果を実施してみましょう。

100枚買ってみる
import itertools

pay,p_off,count,result = 0,0,0,[]

# 選択数字の組み合わせ
b = [[i2 for i2 in range(i1*5+1,i1*5+6)] for i1 in range(8) ]

# 出目の組み合わせ
p = list(itertools.product(b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7]))

# 当たりはコレ
bingo5 = bingo5hits()

# 100枚の選択
selects = random.choices(p, k=100)

# 当たりの判定
for select in selects:
    # ライン数
    line = check_bingo(bingo5 , select)
    # 獲得金額
    p_off += pay_off(line)
    # 結果
    result.append(line)
    pay+=200
    
calc ={k:0 for k in range(9) if k!=7}
for row in result:
    if row in calc:
        calc[row] +=1
    
# 最終結果
print('総支払額 : {0}円'.format(pay))
print('獲得金額 : {0}円'.format(p_off))
print('当たりの数 : {0}回'.format(len([r for r in result if r>0])))
for k,v in sorted(calc.items()):
    print('ライン : {0} , {1:02}回'.format(k,v))
総支払額 : 20000円
獲得金額 : 4300円
当たりの数 : 19回
ライン : 0 , 81回
ライン : 1 , 18回
ライン : 2 , 01回
ライン : 3 , 00回
ライン : 4 , 00回
ライン : 5 , 00回
ライン : 6 , 00回
ライン : 8 , 00回

当たり目等はランダムとしています。
全然当たりませんねwwwwwwwwwwww

くやしいので100枚買うを500回行ってみましょう
import itertools

calc ={k:0 for k in range(9) if k!=7}
pay,p_off,count,all_result = 0,0,0,[]
for i in range(500):
    result = []
    b = [[i2 for i2 in range(i1*5+1,i1*5+6)] for i1 in range(8) ]
    p = list(itertools.product(b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7]))
    bingo5 = bingo5hits()
    selects = random.choices(p, k=100)
    for select in selects:
        line = check_bingo(bingo5 , select)
        p_off += pay_off(line)
        result.append(line)
        pay+=200
    all_result.append(result)
    for row in result:
        if row in calc:
            calc[row] +=1
    
# 最終結果
print('総支払額 : {0:08}円'.format(pay))
print('獲得金額 : {0:08}円'.format(p_off))
for k,v in sorted(calc.items()):
    print('ライン : {0} , {1:02}回'.format(k,v))
総支払額 : 10000000円
獲得金額 : 03631800円
ライン : 0 , 41736回
ライン : 1 , 7283回
ライン : 2 , 812回
ライン : 3 , 138回
ライン : 4 , 24回
ライン : 5 , 05回
ライン : 6 , 02回
ライン : 8 , 00回

1000万円分買ってみましたが
360万円しか返ってきませんでした

最終的に投資金額の何%
回収できるのかというと?
print(p_off/pay)
0.36318

4割程度ですね

ビンゴ5では確率的に
支払った金額の4割程度に落ち着くようです!

かければかけるほど、損をするみたいなので
貧乏5って名前に変えたらどうでしょうか!!!!!

1等は40万分の1に近い確率なので
8000万円分くらい買わないと
当たらなさそうですねーー

いやー宝くじは
変えば買うほど損をする!!!!!

格言が出たところで
終わりにしたいと思います

それでは。


このページのトップヘ