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

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


# プログラム
## 検証用ツール部分

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

```vb
'*****************************************************************************
'[概要] 親のハンドの確率をシュミレーションし配列数式で返す
'[引数] 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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411106/a7ca9590-4cb7-d55e-4b5a-774273e72ad6.png)

## 計算メイン部分

```vb
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枚の場合）
## [先の記事(理論計算値)](https://qiita.com/takahasinaoki/items/f92b4cc3d41ac22a7ae3) の計算結果


| **初手** | **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% |

### [先の記事(理論計算値)](https://qiita.com/takahasinaoki/items/f92b4cc3d41ac22a7ae3) からの乖離幅
| 初手 | 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%  |

## 結論
[先に理論値で計算した結果](https://qiita.com/takahasinaoki/items/f92b4cc3d41ac22a7ae3)とほとんど一致したので、アルゴリズムやロジックに不具合は、ほぼないと言えると思います。

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

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411106/0a39dd31-73c9-3d8a-f163-5db1ab1e9e0f.png)
**実行結果** の合計列のオレンジで示したセルの初手の出現数が少なめにカウントされている傾向が見て取れると思います。
また、**理論計算値からの乖離幅** の表の黄色で示したセルの確率が、理論計算値よりも低くなる傾向が見て取れると思います。
このことから、10や9は次のカードをHitする確率が低くなることによりカードの出現確率が低めに偏り、このような傾向を生み出したと考えられると思います。
しかし、どちらかと言えばこちらの確率の方がより実戦時の確率に近いのではないかとも思われます。



# 関連記事
こちらの記事も併せてお読みください。
[ブラックジャックの親のバーストの確率を乱数を使用せず、確率計算のみでプログラムで計算する](https://qiita.com/takahasinaoki/items/f92b4cc3d41ac22a7ae3)
[ブラックジャックの親のバーストの確率をノーコードで計算する](https://qiita.com/takahasinaoki/items/a345bd145f9fa281b352)
[ブラックジャックの期待値をノーコードで計算する](https://qiita.com/takahasinaoki/items/094da1dd64c6273502dd)


# 類似記事
[ブラックジャックの勝率を上げたくて、ディーラーのバースト率を1300万回かけて計算してみた](https://qiita.com/hirossyi73/items/c427358d9dfeab256d64)