0
0

More than 3 years have passed since last update.

ブラックジャックの親のバーストの確率を乱数(モンテカルロ法)を使用して、計算する

Last updated at Posted at 2021-04-23

はじめに

先に ブラックジャックの親のバーストの確率を乱数を使用せず、確率計算のみでプログラムで計算する という記事を投稿しましたが、別のアプローチ(アルゴリズム)で同様の確率を計算し、アルゴリズムやロジックに不具合がないかということを同じ結果になるかどうかで検証してみました。

プログラム

検証用ツール部分

セル関数で検証可能にします。

'*****************************************************************************
'[概要] 親のハンドの確率をシュミレーションし配列数式で返す
'[引数] LoopCnt:ループ回数
'       Decks:トランプの組数
'[戻値] 配列数式
'*****************************************************************************
Public Function SimulateHandRate(ByVal LoopCnt As Long, ByVal Decks As Double) As Variant
    Dim Result(1 To 10, 17 To 22) As Long
    Call SetHands(LoopCnt, Decks, Result())
    SimulateHandRate = Result
End Function

以下のC4セルのように =SimulateHandRate(Loop回数,組数) でセル関数を入力します。
その時、配列数式となるように、10行6列(例では、C4:H13)の範囲を選択し、Ctrl+Shift+Enter を押下します。

image.png

計算メイン部分

Option Explicit
Private Cards() As Long '山のカードの配列
Private CardPoint As Long '山の何枚目のカードか

'*****************************************************************************
'[概要] LOOP回数試行した各ハンドの出現回数を設定する
'[引数] LoopCnt:ループ回数
'       Decks:トランプの組数
'       Result:各ハンドの出現回数
'*****************************************************************************
Private Sub SetHands(ByVal LoopCnt As Long, ByVal Decks As Double, ByRef Result() As Long)
    Call Initilize(Decks)
    Call Shuffle

    Dim SaveCardPoint As Long
    Dim i As Long
    Dim Hand As Long

    For i = 1 To LoopCnt
        'カードの山を4分の3まで使用するとシャッフルする
        If CardPoint >= UBound(Cards) * 0.75 Then
            Call Shuffle
        End If
        SaveCardPoint = CardPoint

        Dim OpenCard As Long
        OpenCard = Hit()
        Hand = Deal(OpenCard)
        Result(OpenCard, Hand) = Result(OpenCard, Hand) + 1

        '10や9は次のカードをHitする確率が低く、2や3は高くなるので
        'カードの出現確率の偏りを是正させる
        '詳しくは記事の後半を参照ください。
        If SaveCardPoint + 5 > CardPoint Then
            CardPoint = SaveCardPoint + 5
        End If
    Next
End Sub

'*****************************************************************************
'[概要] カードの山を作成する
'[引数] Decks:トランプの組数
'*****************************************************************************
Private Sub Initilize(ByVal Decks As Double)
    ReDim Cards(1 To Decks * 52) As Long
    Dim i As Long
    Dim j As Long
    Dim k As Long
    For i = 1 To Decks * 4
        For j = 1 To 13
            k = k + 1
            Cards(k) = j
        Next
    Next
End Sub

'*****************************************************************************
'[概要] カードをシャッフルする
'[引数] なし
'*****************************************************************************
Private Sub Shuffle()
    CardPoint = 0
    Dim i As Long
    Dim j As Long
    Dim Swap As Long
    For i = 1 To UBound(Cards) - 1
        j = RandBetween(i, UBound(Cards))
        Swap = Cards(i)
        Cards(i) = Cards(j)
        Cards(j) = Swap
    Next
End Sub

'*****************************************************************************
'[概要] 最小値と最大値の間の乱数を発生させる
'[引数] 最小値と最大値
'[戻値] 乱数
'*****************************************************************************
Private Function RandBetween(ByVal min As Long, ByVal max As Long) As Long
    RandBetween = Int(Rnd() * (max - min + 1)) + min
End Function

'*****************************************************************************
'[概要] 17以上になるまでカードを引く
'[引数] OpenCard:オープンカード
'[戻値] 17~22のいずれか(22はバースト)
'*****************************************************************************
Private Function Deal(ByVal OpenCard As Long) As Long
    Dim Hand As Long
    Dim Card As Long
    Dim IsSoftHand As Boolean

    If OpenCard = 1 Then
        IsSoftHand = True
        Hand = 11
    Else
        IsSoftHand = False
        Hand = OpenCard
    End If

    Do While (Hand < 17)
        Card = Hit()
        Hand = Hand + Card
        If Card = 1 And IsSoftHand = False Then
            IsSoftHand = True
            Hand = Hand + 10
        End If
        If Hand > 21 And IsSoftHand = True Then
            IsSoftHand = False
            Hand = Hand - 10
        End If
    Loop

    If Hand > 21 Then
        Deal = 22
    Else
        Deal = Hand
    End If
End Function

'*****************************************************************************
'[概要] カードを1枚引く
'[引数] なし
'[戻値] 引いたカード
'*****************************************************************************
Private Function Hit() As Long
    CardPoint = CardPoint + 1
    Hit = Cards(CardPoint)
    If Hit > 10 Then
        Hit = 10
    End If
End Function

やっていること

1組52枚(関数の引数によって指定可能)のカードをシャッフルし、上から順番に17以上になるまで引くことを繰り返しながら、初手のカードごとの最終手札のバリエーションをひたすらカウントする。
ただし、カードの山が残り4分の1より少なくなったら、再度カードをシャッフルする。

計算結果(1組52枚の場合)

先の記事(理論計算値) の計算結果

初手 17 18 19 20 21 BS
1 12.61% 13.10% 12.95% 13.16% 36.53% 11.65%
2 13.90% 13.18% 13.18% 12.39% 12.05% 35.30%
3 13.03% 13.09% 12.38% 12.33% 11.60% 37.56%
4 13.10% 11.42% 12.07% 11.63% 11.51% 40.28%
5 11.97% 12.35% 11.69% 10.47% 10.63% 42.89%
6 16.69% 10.65% 10.72% 10.07% 9.79% 42.08%
7 37.23% 13.86% 7.73% 7.89% 7.30% 25.99%
8 13.09% 36.30% 12.94% 6.83% 6.98% 23.86%
9 12.19% 10.39% 35.74% 12.23% 6.11% 23.34%
10 11.44% 11.29% 11.47% 32.89% 11.49% 21.43%
平均 14.58% 13.81% 13.48% 17.58% 12.19% 28.36%

今回 1億3000万回繰返した結果

実行結果

初手 17 18 19 20 21 BS 合計
1 126380 131260 129048 131212 365569 116853 1000322
2 139138 131818 131444 124460 120162 353348 1000370
3 130192 131530 123751 122759 115564 376013 999809
4 130898 114257 120800 116576 115243 403272 1001046
5 119684 122523 117001 104278 106330 429089 998905
6 166675 106440 107292 100681 98478 420882 1000448
7 372843 138616 77087 79167 72838 259714 1000265
8 130869 364078 129595 68339 69911 238035 1000827
9 121949 103724 357026 122436 61308 233736 1000179
10 456808 450507 458406 1315549 459177 857382 3997829
合計 1895436 1794753 1751450 2285457 1584580 3688324 13000000

実行結果から計算した確率

初手 17 18 19 20 21 BS
1 12.63% 13.12% 12.90% 13.12% 36.55% 11.68%
2 13.91% 13.18% 13.14% 12.44% 12.01% 35.32%
3 13.02% 13.16% 12.38% 12.28% 11.56% 37.61%
4 13.08% 11.41% 12.07% 11.65% 11.51% 40.29%
5 11.98% 12.27% 11.71% 10.44% 10.64% 42.96%
6 16.66% 10.64% 10.72% 10.06% 9.84% 42.07%
7 37.27% 13.86% 7.71% 7.91% 7.28% 25.96%
8 13.08% 36.38% 12.95% 6.83% 6.99% 23.78%
9 12.19% 10.37% 35.70% 12.24% 6.13% 23.37%
10 11.43% 11.27% 11.47% 32.91% 11.49% 21.45%
平均 14.58% 13.81% 13.47% 17.58% 12.19% 28.37%

先の記事(理論計算値) からの乖離幅

初手 17 18 19 20 21 BS
1 0.02% 0.02% -0.05% -0.04% 0.02% 0.03%
2 0.01% 0.00% -0.04% 0.05% -0.04% 0.02%
3 -0.01% 0.06% 0.00% -0.06% -0.05% 0.05%
4 -0.02% 0.00% 0.00% 0.02% 0.00% 0.00%
5 0.01% -0.08% 0.02% -0.03% 0.01% 0.07%
6 -0.03% -0.01% 0.01% -0.01% 0.06% -0.01%
7 0.04% 0.00% -0.03% 0.02% -0.02% -0.02%
8 -0.01% 0.08% 0.00% 0.00% 0.01% -0.08%
9 0.00% -0.02% -0.04% 0.02% 0.02% 0.03%
10 -0.02% -0.02% 0.00% 0.02% 0.00% 0.02%
平均 0.00% 0.00% -0.01% 0.00% 0.00% 0.01%

結論

先に理論値で計算した結果とほとんど一致したので、アルゴリズムやロジックに不具合は、ほぼないと言えると思います。

カードの引き方による理論値との乖離について

以下の部分を削除して実行した結果

        '10や9は次のカードをHitする確率が低く、2や3は高くなるので
        'カードの出現確率の偏りを是正させる
        '詳しくは記事の後半を参照ください。
        'If SaveCardPoint + 5 > CardPoint Then
        '   CardPoint = SaveCardPoint + 5
        'End If

image.png
実行結果 の合計列のオレンジで示したセルの初手の出現数が少なめにカウントされている傾向が見て取れると思います。
また、理論計算値からの乖離幅 の表の黄色で示したセルの確率が、理論計算値よりも低くなる傾向が見て取れると思います。
このことから、10や9は次のカードをHitする確率が低くなることによりカードの出現確率が低めに偏り、このような傾向を生み出したと考えられると思います。
しかし、どちらかと言えばこちらの確率の方がより実戦時の確率に近いのではないかとも思われます。

関連記事

こちらの記事も併せてお読みください。
ブラックジャックの親のバーストの確率を乱数を使用せず、確率計算のみでプログラムで計算する
ブラックジャックの親のバーストの確率をノーコードで計算する
ブラックジャックの期待値をノーコードで計算する

類似記事

ブラックジャックの勝率を上げたくて、ディーラーのバースト率を1300万回かけて計算してみた

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0